Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  xfdesktop-file-icon-model.c

  Sprache: C
 

/*
 *  xfdesktop - xfce4's desktop manager
 *
 *  Copyright(c) 2022 Brian Tarricone, <brian@tarricone.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */


#include "libxfce4windowing/libxfce4windowing.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gio/gio.h>
#include <libxfce4util/libxfce4util.h>

#include "xfdesktop-common.h"
#include "xfdesktop-extensions.h"
#include "xfdesktop-file-icon-model.h"
#include "xfdesktop-file-icon.h"
#include "xfdesktop-icon.h"
#include "xfdesktop-icon-view-model.h"
#include "xfdesktop-marshal.h"
#include "xfdesktop-regular-file-icon.h"
#include "xfdesktop-special-file-icon.h"
#include "xfdesktop-thumbnailer.h"
#include "xfdesktop-volume-icon.h"

#define PENDING_NEW_FILES_TIMEOUT  (60)

struct _XfdesktopFileIconModel
{
    XfdesktopIconViewModel parent;

    GdkScreen *gdkscreen;
    XfconfChannel *channel;
    GFile *folder;

    GFileMonitor *monitor;
    GFileEnumerator *enumerator;
    GCancellable *cancel_enumeration;

    GVolumeMonitor *volume_monitor;
    GHashTable *volume_icons;  // { GVolume | GMount } -> XfdesktopVolumeIcon

    GFileMonitor *metadata_monitor;
    guint metadata_timer;

    XfdesktopThumbnailer *thumbnailer;

    // This hash table is slightly more complicated than I'd like.  While the values
    // are simply XfdesktopFileIcon instances, the keys are obtained via
    // xfdesktop_file_icon_peek_sort_key().  But if we need to look up an icon and we
    // don't have the icon to get the sort key from, we use:
    //   * Regular file icons: xfdesktop_file_icon_sort_key_for_file()
    //   * Special file icons: xfdesktop_file_icon_sort_key_for_file()
    //   * Volume icons: xfdesktop_volume_icon_sort_key_for_volume()
    GHashTable *icons;

    gboolean show_thumbnails;
    gboolean sort_folders_before_files;
};

struct _XfdesktopFileIconModelClass
{
    XfdesktopIconViewModelClass parent_class;
};

typedef enum {
    PROP0,
    PROP_GDK_SCREEN,
    PROP_CHANNEL,
    PROP_FOLDER,
    PROP_SHOW_THUMBNAILS,
    PROP_SORT_FOLDERS_BEFORE_FILES,
} ModelPropertyId;

enum {
    SIG_READY,
    SIG_ERROR,
    SIG_ICON_POSITION_REQUEST,
    N_SIGS,
};

static void xfdesktop_file_icon_model_constructed(GObject *object);
static void xfdesktop_file_icon_model_set_property(GObject *object,
                                                   guint property_id,
                                                   const GValue *value,
                                                   GParamSpec *pspec);
static void xfdesktop_file_icon_model_get_property(GObject *object,
                                                   guint property_id,
                                                   GValue *value,
                                                   GParamSpec *pspec);
static void xfdesktop_file_icon_model_finalize(GObject *object);

static void xfdesktop_file_icon_model_tree_model_init(GtkTreeModelIface *iface);

static void xfdesktop_file_icon_model_get_value(GtkTreeModel *model,
                                                GtkTreeIter *iter,
                                                gint column,
                                                GValue *value);

static void xfdesktop_file_icon_model_item_free(XfdesktopIconViewModel *ivmodel,
                                                gpointer item);

static void volume_added(GVolumeMonitor *monitor,
                         GVolume *volume,
                         XfdesktopFileIconModel *fmodel);
static void volume_removed(GVolumeMonitor *monitor,
                           GVolume *volume,
                           XfdesktopFileIconModel *fmodel);
static void volume_changed(GVolumeMonitor *monitor,
                           GVolume *volume,
                           XfdesktopFileIconModel *fmodel);
static void mount_added(GVolumeMonitor *monitor,
                        GMount *mount,
                        XfdesktopFileIconModel *fmodel);
static void mount_removed(GVolumeMonitor *monitor,
                          GMount *mount,
                          XfdesktopFileIconModel *fmodel);
static void mount_changed(GVolumeMonitor *monitor,
                          GMount *mount,
                          XfdesktopFileIconModel *fmodel);
static void mount_pre_unmount(GVolumeMonitor *monitor,
                              GMount *mount,
                              XfdesktopFileIconModel *fmodel);

static void thumbnail_ready(GtkWidget *widget,
                            gchar *srcfile,
                            gchar *thumbnail_filename,
                            XfdesktopFileIconModel *fmodel);

static void xfdesktop_file_icon_model_set_show_thumbnails(XfdesktopFileIconModel *fmodel,
                                                          gboolean show);


G_DEFINE_TYPE_WITH_CODE(XfdesktopFileIconModel,
                        xfdesktop_file_icon_model,
                        XFDESKTOP_TYPE_ICON_VIEW_MODEL,
                        G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, xfdesktop_file_icon_model_tree_model_init))

G_DEFINE_QUARK("xfdesktop-file-icon-model-error-quark", xfdesktop_file_icon_model_error)


static guint signals[N_SIGS] = { 0, };

static const struct {
    const gchar *setting;
    GType setting_type;
    const gchar *property;
} setting_bindings[] = {
    { DESKTOP_ICONS_SHOW_THUMBNAILS, G_TYPE_BOOLEAN, "show-thumbnails" },
    { DESKTOP_ICONS_SORT_FOLDERS_BEFORE_FILES_PROP, G_TYPE_BOOLEAN, "sort-folders-before-files" },
};

