Skip to content

Commit

Permalink
Implement filtering.
Browse files Browse the repository at this point in the history
  • Loading branch information
wojciech-graj committed Aug 19, 2022
1 parent 19dfab3 commit 8fb4260
Show file tree
Hide file tree
Showing 9 changed files with 1,002 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.sat_cache
obj/
orbvis
*.glade~
563 changes: 544 additions & 19 deletions res/ui/ui.glade

Large diffs are not rendered by default.

37 changes: 21 additions & 16 deletions src/ui/catalog.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "catalog.h"

#include "filter.h"
#include "info.h"
#include "satcat_code.h"

Expand All @@ -21,6 +22,7 @@ enum Column {
enum Page {
PAGE_CATALOG = 0u,
PAGE_INFO,
PAGE_FILTER,
};

static const char *column_ids[] = {
Expand All @@ -37,13 +39,14 @@ static const char *column_ids[] = {
[COL_PERIGEE] = "col_perigee",
};

GtkTreeModelFilter *e_catalog_filter = NULL;

static GtkTreeViewColumn *columns[NUM_COLS];
static GtkWindow *window_catalog;
static GtkTreeSortable *sort = NULL;
static gchar search_text[64] = { 0 };
static enum Column sort_col = COL_CATNUM;
static GtkSortType sort_type = GTK_SORT_ASCENDING;
static GtkTreeModelFilter *filter = NULL;
static GtkTreeView *satellite_view;
static GtkListStore *satellite_store;
static GtkTreeSelection *selection;
Expand Down Expand Up @@ -93,9 +96,9 @@ void on_column_select_cell_toggled(GtkCellRendererToggle *cell_renderer, char *p
(void)cell_renderer;
(void)user_data;
GtkTreeIter iter;
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(filter), &iter, path);
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(e_catalog_filter), &iter, path);
struct Satellite *satellite;
gtk_tree_model_get(GTK_TREE_MODEL(filter), &iter,
gtk_tree_model_get(GTK_TREE_MODEL(e_catalog_filter), &iter,
0, &satellite,
-1);
satellite_select_ptr(satellite);
Expand All @@ -109,7 +112,7 @@ void on_catalog_search_search_changed(GtkSearchEntry *entry, gpointer user_data)
for (i = 0; i < 63 && text[i]; i++)
search_text[i] = g_ascii_toupper(text[i]);
search_text[i] = '\0';
gtk_tree_model_filter_refilter(filter);
gtk_tree_model_filter_refilter(e_catalog_filter);
}

void on_col_clicked(GtkTreeViewColumn *treeviewcolumn, gpointer user_data)
Expand Down Expand Up @@ -159,37 +162,39 @@ void catalog_construct_views(void)
sort = GTK_TREE_SORTABLE(gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(satellite_store)));
gtk_tree_sortable_set_sort_func(sort, 1, catalog_view_search_compare_func, NULL, NULL);

filter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(sort), NULL));
gtk_tree_model_filter_set_visible_func(filter, catalog_view_filter_visible_func, NULL, NULL);
e_catalog_filter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(sort), NULL));
gtk_tree_model_filter_set_visible_func(e_catalog_filter, catalog_view_filter_visible_func, NULL, NULL);
g_object_unref(sort);

gtk_tree_view_set_model(satellite_view, GTK_TREE_MODEL(filter));
g_object_unref(filter);
gtk_tree_view_set_model(satellite_view, GTK_TREE_MODEL(e_catalog_filter));
g_object_unref(e_catalog_filter);
}

void catalog_deconstruct_views(void)
{
gtk_tree_view_set_model(satellite_view, NULL);

if (filter)
g_object_unref(filter);
if (e_catalog_filter)
g_object_unref(e_catalog_filter);
if (sort)
g_object_unref(sort);
}

