static GdkDragAction
xfdesktop_special_file_icon_get_allowed_drop_actions(XfdesktopIcon *icon,
                                                     GdkDragAction *suggested_action)
{
    XfdesktopSpecialFileIcon *special_file_icon = XFDESKTOP_SPECIAL_FILE_ICON(icon);
    GFileInfo *info;
    GdkDragAction actions = 0;

    if(special_file_icon->priv->type != XFDESKTOP_SPECIAL_FILE_ICON_TRASH) {
        info = xfdesktop_file_icon_peek_file_info(XFDESKTOP_FILE_ICON(icon));
        if(info) {
            if(g_file_info_get_attribute_boolean(info,
                                                 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
            {
                XF_DEBUG("can move, copy, link and ask");
                actions = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK;
                if(suggested_action)
                    *suggested_action = GDK_ACTION_MOVE;
            }
        }
    } else {
        XF_DEBUG("can move");
        actions = GDK_ACTION_MOVE; /* everything else is just silly */
        if(suggested_action)
            *suggested_action = GDK_ACTION_MOVE;
    }

    if(suggested_action)
        *suggested_action = 0;

    return actions;
}
static void
cb_show_thumbnails_notify(GObject *gobject,
                          GParamSpec *pspec,
                          gpointer user_data)
{
    XfdesktopRegularFileIcon *regular_file_icon;
    gboolean show_thumbnails = FALSE;

    TRACE("entering");

    if(!user_data || !XFDESKTOP_IS_REGULAR_FILE_ICON(user_data))
        return;

    regular_file_icon = XFDESKTOP_REGULAR_FILE_ICON(user_data);

    g_object_get(regular_file_icon->priv->fmanager, "show-thumbnails", &show_thumbnails, NULL);

    if(regular_file_icon->priv->show_thumbnails != show_thumbnails) {
        XF_DEBUG("show-thumbnails changed! now: %s", show_thumbnails ? "TRUE" : "FALSE");
        regular_file_icon->priv->show_thumbnails = show_thumbnails;
        xfdesktop_file_icon_invalidate_icon(XFDESKTOP_FILE_ICON(regular_file_icon));
        xfdesktop_icon_invalidate_pixbuf(XFDESKTOP_ICON(regular_file_icon));
        xfdesktop_icon_pixbuf_changed(XFDESKTOP_ICON(regular_file_icon));
    }
}
static void
xfdesktop_volume_icon_changed(GVolume *volume,
                              XfdesktopVolumeIcon *volume_icon)
{
    g_return_if_fail(G_IS_VOLUME(volume));
    g_return_if_fail(XFDESKTOP_IS_VOLUME_ICON(volume_icon));

    XF_DEBUG("VOLUME CHANGED");

    /**
     * NOTE: We use a timeout here to check if the volume is 
     * now mounted (or has been unmounted). This timeout seems
     * to be needed because when the "changed" signal is emitted,
     * the GMount is always NULL. In a 500ms timeout we check
     * at most 5 times for a valid mount until we give up. This
     * hopefully is a suitable workaround for most machines and
     * drives. 
     */

    /* abort an existing timeout, we may have to run it a few times
     * once again for the new event */
    if(volume_icon->priv->changed_timeout_id > 0) {
        g_source_remove(volume_icon->priv->changed_timeout_id);
        volume_icon->priv->changed_timeout_id = 0;
    }

    /* reset timeout information and start a timeout */
    volume_icon->priv->changed_timeout_count = 0;
    volume_icon->priv->changed_timeout_id =
        g_timeout_add_full(G_PRIORITY_LOW, 500, 
                           (GSourceFunc) volume_icon_changed_timeout, 
                           g_object_ref(volume_icon),
                           g_object_unref);
}
static gboolean
xfdesktop_special_file_icon_do_drop_dest(XfdesktopIcon *icon,
                                         XfdesktopIcon *src_icon,
                                         GdkDragAction action)
{
    XfdesktopSpecialFileIcon *special_file_icon = XFDESKTOP_SPECIAL_FILE_ICON(icon);
    XfdesktopFileIcon *src_file_icon = XFDESKTOP_FILE_ICON(src_icon);
    GFileInfo *src_info;
    GFile *src_file;
    GFile *dest_file = NULL;
    gboolean result = FALSE;

    TRACE("entering");

    g_return_val_if_fail(special_file_icon && src_file_icon, FALSE);
    g_return_val_if_fail(xfdesktop_special_file_icon_get_allowed_drop_actions(icon, NULL),
                         FALSE);

    src_file = xfdesktop_file_icon_peek_file(src_file_icon);

    src_info = xfdesktop_file_icon_peek_file_info(src_file_icon);
    if(!src_info)
        return FALSE;

    if(special_file_icon->priv->type == XFDESKTOP_SPECIAL_FILE_ICON_TRASH) {
        GList files;

        XF_DEBUG("doing trash");

        /* fake a file list */
        files.data = src_file;
        files.prev = files.next = NULL;

        /* let the trash service handle the trash operation */
        xfdesktop_file_utils_trash_files(&files, special_file_icon->priv->gscreen, NULL);
    } else {
        gchar *name = g_file_get_basename(src_file);
        if(!name)
            return FALSE;

        switch(action) {
            case GDK_ACTION_MOVE:
                XF_DEBUG("doing move");
                dest_file = g_object_ref(special_file_icon->priv->file);
                break;
            case GDK_ACTION_COPY:
                XF_DEBUG("doing copy");
                dest_file = g_file_get_child(special_file_icon->priv->file, name);
                break;
            case GDK_ACTION_LINK:
                XF_DEBUG("doing link");
                dest_file = g_object_ref(special_file_icon->priv->file);
                break;
            default:
                g_warning("Unsupported drag action: %d", action);
        }

        /* let the file manager service move/copy/link the file */
        if(dest_file) {
            xfdesktop_file_utils_transfer_file(action, src_file, dest_file,
                                               special_file_icon->priv->gscreen);

            result = TRUE;
        }

        g_object_unref(dest_file);
        g_free(name);
    }

    return result;
}
static gboolean
volume_icon_changed_timeout(XfdesktopVolumeIcon *volume_icon)
{
    GMount *mount;
    gboolean mounted_before = FALSE;
    gboolean mounted_after = FALSE;

    g_return_val_if_fail(XFDESKTOP_IS_VOLUME_ICON(volume_icon), FALSE);

    XF_DEBUG("TIMEOUT");

    /* reset the icon's mount point information */
    if(volume_icon->priv->file) {
        g_object_unref(volume_icon->priv->file);
        volume_icon->priv->file = NULL;

        /* apparently the volume was mounted before, otherwise
         * we wouldn't have had a mount point for it */
        mounted_before = TRUE;
    }
    if(volume_icon->priv->file_info) {
        g_object_unref(volume_icon->priv->file_info);
        volume_icon->priv->file_info = NULL;
    }
    if(volume_icon->priv->filesystem_info) {
        g_object_unref(volume_icon->priv->filesystem_info);
        volume_icon->priv->filesystem_info = NULL;
    }

    /* check if we have a valid mount now */
    mount = g_volume_get_mount(volume_icon->priv->volume);
    if(mount) {
        /* load mount point information */
        volume_icon->priv->file = g_mount_get_root(mount);
        volume_icon->priv->file_info = 
            g_file_query_info(volume_icon->priv->file, 
                              XFDESKTOP_FILE_INFO_NAMESPACE,
                              G_FILE_QUERY_INFO_NONE,
                              NULL, NULL);
        volume_icon->priv->filesystem_info = 
            g_file_query_filesystem_info(volume_icon->priv->file,
                                         XFDESKTOP_FILESYSTEM_INFO_NAMESPACE,
                                         NULL, NULL);

        /* release the mount itself */
        g_object_unref(mount);

        /* the device is mounted now (we have a mount point for it) */
        mounted_after = TRUE;
    }

    XF_DEBUG("MOUNTED BEFORE: %d, MOUNTED AFTER: %d", mounted_before, mounted_after);

    if(mounted_before != mounted_after) {
        /* invalidate the tooltip */
        if(volume_icon->priv->tooltip) {
            g_free(volume_icon->priv->tooltip);
            volume_icon->priv->tooltip = NULL;
        }

        /* not really easy to check if this changed or not, so just invalidate it */
        xfdesktop_icon_invalidate_pixbuf(XFDESKTOP_ICON(volume_icon));
        xfdesktop_icon_pixbuf_changed(XFDESKTOP_ICON(volume_icon));

        /* finalize the timeout source */
        volume_icon->priv->changed_timeout_id = 0;
        return FALSE;
    } else {
        /* increment the timeout counter */
        volume_icon->priv->changed_timeout_count += 1;

        if(volume_icon->priv->changed_timeout_count >= 5) {
            /* finalize the timeout source */
            volume_icon->priv->changed_timeout_id = 0;
            return FALSE;
        } else {
            XF_DEBUG("TRY AGAIN");
            return TRUE;
        }
    }
}
static gboolean
xfdesktop_volume_icon_do_drop_dest(XfdesktopIcon *icon,
                                 XfdesktopIcon *src_icon,
                                 GdkDragAction action)
{
    XfdesktopVolumeIcon *volume_icon = XFDESKTOP_VOLUME_ICON(icon);
    XfdesktopFileIcon *src_file_icon = XFDESKTOP_FILE_ICON(src_icon);
    GFileInfo *src_info;
    GFile *src_file, *parent, *dest_file = NULL;
    gboolean result = FALSE;
    gchar *name;
    
    TRACE("entering");
    
    g_return_val_if_fail(volume_icon && src_file_icon, FALSE);
    g_return_val_if_fail(xfdesktop_volume_icon_get_allowed_drop_actions(icon, NULL),
                         FALSE);
    
    src_file = xfdesktop_file_icon_peek_file(src_file_icon);

    src_info = xfdesktop_file_icon_peek_file_info(src_file_icon);
    if(!src_info)
        return FALSE;

    if(!volume_icon->priv->file_info)
        return FALSE;
   
    parent = g_file_get_parent(src_file);
    if(!parent)
        return FALSE;
    g_object_unref(parent);
        
    name = g_file_get_basename(src_file);
    if(!name)
        return FALSE;
    
    switch(action) {
        case GDK_ACTION_MOVE:
            XF_DEBUG("doing move");
            dest_file = g_object_ref(volume_icon->priv->file);
            break;
        
        case GDK_ACTION_COPY:
            XF_DEBUG("doing copy");
            dest_file = g_file_get_child(volume_icon->priv->file, name);
            break;
        
        case GDK_ACTION_LINK:
            XF_DEBUG("doing link");
            dest_file = g_object_ref(volume_icon->priv->file);
            break;
        
        default:
            g_warning("Unsupported drag action: %d", action);
    }

    if(dest_file) {
        xfdesktop_file_utils_transfer_file(action, src_file, dest_file,
                                           volume_icon->priv->gscreen);
    
        g_object_unref(dest_file);

        result = TRUE;
    }

    g_free(name);
        
    return result;
}