static void
xfdesktop_file_icon_model_class_init(XfdesktopFileIconModelClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
    gobject_class->constructed = xfdesktop_file_icon_model_constructed;
    gobject_class->set_property = xfdesktop_file_icon_model_set_property;
    gobject_class->get_property = xfdesktop_file_icon_model_get_property;
    gobject_class->finalize = xfdesktop_file_icon_model_finalize;

    g_object_class_install_property(gobject_class,
                                    PROP_GDK_SCREEN,
                                    g_param_spec_object("gdk-screen",
                                                        "gdk-screen",
                                                        "GdkScreen",
                                                        GDK_TYPE_SCREEN,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
    g_object_class_install_property(gobject_class,
                                    PROP_CHANNEL,
                                    g_param_spec_object("channel",
                                                        "channel",
                                                        "xfconf channel",
                                                        XFCONF_TYPE_CHANNEL,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
    g_object_class_install_property(gobject_class,
                                    PROP_FOLDER,
                                    g_param_spec_object("folder",
                                                        "Desktop Folder",
                                                        "Folder this icon manager manages",
                                                        G_TYPE_FILE,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
    g_object_class_install_property(gobject_class,
                                    PROP_SHOW_THUMBNAILS,
                                    g_param_spec_boolean("show-thumbnails",
                                                         "show-thumbnails",
                                                         "show-thumbnails",
                                                         TRUE,
                                                         G_PARAM_READWRITE));
    g_object_class_install_property(gobject_class,
                                    PROP_SORT_FOLDERS_BEFORE_FILES,
                                    g_param_spec_boolean("sort-folders-before-files",
                                                         "sort-folders-before-files",
                                                         "sort-folders-before-files",
                                                         TRUE,
                                                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    XfdesktopIconViewModelClass *ivmodel_class = XFDESKTOP_ICON_VIEW_MODEL_CLASS(klass);
    ivmodel_class->model_item_ref = g_object_ref;
    ivmodel_class->model_item_free = xfdesktop_file_icon_model_item_free;
    ivmodel_class->model_item_hash = xfdesktop_file_icon_hash;
    ivmodel_class->model_item_equal = xfdesktop_file_icon_equal;

    signals[SIG_READY] = g_signal_new("ready",
                                      XFDESKTOP_TYPE_FILE_ICON_MODEL,
                                      G_SIGNAL_RUN_LAST,
                                      0,
                                      NULL,
                                      NULL,
                                      g_cclosure_marshal_VOID__VOID,
                                      G_TYPE_NONE,
                                      0);

    signals[SIG_ERROR] = g_signal_new("error",
                                      XFDESKTOP_TYPE_FILE_ICON_MODEL,
                                      G_SIGNAL_RUN_LAST,
                                      0,
                                      NULL,
                                      NULL,
                                      g_cclosure_marshal_VOID__BOXED,
                                      G_TYPE_NONE,
                                      1,
                                      G_TYPE_ERROR);

    signals[SIG_ICON_POSITION_REQUEST] = g_signal_new("icon-position-request",
                                                      XFDESKTOP_TYPE_FILE_ICON_MODEL,
                                                      G_SIGNAL_RUN_LAST,
                                                      0,
                                                      NULL,
                                                      NULL,
                                                      xfdesktop_marshal_OBJECT__OBJECT_POINTER_POINTER,
                                                      XFW_TYPE_MONITOR,
                                                      3,
                                                      XFDESKTOP_TYPE_FILE_ICON,
                                                      G_TYPE_POINTER,
                                                      G_TYPE_POINTER);
}

static void
xfdesktop_file_icon_model_tree_model_init(GtkTreeModelIface *iface)
{
    iface->get_value = xfdesktop_file_icon_model_get_value;
}

static void
xfdesktop_file_icon_model_init(XfdesktopFileIconModel *fmodel) {
    fmodel->show_thumbnails = TRUE;
    fmodel->sort_folders_before_files = TRUE;
    fmodel->icons = g_hash_table_new_full(g_str_hash,
                                          g_str_equal,
                                          g_free,
                                          g_object_unref);
    fmodel->volume_icons = g_hash_table_new_full(g_direct_hash, g_direct_equal, g_object_unref, NULL);

}

static void
xfdesktop_file_icon_model_constructed(GObject *object) {
    G_OBJECT_CLASS(xfdesktop_file_icon_model_parent_class)->constructed(object);

    XfdesktopFileIconModel *fmodel = XFDESKTOP_FILE_ICON_MODEL(object);

    fmodel->thumbnailer = xfdesktop_thumbnailer_new();
    g_signal_connect(G_OBJECT(fmodel->thumbnailer), "thumbnail-ready",
                     G_CALLBACK(thumbnail_ready), fmodel);

    fmodel->volume_monitor = g_volume_monitor_get();
    g_signal_connect(fmodel->volume_monitor, "volume-added",
                     G_CALLBACK(volume_added), fmodel);
    g_signal_connect(fmodel->volume_monitor, "volume-removed",
                     G_CALLBACK(volume_removed), fmodel);
    g_signal_connect(fmodel->volume_monitor, "volume-changed",
                     G_CALLBACK(volume_changed), fmodel);
    g_signal_connect(fmodel->volume_monitor, "mount-added",
                     G_CALLBACK(mount_added), fmodel);
    g_signal_connect(fmodel->volume_monitor, "mount-removed",
                     G_CALLBACK(mount_removed), fmodel);
    g_signal_connect(fmodel->volume_monitor, "mount-changed",
                     G_CALLBACK(mount_changed), fmodel);
    g_signal_connect(fmodel->volume_monitor, "mount-pre-unmount",
                     G_CALLBACK(mount_pre_unmount), fmodel);

    for (gsize i = 0; i < G_N_ELEMENTS(setting_bindings); ++i) {
        xfconf_g_property_bind(fmodel->channel,
                               setting_bindings[i].setting,
                               setting_bindings[i].setting_type,
                               fmodel,
                               setting_bindings[i].property);
    }
}

static void
xfdesktop_file_icon_model_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
    XfdesktopFileIconModel *fmodel = XFDESKTOP_FILE_ICON_MODEL(object);

    switch (property_id) {
        case PROP_GDK_SCREEN:
            fmodel->gdkscreen = g_value_get_object(value);
            break;

        case PROP_CHANNEL:
            fmodel->channel = g_value_dup_object(value);
            break;

        case PROP_FOLDER:
            fmodel->folder = g_value_dup_object(value);
            break;

        case PROP_SHOW_THUMBNAILS:
            xfdesktop_file_icon_model_set_show_thumbnails(fmodel, g_value_get_boolean(value));
            break;

        case PROP_SORT_FOLDERS_BEFORE_FILES:
            fmodel->sort_folders_before_files = g_value_get_boolean(value);
            break;

        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
            break;
    }
}

static void
xfdesktop_file_icon_model_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
    XfdesktopFileIconModel *fmodel = XFDESKTOP_FILE_ICON_MODEL(object);

    switch (property_id) {
        case PROP_GDK_SCREEN:
            g_value_set_object(value, fmodel->gdkscreen);
            break;

        case PROP_CHANNEL:
            g_value_set_object(value, fmodel->channel);
            break;

        case PROP_FOLDER:
            g_value_set_object(value, fmodel->folder);
            break;

        case PROP_SHOW_THUMBNAILS:
            g_value_set_boolean(value, fmodel->show_thumbnails);
            break;

        case PROP_SORT_FOLDERS_BEFORE_FILES:
            g_value_set_boolean(value, fmodel->sort_folders_before_files);
            break;

        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
            break;
    }
}

static void
xfdesktop_file_icon_model_finalize(GObject *object) {
    XfdesktopFileIconModel *fmodel = XFDESKTOP_FILE_ICON_MODEL(object);

    /* remove any pending metadata changes */
    if (fmodel->metadata_timer != 0) {
        g_source_remove(fmodel->metadata_timer);
    }

    if (fmodel->cancel_enumeration != NULL) {
        g_cancellable_cancel(fmodel->cancel_enumeration);
        g_object_unref(fmodel->cancel_enumeration);
    }
    if (fmodel->enumerator != NULL) {
        g_object_unref(fmodel->enumerator);
    }

    /* disconnect from the file monitor and release it */
    if (fmodel->monitor) {
        g_signal_handlers_disconnect_by_data(fmodel->monitor, fmodel);
        g_object_unref(fmodel->monitor);
    }

    /* Same for the file metadata monitor */
    if (fmodel->metadata_monitor) {
        g_signal_handlers_disconnect_by_data(fmodel->metadata_monitor, fmodel);
        g_object_unref(fmodel->metadata_monitor);
    }

    g_object_unref(fmodel->volume_monitor);

    g_object_unref(fmodel->thumbnailer);

    g_hash_table_destroy(fmodel->volume_icons);
    g_hash_table_destroy(fmodel->icons);
    g_object_unref(fmodel->folder);
    g_object_unref(fmodel->channel);

    G_OBJECT_CLASS(xfdesktop_file_icon_model_parent_class)->finalize(object);
}

static void
xfdesktop_file_icon_model_get_value(GtkTreeModel *model,
                                    GtkTreeIter *iter,
                                    gint column,
                                    GValue *value)
{
    gpointer model_item;
    XfdesktopFileIcon *icon;

    g_return_if_fail(iter != NULL);

    model_item = xfdesktop_icon_view_model_get_model_item(XFDESKTOP_ICON_VIEW_MODEL(model), iter);
    g_return_if_fail(model_item != NULL && XFDESKTOP_IS_FILE_ICON(model_item));
    icon = XFDESKTOP_FILE_ICON(model_item);

    switch (column) {
        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_IMAGE: {
            GIcon *gicon = xfdesktop_file_icon_get_gicon(icon);
            if (icon != NULL) {
                g_value_init(value, G_TYPE_ICON);
                g_value_set_object(value, gicon);
            }
            break;
        }

        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_IMAGE_OPACITY:
            g_value_init(value, G_TYPE_DOUBLE);
            g_value_set_double(value, xfdesktop_file_icon_get_opacity(icon));
            break;

        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_LABEL: {
            const gchar *label = xfdesktop_icon_peek_label(XFDESKTOP_ICON(icon));
            if (label != NULL) {
                g_value_init(value, G_TYPE_STRING);
                g_value_set_string(value, label);
            }
            break;
        }

        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_ROW: {
            gint16 row = -1, col = -1;

            g_value_init(value, G_TYPE_INT);
            xfdesktop_icon_get_position(XFDESKTOP_ICON(icon), &row, &col);
            g_value_set_int(value, row);
            break;
        }

        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_COL: {
            gint16 row = -1, col = -1;

            g_value_init(value, G_TYPE_INT);
            xfdesktop_icon_get_position(XFDESKTOP_ICON(icon), &row, &col);
            g_value_set_int(value,col);
            break;
        }

        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_SORT_PRIORITY: {
            gint priority;

            if (XFDESKTOP_IS_SPECIAL_FILE_ICON(icon)) {
                priority = 0;
            } else if (XFDESKTOP_IS_VOLUME_ICON(icon)) {
                priority = 1;
            } else if (XFDESKTOP_IS_REGULAR_FILE_ICON(icon)) {
                if (XFDESKTOP_FILE_ICON_MODEL(model)->sort_folders_before_files) {
                    GFileInfo *file_info = xfdesktop_file_icon_peek_file_info(icon);
                    if (g_file_info_get_file_type(file_info) == G_FILE_TYPE_DIRECTORY) {
                        priority = 2;
                    } else {
                        priority = 3;
                    }
                } else {
                    priority = 2;
                }
            } else {
                priority = 4;
            }

            g_value_init(value, G_TYPE_INT);
            g_value_set_int(value, priority);
            break;
        }

        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_TOOLTIP_IMAGE: {
            GIcon *gicon = xfdesktop_file_icon_get_gicon(icon);
            if (icon != NULL) {
                g_value_init(value, G_TYPE_ICON);
                g_value_set_object(value, gicon);
            }
            break;
        }

        case XFDESKTOP_ICON_VIEW_MODEL_COLUMN_TOOLTIP_TEXT: {
            const gchar *tip_text = xfdesktop_icon_peek_tooltip(XFDESKTOP_ICON(icon));
            if (tip_text != NULL) {
                g_value_init(value, G_TYPE_STRING);
                g_value_set_string(value, tip_text);
            }
            break;
        }

        default:
            g_warning("Invalid XfdesktopWindowIconManager column %d", column);
            break;
    }
}

static void
xfdesktop_file_icon_model_item_free(XfdesktopIconViewModel *ivmodel,
                                    gpointer item)
{
    XfdesktopFileIcon *icon = XFDESKTOP_FILE_ICON(item);

    g_signal_handlers_disconnect_by_data(icon, ivmodel);
    g_object_unref(icon);
}

static gboolean
check_create_desktop_folder(XfdesktopFileIconModel *fmodel) {
    gboolean result = TRUE;

    GFileInfo *info = g_file_query_info(fmodel->folder,
                                        XFDESKTOP_FILE_INFO_NAMESPACE,
                                        G_FILE_QUERY_INFO_NONE,
                                        NULL,
                                        NULL);

    if (info == NULL) {
        GError *error = NULL;
        if (!g_file_make_directory_with_parents(fmodel->folder, NULL, &error)) {
            GError *error2 = g_error_new_literal(XFDESKTOP_FILE_ICON_MODEL_ERROR,
                                                 XFDESKTOP_FILE_ICON_MODEL_ERROR_CANT_CREATE_DESKTOP_FOLDER,
                                                 error->message);
            g_error_free(error2);
            g_signal_emit(fmodel, signals[SIG_ERROR], 0, error2);
            result = FALSE;
            g_error_free(error);
        }
    } else if (g_file_info_get_file_type(info) != G_FILE_TYPE_DIRECTORY) {
        GError *error = g_error_new_literal(XFDESKTOP_FILE_ICON_MODEL_ERROR,
                                            XFDESKTOP_FILE_ICON_MODEL_ERROR_DESKTOP_NOT_FOLDER,
                                            _("A normal file with the same name already exists. Please delete or rename it."));
        g_signal_emit(fmodel, signals[SIG_ERROR], 0, error);
        result = FALSE;
        g_error_free(error);
    }

    if (info != NULL) {
        g_object_unref(info);
    }

    return result;
}

static void
add_icon(XfdesktopFileIconModel *fmodel, XfdesktopFileIcon *icon) {
    XfwMonitor *monitor = NULL;
    gint16 row = -1, col = -1;
    g_signal_emit(fmodel, signals[SIG_ICON_POSITION_REQUEST], 0, icon, &row, &col, &monitor);
    xfdesktop_icon_set_monitor(XFDESKTOP_ICON(icon), monitor);
    if (row != -1 && col != -1) {
        xfdesktop_icon_set_position(XFDESKTOP_ICON(icon), row, col);
    }

    g_signal_connect_swapped(icon, "label-changed",
                             G_CALLBACK(xfdesktop_icon_view_model_changed), fmodel);
    g_signal_connect_swapped(icon, "pixbuf-changed",
                             G_CALLBACK(xfdesktop_icon_view_model_changed), fmodel);

    XF_DEBUG("adding icon %s to icon view", xfdesktop_icon_peek_label(XFDESKTOP_ICON(icon)));
    g_hash_table_replace(fmodel->icons, g_strdup(xfdesktop_file_icon_peek_sort_key(icon)), icon);
    xfdesktop_icon_view_model_append(XFDESKTOP_ICON_VIEW_MODEL(fmodel), icon, icon, NULL);
}

static void
remove_icon(XfdesktopFileIconModel *fmodel, XfdesktopFileIcon *icon) {
    GFile *file = xfdesktop_file_icon_peek_file(icon);
    if (G_LIKELY(file != NULL)) {
        gchar *filename = g_file_get_path(file);

        if (G_LIKELY(filename != NULL)) {
            xfdesktop_thumbnailer_dequeue_thumbnail(fmodel->thumbnailer, filename);
            g_free(filename);
        }
    }

    if (XFDESKTOP_IS_VOLUME_ICON(icon)) {
        XfdesktopVolumeIcon *volume_icon = XFDESKTOP_VOLUME_ICON(icon);
        g_hash_table_remove(fmodel->volume_icons, xfdesktop_volume_icon_peek_volume(volume_icon));
        g_hash_table_remove(fmodel->volume_icons, xfdesktop_volume_icon_peek_mount(volume_icon));
    }

    XF_DEBUG("removing icon %s from icon view", xfdesktop_icon_peek_label(XFDESKTOP_ICON(icon)));
    xfdesktop_icon_view_model_remove(XFDESKTOP_ICON_VIEW_MODEL(fmodel), icon);
    g_hash_table_remove(fmodel->icons, xfdesktop_file_icon_peek_sort_key(icon));
}

static void
add_special_file_icon(XfdesktopFileIconModel *fmodel, XfdesktopSpecialFileIconType type) {
    XfdesktopSpecialFileIcon *icon = xfdesktop_special_file_icon_new(type, fmodel->gdkscreen);
    if (icon != NULL) {
        add_icon(fmodel, XFDESKTOP_FILE_ICON(icon));
    }
}

static void
add_volume_icon(XfdesktopFileIconModel *fmodel, GVolume *volume) {
#if defined(DEBUG_TRACE) && DEBUG_TRACE > 0
    {
        gchar *name = g_volume_get_name(volume);
        TRACE("entering: '%s'", name);
        g_free(name);
    }
#endif

    XfdesktopVolumeIcon *icon = xfdesktop_volume_icon_new_for_volume(volume, fmodel->gdkscreen);
    g_hash_table_insert(fmodel->volume_icons, g_object_ref(volume), icon);
    add_icon(fmodel, XFDESKTOP_FILE_ICON(icon));
}

static void
add_mount_icon(XfdesktopFileIconModel *fmodel, GMount *mount) {
#if defined(DEBUG_TRACE) && DEBUG_TRACE > 0
    {
        gchar *name = g_mount_get_name(mount);
        TRACE("entering: '%s'", name);
        g_free(name);
    }
#endif

    XfdesktopVolumeIcon *icon = xfdesktop_volume_icon_new_for_mount(mount, fmodel->gdkscreen);
    g_hash_table_insert(fmodel->volume_icons, g_object_ref(mount), icon);
    add_icon(fmodel, XFDESKTOP_FILE_ICON(icon));
}

static void
volume_added(GVolumeMonitor *monitor, GVolume *volume, XfdesktopFileIconModel *fmodel) {
    TRACE("entering");
    if (!g_hash_table_contains(fmodel->volume_icons, volume)) {
        add_volume_icon(fmodel, volume);
    }
}

static void
volume_removed(GVolumeMonitor *monitor, GVolume *volume, XfdesktopFileIconModel *fmodel) {
    XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->volume_icons, volume);
    if (icon != NULL) {
        g_hash_table_remove(fmodel->volume_icons, volume);
        remove_icon(fmodel, icon);
    }
}

static void
volume_changed(GVolumeMonitor *monitor, GVolume *volume, XfdesktopFileIconModel *fmodel) {
    XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->volume_icons, volume);
    if (icon != NULL) {
        xfdesktop_icon_view_model_changed(XFDESKTOP_ICON_VIEW_MODEL(fmodel), icon);
    }
}

static void
mount_added(GVolumeMonitor *monitor, GMount *mount, XfdesktopFileIconModel *fmodel) {
    TRACE("entering");
    XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->volume_icons, mount);
    if (icon == NULL && !g_mount_is_shadowed(mount)) {
        GVolume *volume = g_mount_get_volume(mount);
        if (volume != NULL) {
            DBG("got existing volume for mount");
            icon = g_hash_table_lookup(fmodel->volume_icons, volume);
            if (icon != NULL) {
                DBG("got existing icon for volume for mount");
                xfdesktop_volume_icon_mounted(XFDESKTOP_VOLUME_ICON(icon), mount);
                xfdesktop_icon_view_model_changed(XFDESKTOP_ICON_VIEW_MODEL(fmodel), icon);
                g_hash_table_insert(fmodel->volume_icons, g_object_ref(mount), icon);
            }
            g_object_unref(volume);
        } else {
            add_mount_icon(fmodel, mount);
        }
    }
}

static void
mount_removed(GVolumeMonitor *monitor, GMount *mount, XfdesktopFileIconModel *fmodel) {
    XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->volume_icons, mount);
    if (icon != NULL) {
        g_hash_table_remove(fmodel->volume_icons, mount);

        XfdesktopVolumeIcon *vicon = XFDESKTOP_VOLUME_ICON(icon);
        xfdesktop_volume_icon_unmounted(vicon);

        if (xfdesktop_volume_icon_peek_volume(vicon) == NULL && xfdesktop_volume_icon_peek_mount(vicon) == NULL) {
            remove_icon(fmodel, icon);
        }
    }
}

static void
mount_changed(GVolumeMonitor *monitor, GMount *mount, XfdesktopFileIconModel *fmodel) {
    XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->volume_icons, mount);
    if (icon != NULL) {
        xfdesktop_icon_view_model_changed(XFDESKTOP_ICON_VIEW_MODEL(fmodel), icon);
    }
}

static void
mount_pre_unmount(GVolumeMonitor *monitor, GMount *mount, XfdesktopFileIconModel *fmodel) {
    XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->volume_icons, mount);
    if (icon == NULL) {
        GVolume *volume = g_mount_get_volume(mount);
        if (volume != NULL) {
            icon = g_hash_table_lookup(fmodel->volume_icons, volume);
            g_object_unref(volume);
        }
    }

    if (icon != NULL) {
        // XXX: do we need to bother with this?
        xfdesktop_icon_view_model_changed(XFDESKTOP_ICON_VIEW_MODEL(fmodel), icon);
    }
}

