Skip to content
Snippets Groups Projects
rugby-list-store.c 8.21 KiB
Newer Older
Bruce Cowan's avatar
Bruce Cowan committed
/*
 * SPDX-FileCopyrightText: 2018-2024 Bruce Cowan <bruce@bcowan.me.uk>
 * SPDX-License-Identifier: GPL-3.0-or-later
Bruce Cowan's avatar
Bruce Cowan committed
 */
Bruce Cowan's avatar
Bruce Cowan committed
#include "rugby-list-store.h"
Bruce Cowan's avatar
Bruce Cowan committed

Bruce Cowan's avatar
Bruce Cowan committed
#include "rugby-possibility.h"

#include <gio/gio.h>
#include <gtk/gtk.h>
Bruce Cowan's avatar
Bruce Cowan committed

struct _RugbyListStore
{
    GObject parent_instance;

Bruce Cowan's avatar
Bruce Cowan committed
    int score;

    GListStore *items;
    GSettings *settings;
Bruce Cowan's avatar
Bruce Cowan committed
};

static void rugby_list_store_list_model_iface_init (GListModelInterface *iface);
static void rugby_list_store_section_model_iface_init (GtkSectionModelInterface *iface);
Bruce Cowan's avatar
Bruce Cowan committed

Bruce Cowan's avatar
Bruce Cowan committed
G_DEFINE_FINAL_TYPE_WITH_CODE (RugbyListStore, rugby_list_store, G_TYPE_OBJECT,
                               G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
                                                      rugby_list_store_list_model_iface_init)
                               G_IMPLEMENT_INTERFACE (GTK_TYPE_SECTION_MODEL,
                                                      rugby_list_store_section_model_iface_init))

Bruce Cowan's avatar
Bruce Cowan committed

enum
{
    PROP_SCORE = 1,
Bruce Cowan's avatar
Bruce Cowan committed
    N_PROPS
};

static GParamSpec *properties[N_PROPS];
Bruce Cowan's avatar
Bruce Cowan committed

// Helper functions

Bruce Cowan's avatar
Bruce Cowan committed
static int
Bruce Cowan's avatar
Bruce Cowan committed
sort_func (              gconstpointer  a,
                         gconstpointer  b,
           G_GNUC_UNUSED void          *user_data)
Bruce Cowan's avatar
Bruce Cowan committed
{
    int atries = rugby_possibility_get_tries ((void *) a);
    int autries = rugby_possibility_get_utries ((void *) a);
    int btries = rugby_possibility_get_tries ((void *) b);
    int butries = rugby_possibility_get_utries ((void *) b);
Bruce Cowan's avatar
Bruce Cowan committed

Bruce Cowan's avatar
Bruce Cowan committed
    int trydiff = (btries + butries) - (atries + autries);
Bruce Cowan's avatar
Bruce Cowan committed

    // Sort by total tries first, then converted
    if (trydiff != 0)
        return trydiff;
    else
        return btries - atries;
Bruce Cowan's avatar
Bruce Cowan committed
}