void catalog_satellite_changed(struct Satellite *satellite)
{
GtkTreeIter iter;
GtkTreeModel *model = GTK_TREE_MODEL(filter);
gtk_tree_model_get_iter_first(model, &iter);
GtkTreeModel *model = GTK_TREE_MODEL(e_catalog_filter);
if (!gtk_tree_model_get_iter_first(model, &iter))
return;
struct Satellite *iter_satellite;
gtk_tree_model_get(model, &iter, 0, &iter_satellite, -1);
while (iter_satellite != satellite) {
gtk_tree_model_iter_next(model, &iter);
if (!gtk_tree_model_iter_next(model, &iter))
return;
gtk_tree_model_get(model, &iter, 0, &iter_satellite, -1);
}
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(filter), &iter);
gtk_tree_model_row_changed(GTK_TREE_MODEL(filter), path, &iter);
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(e_catalog_filter), &iter);
gtk_tree_model_row_changed(GTK_TREE_MODEL(e_catalog_filter), path, &iter);
gtk_tree_path_free(path);
}

Expand All @@ -200,7 +205,7 @@ gboolean catalog_view_filter_visible_func(GtkTreeModel *model, GtkTreeIter *iter
gtk_tree_model_get(model, iter,
0, &satellite,
-1);
return g_strstr_len(satellite->satcat.name, 24, search_text) != NULL;
return (g_strstr_len(satellite->satcat.name, 24, search_text) != NULL) && (filter_func(satellite));
}

gint catalog_view_search_compare_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
Expand Down
2 changes: 2 additions & 0 deletions src/ui/catalog.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ void catalog_deinit(void);
void catalog_satellite_changed(struct Satellite *satellite);
void catalog_satellites_fill(struct Satellite *satellites, size_t n_satellites);

extern GtkTreeModelFilter *e_catalog_filter;

#endif /* __CATALOG_H__ */
235 changes: 235 additions & 0 deletions src/ui/filter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#include "filter.h"

#include "catalog.h"
#include "satcat_code_list.h"

#include <float.h>

#define FILTER_FUNC_BETWEEN(field, target, var) ((field_active[field]) && (((target)[0] >= (var)) || ((target)[1] <= (var))))
#define SC_DATE_LESS(d1, d2) (((d1).year < (d2).year) ? (TRUE) : (((d1).year == (d2).year) ? (((d1).month < (d2).month) ? (TRUE) : (((d1).month == (d2).month) ? ((d1).day < (d2).day) : (FALSE))) : (FALSE)))

enum FilterCombo {
COMBO_STATUS = 0u,
COMBO_LAUNCH_SITE,
COMBO_SOURCE,
NUM_FILTER_COMBO,
};

enum FilterEntry {
ENTRY_PERIGEE = 0u,
ENTRY_APOGEE,
ENTRY_INCLINATION,
ENTRY_PERIOD,
ENTRY_LAUNCH_DATE,
NUM_FILTER_ENTRY,
};

enum FilterField {
FIELD_STATUS = 0u,
FIELD_LAUNCH_SITE,
FIELD_SOURCE,
FIELD_PERIGEE,
FIELD_APOGEE,
FIELD_INCLINATION,
FIELD_PERIOD,
FIELD_LAUNCH_DATE,
NUM_FILTER_FIELD,
};

static const char *combo_item_ids[NUM_FILTER_COMBO] = {
[COMBO_STATUS] = "filter_status",
[COMBO_LAUNCH_SITE] = "filter_launch_site",
[COMBO_SOURCE] = "filter_source",
};

static const char *entry_item_ids[NUM_FILTER_ENTRY][2] = {
[ENTRY_PERIGEE] = { "filter_low_perigee", "filter_high_perigee" },
[ENTRY_APOGEE] = { "filter_low_apogee", "filter_high_apogee" },
[ENTRY_INCLINATION] = { "filter_low_inclination", "filter_high_inclination" },
[ENTRY_PERIOD] = { "filter_low_period", "filter_high_period" },
[ENTRY_LAUNCH_DATE] = { "filter_low_launch_date", "filter_high_launch_date" },
};

static GtkComboBoxText *combo_items[NUM_FILTER_COMBO];
static GtkEntry *entry_items[NUM_FILTER_ENTRY][2];
static gboolean field_active[NUM_FILTER_FIELD] = { FALSE };

static gboolean active = FALSE;

static char target_status;
static sc_code_t target_source;
static sc_code_t target_launch_site;
static double target_perigee[2];
static double target_apogee[2];
static double target_inclination[2];
static double target_period[2];
static struct SCDate target_launch_date[2];