static void
load_removable_media(XfdesktopFileIconModel *fmodel) {
    DBG("entering");

    GList *volumes = g_volume_monitor_get_volumes(fmodel->volume_monitor);
    for (GList *l = volumes; l != NULL; l = l->next) {
        GVolume *volume = G_VOLUME(l->data);
        volume_added(fmodel->volume_monitor, volume, fmodel);
        g_object_unref(volume);
    }
    g_list_free(volumes);

    GList *mounts = g_volume_monitor_get_mounts(fmodel->volume_monitor);
    for (GList *l = mounts; l != NULL; l = l->next) {
        GMount *mount = G_MOUNT(l->data);
        mount_added(fmodel->volume_monitor, mount, fmodel);
        g_object_unref(mount);
    }
    g_list_free(mounts);
}

static void
queue_thumbnail(XfdesktopFileIconModel *fmodel, XfdesktopRegularFileIcon *icon) {
    if (fmodel->show_thumbnails) {
        GFile *file = xfdesktop_file_icon_peek_file(XFDESKTOP_FILE_ICON(icon));
        if (file != NULL) {
            gchar *path = g_file_get_path(file);
            if (path != NULL) {
                xfdesktop_thumbnailer_queue_thumbnail(fmodel->thumbnailer, path);
                g_free(path);
            }
        }
    }
}