static void
process_data (RugbyListStore *self)
Bruce Cowan's avatar
Bruce Cowan committed
{
    int try_points = g_settings_get_int (self->settings, "try-points");
    int utry_points = g_settings_get_int (self->settings, "utry-points");
    int kick_points = g_settings_get_int (self->settings, "kick-points");

    unsigned old_length = g_list_model_get_n_items (G_LIST_MODEL (self->items));
    g_list_store_remove_all (self->items);

Bruce Cowan's avatar
Bruce Cowan committed
    if (self->score == 0)
    {
        g_list_model_items_changed (G_LIST_MODEL (self), 0, old_length, 0);
        return;
    }

    int max_tries = self->score / try_points;
    int max_utries = self->score / utry_points;
Bruce Cowan's avatar
Bruce Cowan committed

Bruce Cowan's avatar
Bruce Cowan committed
    for (int tries = 0; tries <= max_tries; tries++)
Bruce Cowan's avatar
Bruce Cowan committed
    {
Bruce Cowan's avatar
Bruce Cowan committed
        for (int utries = 0; utries <= max_utries; utries++)
Bruce Cowan's avatar
Bruce Cowan committed
        {
            int left = self->score - (tries * try_points) - (utries * utry_points);
Bruce Cowan's avatar
Bruce Cowan committed

            if (left < 0)
                break;

            if (left % kick_points == 0)
Bruce Cowan's avatar
Bruce Cowan committed
            {
                int kicks = left / kick_points;
Bruce Cowan's avatar
Bruce Cowan committed

                RugbyPossibility *possibility = rugby_possibility_new (tries,
                                                                       utries,
                                                                       kicks);
                g_list_store_append (self->items, possibility);
                g_object_unref (possibility);
    g_list_store_sort (self->items, sort_func, NULL);
    g_list_model_items_changed (G_LIST_MODEL (self), 0, old_length,
                                g_list_model_get_n_items (G_LIST_MODEL (self->items)));
// GListModel implementation

static GType
rugby_list_store_get_item_type (G_GNUC_UNUSED GListModel *list)
{
    return RUGBY_TYPE_POSSIBILITY;
}

Bruce Cowan's avatar
Bruce Cowan committed
static unsigned
rugby_list_store_get_n_items (GListModel *list)
{
    RugbyListStore *self = RUGBY_LIST_STORE (list);

    return g_list_model_get_n_items (G_LIST_MODEL (self->items));
Bruce Cowan's avatar
Bruce Cowan committed
static void *
rugby_list_store_get_item (GListModel *list,
Bruce Cowan's avatar
Bruce Cowan committed
                           unsigned    position)
{
    RugbyListStore *self = RUGBY_LIST_STORE (list);

    return g_list_model_get_item (G_LIST_MODEL (self->items), position);
rugby_list_store_list_model_iface_init (GListModelInterface *iface)
    iface->get_item_type = rugby_list_store_get_item_type;
    iface->get_n_items = rugby_list_store_get_n_items;
    iface->get_item = rugby_list_store_get_item;
// SectionModel implementation

static void
rugby_list_store_get_section (GtkSectionModel *model,
                              unsigned         position,
                              unsigned        *out_start,
                              unsigned        *out_end)
{
    RugbyListStore *self = RUGBY_LIST_STORE (model);

    unsigned n_items = g_list_model_get_n_items (G_LIST_MODEL (self->items));
    RugbyPossibility *possibility = g_list_model_get_item (G_LIST_MODEL (self->items),
                                                           position);
    if (!possibility)
    {
        *out_start = n_items;
        *out_end = G_MAXUINT;
        return;
    }

    int target_tries = rugby_possibility_total_tries (possibility);

    // Find start
    for (unsigned i = 0; i < n_items; i++)
    {
        possibility = g_list_model_get_item (G_LIST_MODEL (self->items), i);
        int total = rugby_possibility_total_tries (possibility);

        if (total == target_tries)
        {
            *out_start = i;
            break;
        }
    }

    // Find end
    for (unsigned i = *out_start + 1; i < n_items; i++)
    {
        possibility = g_list_model_get_item (G_LIST_MODEL (self->items), i);
        int total = rugby_possibility_total_tries (possibility);

        if (total != target_tries)
        {
            *out_end = i;
            return;
        }
    }

    *out_end = n_items;
}

static void
rugby_list_store_section_model_iface_init (GtkSectionModelInterface *iface)
{
    iface->get_section = rugby_list_store_get_section;
}

// Class functions

static void
rugby_list_store_dispose (GObject *object)
{
    RugbyListStore *self = RUGBY_LIST_STORE (object);

    g_clear_object (&self->settings);
    g_clear_object (&self->items);

    G_OBJECT_CLASS (rugby_list_store_parent_class)->dispose (object);
}

Bruce Cowan's avatar
Bruce Cowan committed
static void
rugby_list_store_get_property (GObject    *object,
Bruce Cowan's avatar
Bruce Cowan committed
                               unsigned    prop_id,
Bruce Cowan's avatar
Bruce Cowan committed
                               GValue     *value,
                               GParamSpec *pspec)
{
    RugbyListStore *self = RUGBY_LIST_STORE (object);

    switch (prop_id)
    {
        case PROP_SCORE:
Bruce Cowan's avatar
Bruce Cowan committed
            g_value_set_int (value, self->score);
Bruce Cowan's avatar
Bruce Cowan committed
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
rugby_list_store_set_property (GObject      *object,
Bruce Cowan's avatar
Bruce Cowan committed
                               unsigned      prop_id,
Bruce Cowan's avatar
Bruce Cowan committed
                               const GValue *value,
                               GParamSpec   *pspec)
{
    RugbyListStore *self = RUGBY_LIST_STORE (object);

    switch (prop_id)
    {
Bruce Cowan's avatar
Bruce Cowan committed
        int score;
Bruce Cowan's avatar
Bruce Cowan committed
        case PROP_SCORE:
Bruce Cowan's avatar
Bruce Cowan committed
            score = g_value_get_int (value);
            if (score != self->score)
            {
                self->score = score;
                process_data (self);
                g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SCORE]);
            }
Bruce Cowan's avatar
Bruce Cowan committed
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
rugby_list_store_class_init (RugbyListStoreClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->dispose = rugby_list_store_dispose;
Bruce Cowan's avatar
Bruce Cowan committed
    object_class->get_property = rugby_list_store_get_property;
    object_class->set_property = rugby_list_store_set_property;

    properties[PROP_SCORE] = g_param_spec_int ("score", "", "",
Bruce Cowan's avatar
Bruce Cowan committed
                                               0, 200, 0,
Bruce Cowan's avatar
Bruce Cowan committed
                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
Bruce Cowan's avatar
Bruce Cowan committed

    g_object_class_install_properties (object_class,
                                       N_PROPS,
                                       properties);
}

static void
rugby_list_store_init (RugbyListStore *self)
{
    self->settings = g_settings_new ("uk.me.bcowan.Rugby");

    g_signal_connect_swapped (self->settings, "changed::try-points",
                              G_CALLBACK (process_data), self);
    g_signal_connect_swapped (self->settings, "changed::utry-points",
                              G_CALLBACK (process_data), self);
    g_signal_connect_swapped (self->settings, "changed::kick-points",
                              G_CALLBACK (process_data), self);
Bruce Cowan's avatar
Bruce Cowan committed
    self->score = 0;
    self->items = g_list_store_new (RUGBY_TYPE_POSSIBILITY);