static void on_filter_clear_clicked(GtkButton *button, gpointer user_data);
static void on_filter_apply_clicked(GtkButton *button, gpointer user_data);
static void on_filter_clear_status_clicked(GtkButton *button, gpointer user_data);
static void on_filter_clear_source_clicked(GtkButton *button, gpointer user_data);
static void on_filter_clear_launch_site_clicked(GtkButton *button, gpointer user_data);
static void on_filter_launch_date_activate(GtkEntry *entry, gpointer user_data);

static void filter_set_from_entry(enum FilterEntry entry, enum FilterField field, double *target, double dflt[2]);

void on_filter_clear_status_clicked(GtkButton *button, gpointer user_data)
{
(void)button;
(void)user_data;
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_items[COMBO_STATUS]), -1);
}

void on_filter_clear_source_clicked(GtkButton *button, gpointer user_data)
{
(void)button;
(void)user_data;
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_items[COMBO_SOURCE]), -1);
}

void on_filter_clear_launch_site_clicked(GtkButton *button, gpointer user_data)
{
(void)button;
(void)user_data;
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_items[COMBO_LAUNCH_SITE]), -1);
}

void on_filter_launch_date_activate(GtkEntry *entry, gpointer user_data)
{
(void)user_data;

const gchar *text = gtk_entry_get_text(entry);
unsigned y, m = 1, d = 1;
int nitems = sscanf(text, "%4u-%2u-%2u", &y, &m, &d);
if (!nitems)
goto FAIL;
{
GTimeZone *tz = g_time_zone_new_utc();
GDateTime *dt = g_date_time_new(tz, y, m, d, 0, 0, 0);
if (dt) {
g_date_time_unref(dt);
g_time_zone_unref(tz);
return;
}
g_time_zone_unref(tz);
}
FAIL:
gtk_entry_set_text(entry, "");
}

void on_filter_clear_clicked(GtkButton *button, gpointer user_data)
{
(void)button;
(void)user_data;

unsigned i, j;
for (i = 0; i < NUM_FILTER_COMBO; i++)
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_items[i]), -1);
for (i = 0; i < NUM_FILTER_ENTRY; i++)
for (j = 0; j < 2; j++)
gtk_entry_set_text(entry_items[i][j], "");
for (i = 0; i < NUM_FILTER_FIELD; i++)
field_active[i] = FALSE;

active = FALSE;
gtk_tree_model_filter_refilter(e_catalog_filter);
}

void filter_set_from_entry(enum FilterEntry entry, enum FilterField field, double *target, double dflt[2])
{
field_active[field] = FALSE;
unsigned j;
for (j = 0; j < 2; j++) {
const gchar *text = gtk_entry_get_text(entry_items[entry][j]);
gchar *endptr;
target[j] = g_ascii_strtod(text, &endptr);
if (endptr == text)
target[j] = dflt[j];
else
field_active[field] = TRUE;
}
}

void on_filter_apply_clicked(GtkButton *button, gpointer user_data)
{
(void)button;
(void)user_data;

int idx;

idx = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_items[COMBO_STATUS]));
field_active[FIELD_STATUS] = (idx != -1);
if (field_active[FIELD_STATUS])
target_status = SCSTAT[idx];

idx = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_items[COMBO_SOURCE]));
field_active[FIELD_SOURCE] = (idx != -1);
if (field_active[FIELD_SOURCE])
target_source = SCSRC[idx];

idx = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_items[COMBO_LAUNCH_SITE]));
field_active[FIELD_LAUNCH_SITE] = (idx != -1);
if (field_active[FIELD_LAUNCH_SITE])
target_launch_site = SCSITE[idx];

filter_set_from_entry(ENTRY_PERIGEE, FIELD_PERIGEE, target_perigee, (double[2]){ 0, DBL_MAX });
filter_set_from_entry(ENTRY_APOGEE, FIELD_APOGEE, target_apogee, (double[2]){ 0, DBL_MAX });
filter_set_from_entry(ENTRY_INCLINATION, FIELD_INCLINATION, target_inclination, (double[2]){ 0, 360.0 });
filter_set_from_entry(ENTRY_PERIOD, FIELD_PERIOD, target_period, (double[2]){ 0, DBL_MAX });