static void
add_regular_icon(XfdesktopFileIconModel *fmodel,
                 GFile *file,
                 GFileInfo *info,
                 gint16 row,
                 gint16 col,
                 gboolean defer_if_missing)
{
    XfdesktopRegularFileIcon *icon = xfdesktop_regular_file_icon_new(fmodel->channel, fmodel->gdkscreen, file, info);
    if (row >= 0 && col >= 0) {
        xfdesktop_icon_set_position(XFDESKTOP_ICON(icon), row, col);
    }
    add_icon(fmodel, XFDESKTOP_FILE_ICON(icon));

    queue_thumbnail(fmodel, icon);
}

static void
file_monitor_changed(GFileMonitor *monitor,
                     GFile *file,
                     GFile *other_file,
                     GFileMonitorEvent event,
                     XfdesktopFileIconModel *fmodel)
{
    switch (event) {
        case G_FILE_MONITOR_EVENT_RENAMED:
        case G_FILE_MONITOR_EVENT_MOVED_OUT: {
            gint16 row = -1, col = -1;

            XF_DEBUG("got a moved event");

            gchar *ht_key = xfdesktop_file_icon_sort_key_for_file(file);
            XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->icons, ht_key);
            g_free(ht_key);

            if (icon != NULL) {
                /* Get the old position so we can use it for the new icon */
                if(!xfdesktop_icon_get_position(XFDESKTOP_ICON(icon), &row, &col)) {
                    /* Failed to get position... not supported? */
                    row = col = 0;
                }
                XF_DEBUG("row %d, col %d", row, col);

                /* Remove the old icon */
                remove_icon(fmodel, icon);
            }

            /* In case of MOVED_OUT, other_file will be NULL */
            if (other_file != NULL) {
                /* Check to see if there's already an other_file represented on
                 * the desktop and remove it so there aren't duplicated icons
                 * present. */

                ht_key = xfdesktop_file_icon_sort_key_for_file(other_file);
                XfdesktopFileIcon *moved_icon = g_hash_table_lookup(fmodel->icons, ht_key);
                g_free(ht_key);
                if (moved_icon != NULL) {
                    /* Since we're replacing an existing icon, get that location
                     * to use instead */

                    if (!xfdesktop_icon_get_position(XFDESKTOP_ICON(moved_icon), &row, &col)) {
                        /* Failed to get position... not supported? */
                        row = col = 0;
                    }
                    XF_DEBUG("row %d, col %d", row, col);

                    remove_icon(fmodel, moved_icon);
                }

                if (xfdesktop_compare_paths(g_file_get_parent(other_file), fmodel->folder)) {
                    XF_DEBUG("icon moved off the desktop");
                    /* Nothing moved, this is actually a delete */
                } else {
                    GFileInfo *file_info = g_file_query_info(other_file,
                                                             XFDESKTOP_FILE_INFO_NAMESPACE,
                                                             G_FILE_QUERY_INFO_NONE,
                                                             NULL,
                                                             NULL);

                    if (file_info != NULL) {
                        add_regular_icon(fmodel, other_file, file_info, row, col, FALSE);
                        g_object_unref(file_info);
                    }
                }
            }

            break;
        }

        case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
        case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: {
            XF_DEBUG("got changed event");

            gchar *ht_key = xfdesktop_file_icon_sort_key_for_file(file);
            XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->icons, ht_key);
            g_free(ht_key);
            if (icon != NULL) {
                GFileInfo *file_info = g_file_query_info(file,
                                                         XFDESKTOP_FILE_INFO_NAMESPACE,
                                                         G_FILE_QUERY_INFO_NONE,
                                                         NULL,
                                                         NULL);

                if (file_info != NULL) {
                    /* update the icon if the file still exists */
                    xfdesktop_file_icon_update_file_info(icon, file_info);
                    queue_thumbnail(fmodel, XFDESKTOP_REGULAR_FILE_ICON(icon));

                    g_object_unref(file_info);
                } else {
                    /* Remove the icon as it doesn't seem to exist */
                    remove_icon(fmodel, icon);
                }
            }
            break;
        }

        case G_FILE_MONITOR_EVENT_MOVED_IN:
        case G_FILE_MONITOR_EVENT_CREATED:
            XF_DEBUG("got created event");

            if (!g_file_equal(fmodel->folder, file)) {
                /* first make sure we don't already have an icon for this path.
                 * this seems to be necessary to avoid inconsistencies */

                gchar *ht_key = xfdesktop_file_icon_sort_key_for_file(file);
                XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->icons, ht_key);
                g_free(ht_key);
                if (icon != NULL) {
                    remove_icon(fmodel, icon);
                }

                GFileInfo *file_info = g_file_query_info(file,
                                                         XFDESKTOP_FILE_INFO_NAMESPACE,
                                                         G_FILE_QUERY_INFO_NONE,
                                                         NULL,
                                                         NULL);
                if (file_info != NULL) {
                    add_regular_icon(fmodel, file, file_info, -1, -1TRUE);
                    g_object_unref(file_info);
                }
            }
            break;

        case G_FILE_MONITOR_EVENT_DELETED: {
            XF_DEBUG("got deleted event");

            gchar *ht_key = xfdesktop_file_icon_sort_key_for_file(file);
            XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->icons, ht_key);
            g_free(ht_key);
            if (icon != NULL) {
                gchar *filename = g_file_get_path(file);
                xfdesktop_thumbnailer_dequeue_thumbnail(fmodel->thumbnailer, filename);
                /* Always try to remove thumbnail so it doesn't take up
                 * space on the user's disk. */

                xfdesktop_thumbnailer_delete_thumbnail(fmodel->thumbnailer, filename);
                remove_icon(fmodel, icon);
                g_free(filename);
            } else if (g_file_equal(file, fmodel->folder)) {
                XF_DEBUG("~/Desktop disappeared!");
                /* yes, reload before and after is correct */
                xfdesktop_file_icon_model_reload(fmodel);
                check_create_desktop_folder(fmodel);
                xfdesktop_file_icon_model_reload(fmodel);
            }

            break;
        }

        default:
            break;
    }
}

static void
update_file_info(gpointer key, gpointer value, gpointer user_data) {
    XfdesktopFileIcon *icon = XFDESKTOP_FILE_ICON(value);

    if (icon != NULL && (XFDESKTOP_IS_REGULAR_FILE_ICON(icon) || XFDESKTOP_IS_SPECIAL_FILE_ICON(icon))) {
        GFile *file = xfdesktop_file_icon_peek_file(icon);
        GFileInfo *file_info = g_file_query_info(file, XFDESKTOP_FILE_INFO_NAMESPACE,
                                      G_FILE_QUERY_INFO_NONE, NULL, NULL);

        if (file_info != NULL) {
            /* update the icon if the file still exists */
            xfdesktop_file_icon_update_file_info(icon, file_info);
            g_object_unref(file_info);
        }
    }
}

static gboolean
metadata_update_timer(gpointer user_data)
{
    XfdesktopFileIconModel *fmodel = XFDESKTOP_FILE_ICON_MODEL(user_data);
    fmodel->metadata_timer = 0;
    g_hash_table_foreach(fmodel->icons, update_file_info, fmodel);

    return FALSE;
}

static void
metadata_monitor_changed(GFileMonitor *monitor,
                         GFile *file,
                         GFile *other_file,
                         GFileMonitorEvent event,
                         XfdesktopFileIconModel *fmodel)
{
    if (event == G_FILE_MONITOR_EVENT_CHANGED) {
        XF_DEBUG("metadata file changed event");

        /* remove any pending metadata changes */
        if (fmodel->metadata_timer != 0) {
            g_source_remove(fmodel->metadata_timer);
            fmodel->metadata_timer = 0;
        }

        /* cool down timer so we don't call this due to multiple file
         * changes at the same time. */

        fmodel->metadata_timer = g_timeout_add_seconds(5, metadata_update_timer, fmodel);
    }
}