field_active[FIELD_LAUNCH_DATE] = FALSE;
unsigned i;
for (i = 0; i < 2; i++) {
target_launch_date[i].year = (unsigned[2]){ 0, 9999 }[i];
target_launch_date[i].month = 1;
target_launch_date[i].day = 1;
int nitems = sscanf(gtk_entry_get_text(entry_items[ENTRY_LAUNCH_DATE][i]), "%4u-%2u-%2u", &target_launch_date[i].year, &target_launch_date[i].month, &target_launch_date[i].day);
if (nitems)
field_active[FIELD_LAUNCH_DATE] = TRUE;
}

active = TRUE;
gtk_tree_model_filter_refilter(e_catalog_filter);
}

gboolean filter_func(struct Satellite *satellite)
{
if (!active)
return TRUE;

return !((field_active[FIELD_STATUS] && satellite->satcat.opstat != target_status)
|| (field_active[FIELD_SOURCE] && SC_STR5_TO_CODE(satellite->satcat.source) != target_source)
|| (field_active[FIELD_LAUNCH_SITE] && SC_STR5_TO_CODE(satellite->satcat.launch_site) != target_launch_site)
|| FILTER_FUNC_BETWEEN(FIELD_PERIGEE, target_perigee, satellite->satcat.perigee)
|| FILTER_FUNC_BETWEEN(FIELD_APOGEE, target_apogee, satellite->satcat.apogee)
|| FILTER_FUNC_BETWEEN(FIELD_INCLINATION, target_inclination, satellite->satcat.inc_deg)
|| FILTER_FUNC_BETWEEN(FIELD_PERIOD, target_period, satellite->satcat.period)
|| (field_active[FIELD_LAUNCH_DATE] && (SC_DATE_LESS(satellite->satcat.launch_date, target_launch_date[0]) || SC_DATE_LESS(target_launch_date[1], satellite->satcat.launch_date))));
}

void filter_init(GtkBuilder *builder)
{
gtk_builder_add_callback_symbols(builder,
"on_filter_clear_clicked", G_CALLBACK(on_filter_clear_clicked),
"on_filter_apply_clicked", G_CALLBACK(on_filter_apply_clicked),
"on_filter_clear_status_clicked", G_CALLBACK(on_filter_clear_status_clicked),
"on_filter_clear_source_clicked", G_CALLBACK(on_filter_clear_source_clicked),
"on_filter_clear_launch_site_clicked", G_CALLBACK(on_filter_clear_launch_site_clicked),
"on_filter_launch_date_activate", G_CALLBACK(on_filter_launch_date_activate),
NULL);

unsigned i, j;
for (i = 0; i < NUM_FILTER_COMBO; i++)
combo_items[i] = GTK_COMBO_BOX_TEXT(gtk_builder_get_object(builder, combo_item_ids[i]));
for (i = 0; i < NUM_FILTER_ENTRY; i++)
for (j = 0; j < 2; j++)
entry_items[i][j] = GTK_ENTRY(gtk_builder_get_object(builder, entry_item_ids[i][j]));

for (i = 0; i < NUM_SCSTAT; i++)
gtk_combo_box_text_append(combo_items[COMBO_STATUS], NULL, sc_status_str(SCSTAT[i]));
for (i = 0; i < NUM_SCSRC; i++)
gtk_combo_box_text_append(combo_items[COMBO_SOURCE], NULL, sc_source_str(SCSRC[i]));
for (i = 0; i < NUM_SCSITE; i++)
gtk_combo_box_text_append(combo_items[COMBO_LAUNCH_SITE], NULL, sc_launch_site_str(SCSITE[i]));
}
10 changes: 10 additions & 0 deletions src/ui/filter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef __FILTER_H__
#define __FILTER_H__

#include "satellite.h"
#include "ui.h"

void filter_init(GtkBuilder *builder);
gboolean filter_func(struct Satellite *satellite);

#endif /* __FILTER_H__ */
2 changes: 2 additions & 0 deletions src/ui/ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "catalog.h"
#include "entity.h"
#include "error.h"
#include "filter.h"
#include "gfx.h"
#include "info.h"
#include "input.h"
Expand Down Expand Up @@ -68,6 +69,7 @@ void ui_init(int argc, char ***argv)
catalog_init(builder);
setting_init(builder);
info_init(builder);
filter_init(builder);

gtk_builder_connect_signals(builder, NULL);

Expand Down
Loading

0 comments on commit 8fb4260

Please sign in to comment.