static void
enumerator_files_ready(GFileEnumerator *enumerator, GAsyncResult *result, XfdesktopFileIconModel *fmodel) {
    DBG("entering");

    /* Make sure not to reference fmodel if we have been cancelled */
    GError *error = NULL;
    GList *files = g_file_enumerator_next_files_finish(enumerator, result, &error);
    if (files == NULL && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        DBG("cancelled");
        g_error_free(error);
    } else if (files == NULL) {
        g_clear_object(&fmodel->enumerator);

        if (error != NULL) {
            GError *error2 = g_error_new_literal(XFDESKTOP_FILE_ICON_MODEL_ERROR,
                                                 XFDESKTOP_FILE_ICON_MODEL_ERROR_FOLDER_LIST_FAILED,
                                                 error->message);
            g_error_free(error);
            g_signal_emit(fmodel, signals[SIG_ERROR], 0, error2);
            g_error_free(error2);
        } else {
            if (fmodel->monitor == NULL) {
                fmodel->monitor = g_file_monitor(fmodel->folder, G_FILE_MONITOR_WATCH_MOVES, NULL, NULL);
                g_signal_connect(fmodel->monitor, "changed",
                                 G_CALLBACK(file_monitor_changed), fmodel);
            }

            /* initialize the metadata watching the gvfs files since it doesn't
             * send notification messages when monitored files change */

            if (fmodel->metadata_monitor == NULL) {
                gchar *location = xfce_resource_lookup(XFCE_RESOURCE_DATA, "gvfs-metadata/");
                if (location != NULL) {
                    GFile *metadata_location = g_file_new_for_path(location);

                    fmodel->metadata_monitor = g_file_monitor(metadata_location, G_FILE_MONITOR_NONE, NULL, NULL);
                    g_signal_connect(fmodel->metadata_monitor, "changed",
                                     G_CALLBACK(metadata_monitor_changed), fmodel);

                    g_object_unref(metadata_location);
                    g_free(location);
                }
            }

            g_signal_emit(fmodel, signals[SIG_READY], 0);
        }
    } else {
        for (GList *l = files; l; l = l->next) {
            GFileInfo *info = G_FILE_INFO(l->data);
            const gchar *name = g_file_info_get_name(info);
            GFile *file = g_file_get_child(fmodel->folder, name);

            add_regular_icon(fmodel, file, info, -1, -1TRUE);

            g_object_unref(file);
            g_object_unref(info);
        }
        g_list_free(files);

        g_file_enumerator_next_files_async(fmodel->enumerator,
                                           10,
                                           G_PRIORITY_DEFAULT,
                                           fmodel->cancel_enumeration,
                                           (GAsyncReadyCallback)enumerator_files_ready,
                                           fmodel);
    }
}

static void
file_enumerator_ready(GFile *file, GAsyncResult *result, XfdesktopFileIconModel *fmodel) {
    GError *error = NULL;
    /* Make sure we don't access fmodel until after we have checked for cancellation */
    GFileEnumerator *enumerator = g_file_enumerate_children_finish(file, result, &error);
    if (enumerator == NULL) {
        if (error != NULL) {
            if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED) == FALSE) {
                GError *error2 = g_error_new_literal(XFDESKTOP_FILE_ICON_MODEL_ERROR,
                                                     XFDESKTOP_FILE_ICON_MODEL_ERROR_FOLDER_LIST_FAILED,
                                                     error->message);
                g_signal_emit(fmodel, signals[SIG_ERROR], 0, error2);
                g_error_free(error2);
            } else {
                DBG("cancelled");
            }
            g_error_free(error);
        }
    } else {
        g_clear_object(&fmodel->enumerator);
        fmodel->enumerator = enumerator;
        g_file_enumerator_next_files_async(fmodel->enumerator,
                                           10, G_PRIORITY_DEFAULT, fmodel->cancel_enumeration,
                                           (GAsyncReadyCallback)enumerator_files_ready,
                                           fmodel);
    }
}

static void
thumbnail_ready(GtkWidget *widget, gchar *srcfile, gchar *thumbnail_filename, XfdesktopFileIconModel *fmodel) {
    g_return_if_fail(srcfile != NULL && thumbnail_filename != NULL);

    GFile *file = g_file_new_for_path(srcfile);

    gchar *ht_key = xfdesktop_file_icon_sort_key_for_file(file);
    XfdesktopIcon *icon = g_hash_table_lookup(fmodel->icons, ht_key);
    g_free(ht_key);

    if (icon != NULL) {
        GFile *thumb_file = g_file_new_for_path(thumbnail_filename);
        xfdesktop_icon_set_thumbnail_file(icon, thumb_file);
    }

    g_object_unref(file);
}

static void
xfdesktop_file_icon_model_set_show_thumbnails(XfdesktopFileIconModel *fmodel, gboolean show) {
    if (fmodel->show_thumbnails != show) {
        fmodel->show_thumbnails = show;

        GHashTableIter iter;
        g_hash_table_iter_init(&iter, fmodel->icons);

        XfdesktopFileIcon *icon;
        while (g_hash_table_iter_next(&iter, NULL, (gpointer)&icon)) {
            if (XFDESKTOP_IS_REGULAR_FILE_ICON(icon)) {
                if (show) {
                    queue_thumbnail(fmodel, XFDESKTOP_REGULAR_FILE_ICON(icon));
                } else {
                    xfdesktop_icon_delete_thumbnail(XFDESKTOP_ICON(icon));
                }
            }

        }
    }
}

XfdesktopFileIconModel *
xfdesktop_file_icon_model_new(XfconfChannel *channel, GFile *folder, GdkScreen *gdkscreen) {
    g_return_val_if_fail(XFCONF_IS_CHANNEL(channel), NULL);
    g_return_val_if_fail(G_IS_FILE(folder), NULL);
    return g_object_new(XFDESKTOP_TYPE_FILE_ICON_MODEL,
                        "channel", channel,
                        "folder", folder,
                        "gdk-screen", gdkscreen,
                        NULL);
}

XfdesktopFileIcon *
xfdesktop_file_icon_model_get_icon(XfdesktopFileIconModel *fmodel,
                                   GtkTreeIter *iter)
{
    gpointer model_item;

    g_return_val_if_fail(XFDESKTOP_IS_FILE_ICON_MODEL(fmodel), NULL);

    model_item = xfdesktop_icon_view_model_get_model_item(XFDESKTOP_ICON_VIEW_MODEL(fmodel), iter);
    if (model_item != NULL) {
        return XFDESKTOP_FILE_ICON(model_item);
    } else {
        return NULL;
    }
}

XfdesktopFileIcon *
xfdesktop_file_icon_model_get_icon_for_file(XfdesktopFileIconModel *fmodel, GFile *file) {
    g_return_val_if_fail(XFDESKTOP_IS_FILE_ICON_MODEL(fmodel), NULL);
    g_return_val_if_fail(G_IS_FILE(file), NULL);

    gchar *ht_sort_key = xfdesktop_file_icon_sort_key_for_file(file);
    XfdesktopFileIcon *icon = g_hash_table_lookup(fmodel->icons, ht_sort_key);
    g_free(ht_sort_key);

    return icon;
}

gboolean
xfdesktop_file_icon_model_get_icon_iter(XfdesktopFileIconModel *fmodel,
                                        XfdesktopFileIcon *icon,
                                        GtkTreeIter *iter)
{
    g_return_val_if_fail(XFDESKTOP_IS_FILE_ICON_MODEL(fmodel), FALSE);
    g_return_val_if_fail(XFDESKTOP_IS_FILE_ICON(icon), FALSE);
    g_return_val_if_fail(iter != NULL, FALSE);

    return xfdesktop_icon_view_model_get_iter_for_key(XFDESKTOP_ICON_VIEW_MODEL(fmodel), icon, iter);
}

void
xfdesktop_file_icon_model_reload(XfdesktopFileIconModel *fmodel) {
    TRACE("entering");

    g_return_if_fail(XFDESKTOP_IS_FILE_ICON_MODEL(fmodel));

    if (fmodel->cancel_enumeration != NULL) {
        g_cancellable_cancel(fmodel->cancel_enumeration);
        g_object_unref(fmodel->cancel_enumeration);
    }
    fmodel->cancel_enumeration = g_cancellable_new();

    GList *icons = g_hash_table_get_values(fmodel->icons);
    for (GList *l = icons; l != NULL; l = l->next) {
        XfdesktopFileIcon *icon = XFDESKTOP_FILE_ICON(l->data);
        remove_icon(fmodel, icon);
    }
    g_list_free(icons);
    g_assert(g_hash_table_size(fmodel->icons) == 0);
    g_assert(g_hash_table_size(fmodel->volume_icons) == 0);

    xfdesktop_icon_view_model_clear(XFDESKTOP_ICON_VIEW_MODEL(fmodel));

    if (check_create_desktop_folder(fmodel)) {
        g_clear_object(&fmodel->enumerator);

        for (gint i = 0; i <= XFDESKTOP_SPECIAL_FILE_ICON_TRASH; ++i) {
            DBG("adding special file type %d", i);
            add_special_file_icon(fmodel, i);
        }

        load_removable_media(fmodel);

        g_file_enumerate_children_async(fmodel->folder,
                                        XFDESKTOP_FILE_INFO_NAMESPACE,
                                        G_FILE_QUERY_INFO_NONE,
                                        G_PRIORITY_DEFAULT,
                                        fmodel->cancel_enumeration,
                                        (GAsyncReadyCallback)file_enumerator_ready,
                                        fmodel);
    }
}

Messung V0.5 in Prozent
C=97 H=94 G=95

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-07-10) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik