feat: add image catalog sorting and filtering
This commit is contained in:
parent
118172ad0e
commit
2fe594cc51
10 changed files with 1113 additions and 64 deletions
|
|
@ -39,3 +39,7 @@ opération, la touche `C` demande son annulation.
|
||||||
L'écran Import présente un catalogue en mémoire vérifié par rapport au manifeste
|
L'écran Import présente un catalogue en mémoire vérifié par rapport au manifeste
|
||||||
et aux fichiers de `images/originals`. Les flèches, `j`/`k`, PageUp, PageDown,
|
et aux fichiers de `images/originals`. Les flèches, `j`/`k`, PageUp, PageDown,
|
||||||
Home et End naviguent dans la liste ; `R` recharge le catalogue.
|
Home et End naviguent dans la liste ; `R` recharge le catalogue.
|
||||||
|
La touche `S` fait défiler les tris par ordre d'import, nom ou taille, dans les
|
||||||
|
deux directions. `/` filtre les noms d'images sans distinction de casse ASCII et
|
||||||
|
`X` efface le filtre. Le tri et le filtre restent entièrement en mémoire et ne
|
||||||
|
modifient ni le manifeste ni les images.
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
typedef struct Lardon3DImageCatalog Lardon3DImageCatalog;
|
typedef struct Lardon3DImageCatalog Lardon3DImageCatalog;
|
||||||
|
typedef struct Lardon3DImageView Lardon3DImageView;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LARDON3D_SCREEN_HOME = 0,
|
LARDON3D_SCREEN_HOME = 0,
|
||||||
|
|
@ -23,8 +22,7 @@ typedef struct {
|
||||||
char project_path[PATH_MAX];
|
char project_path[PATH_MAX];
|
||||||
char status_message[256];
|
char status_message[256];
|
||||||
Lardon3DImageCatalog *image_catalog;
|
Lardon3DImageCatalog *image_catalog;
|
||||||
size_t image_selection;
|
Lardon3DImageView *image_view;
|
||||||
size_t image_offset;
|
|
||||||
} Lardon3DAppState;
|
} Lardon3DAppState;
|
||||||
|
|
||||||
void lardon3d_app_state_init(Lardon3DAppState *state);
|
void lardon3d_app_state_init(Lardon3DAppState *state);
|
||||||
|
|
|
||||||
71
include/lardon3d/image_view.h
Normal file
71
include/lardon3d/image_view.h
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#ifndef LARDON3D_IMAGE_VIEW_H
|
||||||
|
#define LARDON3D_IMAGE_VIEW_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <lardon3d/image_catalog.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LARDON3D_IMAGE_FILTER_CAPACITY = 128,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LARDON3D_IMAGE_SORT_IMPORT_ORDER = 0,
|
||||||
|
LARDON3D_IMAGE_SORT_NAME_ASC,
|
||||||
|
LARDON3D_IMAGE_SORT_NAME_DESC,
|
||||||
|
LARDON3D_IMAGE_SORT_SIZE_ASC,
|
||||||
|
LARDON3D_IMAGE_SORT_SIZE_DESC
|
||||||
|
} Lardon3DImageSort;
|
||||||
|
|
||||||
|
Lardon3DImageView *lardon3d_image_view_create(
|
||||||
|
const Lardon3DImageCatalog *catalog
|
||||||
|
);
|
||||||
|
void lardon3d_image_view_destroy(Lardon3DImageView *view);
|
||||||
|
bool lardon3d_image_view_set_catalog(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
const Lardon3DImageCatalog *catalog
|
||||||
|
);
|
||||||
|
bool lardon3d_image_view_set_sort(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
Lardon3DImageSort sort
|
||||||
|
);
|
||||||
|
bool lardon3d_image_view_set_filter(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
const char *filter,
|
||||||
|
char *error_message,
|
||||||
|
size_t error_message_size
|
||||||
|
);
|
||||||
|
bool lardon3d_image_view_rebuild(Lardon3DImageView *view);
|
||||||
|
size_t lardon3d_image_view_count(const Lardon3DImageView *view);
|
||||||
|
uint64_t lardon3d_image_view_total_size(const Lardon3DImageView *view);
|
||||||
|
const Lardon3DImageEntry *lardon3d_image_view_get(
|
||||||
|
const Lardon3DImageView *view,
|
||||||
|
size_t visible_index
|
||||||
|
);
|
||||||
|
bool lardon3d_image_view_catalog_index(
|
||||||
|
const Lardon3DImageView *view,
|
||||||
|
size_t visible_index,
|
||||||
|
size_t *catalog_index
|
||||||
|
);
|
||||||
|
Lardon3DImageSort lardon3d_image_view_sort(const Lardon3DImageView *view);
|
||||||
|
const char *lardon3d_image_view_filter(const Lardon3DImageView *view);
|
||||||
|
const char *lardon3d_image_view_sort_name(Lardon3DImageSort sort);
|
||||||
|
size_t lardon3d_image_view_selection(const Lardon3DImageView *view);
|
||||||
|
size_t lardon3d_image_view_offset(const Lardon3DImageView *view);
|
||||||
|
void lardon3d_image_view_select(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
size_t visible_index,
|
||||||
|
size_t page_size
|
||||||
|
);
|
||||||
|
void lardon3d_image_view_normalize(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
size_t page_size
|
||||||
|
);
|
||||||
|
bool lardon3d_image_view_ascii_contains(
|
||||||
|
const char *text,
|
||||||
|
const char *filter
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
14
meson.build
14
meson.build
|
|
@ -32,6 +32,7 @@ executable(
|
||||||
'src/import.c',
|
'src/import.c',
|
||||||
'src/import_task.c',
|
'src/import_task.c',
|
||||||
'src/image_catalog.c',
|
'src/image_catalog.c',
|
||||||
|
'src/image_view.c',
|
||||||
'src/project.c',
|
'src/project.c',
|
||||||
],
|
],
|
||||||
include_directories: include_directories('include'),
|
include_directories: include_directories('include'),
|
||||||
|
|
@ -75,3 +76,16 @@ image_catalog_test = executable(
|
||||||
)
|
)
|
||||||
|
|
||||||
test('image-catalog', image_catalog_test, timeout: 30)
|
test('image-catalog', image_catalog_test, timeout: 30)
|
||||||
|
|
||||||
|
image_view_test = executable(
|
||||||
|
'test-image-view',
|
||||||
|
sources: [
|
||||||
|
'tests/test_image_view.c',
|
||||||
|
'src/app_state.c',
|
||||||
|
'src/image_catalog.c',
|
||||||
|
'src/image_view.c',
|
||||||
|
],
|
||||||
|
include_directories: include_directories('include'),
|
||||||
|
)
|
||||||
|
|
||||||
|
test('image-view', image_view_test, timeout: 30)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <lardon3d/app.h>
|
#include <lardon3d/app.h>
|
||||||
#include <lardon3d/app_state.h>
|
#include <lardon3d/app_state.h>
|
||||||
#include <lardon3d/image_catalog.h>
|
#include <lardon3d/image_catalog.h>
|
||||||
|
#include <lardon3d/image_view.h>
|
||||||
#include <lardon3d/tui.h>
|
#include <lardon3d/tui.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
@ -21,6 +22,7 @@ lardon3d_app_run(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = lardon3d_tui_run(&state);
|
bool success = lardon3d_tui_run(&state);
|
||||||
|
lardon3d_image_view_destroy(state.image_view);
|
||||||
lardon3d_image_catalog_destroy(state.image_catalog);
|
lardon3d_image_catalog_destroy(state.image_catalog);
|
||||||
lardon3d_tui_shutdown();
|
lardon3d_tui_shutdown();
|
||||||
|
|
||||||
|
|
|
||||||
436
src/image_view.c
Normal file
436
src/image_view.c
Normal file
|
|
@ -0,0 +1,436 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <lardon3d/image_view.h>
|
||||||
|
|
||||||
|
struct Lardon3DImageView {
|
||||||
|
const Lardon3DImageCatalog *catalog;
|
||||||
|
size_t *indices;
|
||||||
|
size_t count;
|
||||||
|
uint64_t total_size;
|
||||||
|
Lardon3DImageSort sort;
|
||||||
|
char filter[LARDON3D_IMAGE_FILTER_CAPACITY];
|
||||||
|
size_t selection;
|
||||||
|
size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned char
|
||||||
|
ascii_lower(unsigned char character)
|
||||||
|
{
|
||||||
|
return character >= 'A' && character <= 'Z'
|
||||||
|
? (unsigned char)(character + ('a' - 'A'))
|
||||||
|
: character;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
lardon3d_image_view_ascii_contains(
|
||||||
|
const char *text,
|
||||||
|
const char *filter
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!text || !filter) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!filter[0]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const unsigned char *start = (const unsigned char *)text;
|
||||||
|
*start;
|
||||||
|
++start) {
|
||||||
|
const unsigned char *left = start;
|
||||||
|
const unsigned char *right = (const unsigned char *)filter;
|
||||||
|
while (*left && *right && ascii_lower(*left) == ascii_lower(*right)) {
|
||||||
|
++left;
|
||||||
|
++right;
|
||||||
|
}
|
||||||
|
if (!*right) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare_indices(
|
||||||
|
const Lardon3DImageView *view,
|
||||||
|
size_t left_index,
|
||||||
|
size_t right_index
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const Lardon3DImageEntry *left = lardon3d_image_catalog_get(
|
||||||
|
view->catalog,
|
||||||
|
left_index
|
||||||
|
);
|
||||||
|
const Lardon3DImageEntry *right = lardon3d_image_catalog_get(
|
||||||
|
view->catalog,
|
||||||
|
right_index
|
||||||
|
);
|
||||||
|
if (!left || !right) {
|
||||||
|
return left ? -1 : right ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int name_comparison = strcmp(left->filename, right->filename);
|
||||||
|
switch (view->sort) {
|
||||||
|
case LARDON3D_IMAGE_SORT_NAME_ASC:
|
||||||
|
return name_comparison;
|
||||||
|
case LARDON3D_IMAGE_SORT_NAME_DESC:
|
||||||
|
return name_comparison > 0 ? -1 : name_comparison < 0 ? 1 : 0;
|
||||||
|
case LARDON3D_IMAGE_SORT_SIZE_ASC:
|
||||||
|
case LARDON3D_IMAGE_SORT_SIZE_DESC:
|
||||||
|
if (left->size_bytes != right->size_bytes) {
|
||||||
|
bool left_first = view->sort == LARDON3D_IMAGE_SORT_SIZE_ASC
|
||||||
|
? left->size_bytes < right->size_bytes
|
||||||
|
: left->size_bytes > right->size_bytes;
|
||||||
|
return left_first ? -1 : 1;
|
||||||
|
}
|
||||||
|
return name_comparison;
|
||||||
|
case LARDON3D_IMAGE_SORT_IMPORT_ORDER:
|
||||||
|
default:
|
||||||
|
return left_index < right_index ? -1 : left_index > right_index ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
merge_sort_range(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
size_t *indices,
|
||||||
|
size_t *temporary,
|
||||||
|
size_t begin,
|
||||||
|
size_t end
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (end - begin < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t middle = begin + (end - begin) / 2;
|
||||||
|
merge_sort_range(view, indices, temporary, begin, middle);
|
||||||
|
merge_sort_range(view, indices, temporary, middle, end);
|
||||||
|
|
||||||
|
size_t left = begin;
|
||||||
|
size_t right = middle;
|
||||||
|
size_t output = begin;
|
||||||
|
while (left < middle || right < end) {
|
||||||
|
if (right >= end
|
||||||
|
|| (left < middle
|
||||||
|
&& compare_indices(view, indices[left], indices[right]) <= 0)) {
|
||||||
|
temporary[output++] = indices[left++];
|
||||||
|
} else {
|
||||||
|
temporary[output++] = indices[right++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void)memcpy(
|
||||||
|
indices + begin,
|
||||||
|
temporary + begin,
|
||||||
|
(end - begin) * sizeof(*indices)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
selected_catalog_index(
|
||||||
|
const Lardon3DImageView *view,
|
||||||
|
size_t *catalog_index
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!view || view->selection >= view->count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*catalog_index = view->indices[view->selection];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
build_view(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
const Lardon3DImageCatalog *catalog,
|
||||||
|
Lardon3DImageSort sort,
|
||||||
|
const char *filter
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char filter_copy[LARDON3D_IMAGE_FILTER_CAPACITY];
|
||||||
|
(void)snprintf(filter_copy, sizeof(filter_copy), "%s", filter);
|
||||||
|
size_t preserved_index = 0;
|
||||||
|
bool preserve = selected_catalog_index(view, &preserved_index);
|
||||||
|
size_t catalog_count = lardon3d_image_catalog_count(catalog);
|
||||||
|
if (catalog_count > SIZE_MAX / sizeof(*view->indices)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t *indices = catalog_count > 0
|
||||||
|
? malloc(catalog_count * sizeof(*indices))
|
||||||
|
: NULL;
|
||||||
|
if (catalog_count > 0 && !indices) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
uint64_t total_size = 0;
|
||||||
|
for (size_t index = 0; index < catalog_count; ++index) {
|
||||||
|
const Lardon3DImageEntry *entry = lardon3d_image_catalog_get(
|
||||||
|
catalog,
|
||||||
|
index
|
||||||
|
);
|
||||||
|
if (entry
|
||||||
|
&& lardon3d_image_view_ascii_contains(
|
||||||
|
entry->filename,
|
||||||
|
filter_copy
|
||||||
|
)) {
|
||||||
|
indices[count++] = index;
|
||||||
|
total_size += entry->size_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Lardon3DImageCatalog *old_catalog = view->catalog;
|
||||||
|
Lardon3DImageSort old_sort = view->sort;
|
||||||
|
view->catalog = catalog;
|
||||||
|
view->sort = sort;
|
||||||
|
if (sort != LARDON3D_IMAGE_SORT_IMPORT_ORDER && count > 1) {
|
||||||
|
size_t *temporary = malloc(count * sizeof(*temporary));
|
||||||
|
if (!temporary) {
|
||||||
|
view->catalog = old_catalog;
|
||||||
|
view->sort = old_sort;
|
||||||
|
free(indices);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
merge_sort_range(view, indices, temporary, 0, count);
|
||||||
|
free(temporary);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(view->indices);
|
||||||
|
view->indices = indices;
|
||||||
|
view->count = count;
|
||||||
|
view->total_size = total_size;
|
||||||
|
view->selection = 0;
|
||||||
|
view->offset = 0;
|
||||||
|
(void)snprintf(view->filter, sizeof(view->filter), "%s", filter_copy);
|
||||||
|
if (preserve) {
|
||||||
|
for (size_t index = 0; index < count; ++index) {
|
||||||
|
if (indices[index] == preserved_index) {
|
||||||
|
view->selection = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lardon3DImageView *
|
||||||
|
lardon3d_image_view_create(const Lardon3DImageCatalog *catalog)
|
||||||
|
{
|
||||||
|
Lardon3DImageView *view = calloc(1, sizeof(*view));
|
||||||
|
if (!view) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
view->catalog = catalog;
|
||||||
|
view->sort = LARDON3D_IMAGE_SORT_IMPORT_ORDER;
|
||||||
|
if (!build_view(view, catalog, view->sort, "")) {
|
||||||
|
free(view);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
lardon3d_image_view_destroy(Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
if (!view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free(view->indices);
|
||||||
|
free(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
lardon3d_image_view_set_catalog(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
const Lardon3DImageCatalog *catalog
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return view && build_view(view, catalog, view->sort, view->filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
lardon3d_image_view_set_sort(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
Lardon3DImageSort sort
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!view || sort < LARDON3D_IMAGE_SORT_IMPORT_ORDER
|
||||||
|
|| sort > LARDON3D_IMAGE_SORT_SIZE_DESC) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return build_view(view, view->catalog, sort, view->filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
lardon3d_image_view_set_filter(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
const char *filter,
|
||||||
|
char *error_message,
|
||||||
|
size_t error_message_size
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (error_message && error_message_size > 0) {
|
||||||
|
error_message[0] = '\0';
|
||||||
|
}
|
||||||
|
if (!view || !filter) {
|
||||||
|
if (error_message && error_message_size > 0) {
|
||||||
|
(void)snprintf(
|
||||||
|
error_message,
|
||||||
|
error_message_size,
|
||||||
|
"Erreur : filtre invalide."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (strnlen(filter, LARDON3D_IMAGE_FILTER_CAPACITY)
|
||||||
|
>= LARDON3D_IMAGE_FILTER_CAPACITY) {
|
||||||
|
if (error_message && error_message_size > 0) {
|
||||||
|
(void)snprintf(
|
||||||
|
error_message,
|
||||||
|
error_message_size,
|
||||||
|
"Erreur : filtre trop long."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!build_view(view, view->catalog, view->sort, filter)) {
|
||||||
|
if (error_message && error_message_size > 0) {
|
||||||
|
(void)snprintf(
|
||||||
|
error_message,
|
||||||
|
error_message_size,
|
||||||
|
"Erreur : mémoire insuffisante pour la vue."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
lardon3d_image_view_rebuild(Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
return view && build_view(view, view->catalog, view->sort, view->filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
lardon3d_image_view_count(const Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
return view ? view->count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
lardon3d_image_view_total_size(const Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
return view ? view->total_size : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Lardon3DImageEntry *
|
||||||
|
lardon3d_image_view_get(
|
||||||
|
const Lardon3DImageView *view,
|
||||||
|
size_t visible_index
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return view && visible_index < view->count
|
||||||
|
? lardon3d_image_catalog_get(view->catalog, view->indices[visible_index])
|
||||||
|
: NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
lardon3d_image_view_catalog_index(
|
||||||
|
const Lardon3DImageView *view,
|
||||||
|
size_t visible_index,
|
||||||
|
size_t *catalog_index
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!view || !catalog_index || visible_index >= view->count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*catalog_index = view->indices[visible_index];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lardon3DImageSort
|
||||||
|
lardon3d_image_view_sort(const Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
return view ? view->sort : LARDON3D_IMAGE_SORT_IMPORT_ORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
lardon3d_image_view_filter(const Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
return view ? view->filter : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
lardon3d_image_view_sort_name(Lardon3DImageSort sort)
|
||||||
|
{
|
||||||
|
switch (sort) {
|
||||||
|
case LARDON3D_IMAGE_SORT_NAME_ASC:
|
||||||
|
return "Nom croissant";
|
||||||
|
case LARDON3D_IMAGE_SORT_NAME_DESC:
|
||||||
|
return "Nom décroissant";
|
||||||
|
case LARDON3D_IMAGE_SORT_SIZE_ASC:
|
||||||
|
return "Taille croissante";
|
||||||
|
case LARDON3D_IMAGE_SORT_SIZE_DESC:
|
||||||
|
return "Taille décroissante";
|
||||||
|
case LARDON3D_IMAGE_SORT_IMPORT_ORDER:
|
||||||
|
default:
|
||||||
|
return "Ordre d'import";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
lardon3d_image_view_selection(const Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
return view ? view->selection : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
lardon3d_image_view_offset(const Lardon3DImageView *view)
|
||||||
|
{
|
||||||
|
return view ? view->offset : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
lardon3d_image_view_normalize(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
size_t page_size
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (view->count == 0) {
|
||||||
|
view->selection = 0;
|
||||||
|
view->offset = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (view->selection >= view->count) {
|
||||||
|
view->selection = view->count - 1;
|
||||||
|
}
|
||||||
|
if (view->offset > view->selection) {
|
||||||
|
view->offset = view->selection;
|
||||||
|
}
|
||||||
|
if (page_size == 0) {
|
||||||
|
page_size = 1;
|
||||||
|
}
|
||||||
|
if (view->selection - view->offset >= page_size) {
|
||||||
|
view->offset = view->selection - page_size + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
lardon3d_image_view_select(
|
||||||
|
Lardon3DImageView *view,
|
||||||
|
size_t visible_index,
|
||||||
|
size_t page_size
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
view->selection = visible_index;
|
||||||
|
lardon3d_image_view_normalize(view, page_size);
|
||||||
|
}
|
||||||
60
src/layout.c
60
src/layout.c
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <lardon3d/layout.h>
|
#include <lardon3d/layout.h>
|
||||||
#include <lardon3d/image_catalog.h>
|
#include <lardon3d/image_catalog.h>
|
||||||
|
#include <lardon3d/image_view.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MINIMUM_ROWS = 20,
|
MINIMUM_ROWS = 20,
|
||||||
|
|
@ -71,7 +72,7 @@ screen_texts(
|
||||||
case LARDON3D_SCREEN_IMPORT:
|
case LARDON3D_SCREEN_IMPORT:
|
||||||
*title = "Import";
|
*title = "Import";
|
||||||
*content = "Import des images";
|
*content = "Import des images";
|
||||||
*footer = "I Importer R Recharger ↑/↓ Naviguer ESC Accueil Q Quit";
|
*footer = "I Importer R Recharger S Tri / Filtre X Effacer ↑/↓ ESC Q";
|
||||||
break;
|
break;
|
||||||
case LARDON3D_SCREEN_VIEWER:
|
case LARDON3D_SCREEN_VIEWER:
|
||||||
*title = "Viewer";
|
*title = "Viewer";
|
||||||
|
|
@ -143,34 +144,67 @@ draw_catalog(
|
||||||
draw_text(7, 4, columns - 6, "Aucun projet chargé.");
|
draw_text(7, 4, columns - 6, "Aucun projet chargé.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
size_t count = lardon3d_image_catalog_count(state->image_catalog);
|
size_t count = lardon3d_image_view_count(state->image_view);
|
||||||
|
size_t total = lardon3d_image_catalog_count(state->image_catalog);
|
||||||
char line[512];
|
char line[512];
|
||||||
(void)snprintf(
|
(void)snprintf(
|
||||||
line,
|
line,
|
||||||
sizeof(line),
|
sizeof(line),
|
||||||
"Images importées : %zu",
|
"Images visibles : %zu / %zu",
|
||||||
count
|
count,
|
||||||
|
total
|
||||||
);
|
);
|
||||||
draw_text(6, 4, columns - 6, line);
|
draw_text(6, 4, columns - 6, line);
|
||||||
char size_text[64];
|
char size_text[64];
|
||||||
lardon3d_image_catalog_format_size(
|
lardon3d_image_catalog_format_size(
|
||||||
lardon3d_image_catalog_total_size(state->image_catalog),
|
lardon3d_image_view_total_size(state->image_view),
|
||||||
size_text,
|
size_text,
|
||||||
sizeof(size_text)
|
sizeof(size_text)
|
||||||
);
|
);
|
||||||
(void)snprintf(line, sizeof(line), "Taille totale : %s", size_text);
|
(void)snprintf(
|
||||||
|
line,
|
||||||
|
sizeof(line),
|
||||||
|
"Taille totale visible : %s",
|
||||||
|
size_text
|
||||||
|
);
|
||||||
draw_text(7, 4, columns - 6, line);
|
draw_text(7, 4, columns - 6, line);
|
||||||
|
(void)snprintf(
|
||||||
|
line,
|
||||||
|
sizeof(line),
|
||||||
|
"Tri : %s",
|
||||||
|
lardon3d_image_view_sort_name(
|
||||||
|
lardon3d_image_view_sort(state->image_view)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
draw_text(8, 4, columns - 6, line);
|
||||||
|
const char *filter = lardon3d_image_view_filter(state->image_view);
|
||||||
|
(void)snprintf(
|
||||||
|
line,
|
||||||
|
sizeof(line),
|
||||||
|
"Filtre : %s",
|
||||||
|
filter[0] ? filter : "Aucun"
|
||||||
|
);
|
||||||
|
draw_text(9, 4, columns - 6, line);
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
draw_text(9, 4, columns - 6, "Aucune image importée.");
|
draw_text(
|
||||||
|
11,
|
||||||
|
4,
|
||||||
|
columns - 6,
|
||||||
|
filter[0]
|
||||||
|
? "Aucune image ne correspond au filtre."
|
||||||
|
: "Aucune image importée."
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int journal_row = rows - 7;
|
int journal_row = rows - 7;
|
||||||
size_t visible = journal_row > 9 ? (size_t)(journal_row - 9) : 0;
|
size_t visible = journal_row > 11 ? (size_t)(journal_row - 11) : 0;
|
||||||
|
size_t offset = lardon3d_image_view_offset(state->image_view);
|
||||||
|
size_t selection = lardon3d_image_view_selection(state->image_view);
|
||||||
for (size_t row = 0; row < visible; ++row) {
|
for (size_t row = 0; row < visible; ++row) {
|
||||||
size_t index = state->image_offset + row;
|
size_t index = offset + row;
|
||||||
const Lardon3DImageEntry *entry = lardon3d_image_catalog_get(
|
const Lardon3DImageEntry *entry = lardon3d_image_view_get(
|
||||||
state->image_catalog,
|
state->image_view,
|
||||||
index
|
index
|
||||||
);
|
);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
|
|
@ -185,11 +219,11 @@ draw_catalog(
|
||||||
line,
|
line,
|
||||||
sizeof(line),
|
sizeof(line),
|
||||||
"%c %s %s",
|
"%c %s %s",
|
||||||
index == state->image_selection ? '>' : ' ',
|
index == selection ? '>' : ' ',
|
||||||
entry->filename,
|
entry->filename,
|
||||||
size_text
|
size_text
|
||||||
);
|
);
|
||||||
draw_text(9 + (int)row, 4, columns - 6, line);
|
draw_text(11 + (int)row, 4, columns - 6, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <lardon3d/project.h>
|
#include <lardon3d/project.h>
|
||||||
#include <lardon3d/image_catalog.h>
|
#include <lardon3d/image_catalog.h>
|
||||||
|
#include <lardon3d/image_view.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MAX_CREATED_DIRECTORIES = 16,
|
MAX_CREATED_DIRECTORIES = 16,
|
||||||
|
|
@ -36,10 +37,10 @@ set_status(Lardon3DAppState *state, const char *message)
|
||||||
static void
|
static void
|
||||||
clear_catalog(Lardon3DAppState *state)
|
clear_catalog(Lardon3DAppState *state)
|
||||||
{
|
{
|
||||||
|
lardon3d_image_view_destroy(state->image_view);
|
||||||
|
state->image_view = NULL;
|
||||||
lardon3d_image_catalog_destroy(state->image_catalog);
|
lardon3d_image_catalog_destroy(state->image_catalog);
|
||||||
state->image_catalog = NULL;
|
state->image_catalog = NULL;
|
||||||
state->image_selection = 0;
|
|
||||||
state->image_offset = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
|
||||||
263
src/tui.c
263
src/tui.c
|
|
@ -3,9 +3,11 @@
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <lardon3d/import_task.h>
|
#include <lardon3d/import_task.h>
|
||||||
#include <lardon3d/image_catalog.h>
|
#include <lardon3d/image_catalog.h>
|
||||||
|
#include <lardon3d/image_view.h>
|
||||||
#include <lardon3d/layout.h>
|
#include <lardon3d/layout.h>
|
||||||
#include <lardon3d/project.h>
|
#include <lardon3d/project.h>
|
||||||
#include <lardon3d/tui.h>
|
#include <lardon3d/tui.h>
|
||||||
|
|
@ -20,6 +22,7 @@ typedef enum {
|
||||||
INPUT_PROJECT_CREATE,
|
INPUT_PROJECT_CREATE,
|
||||||
INPUT_PROJECT_OPEN,
|
INPUT_PROJECT_OPEN,
|
||||||
INPUT_IMPORT_DIRECTORY,
|
INPUT_IMPORT_DIRECTORY,
|
||||||
|
INPUT_IMAGE_FILTER,
|
||||||
} InputMode;
|
} InputMode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -35,58 +38,108 @@ catalog_page_size(void)
|
||||||
int columns;
|
int columns;
|
||||||
getmaxyx(stdscr, rows, columns);
|
getmaxyx(stdscr, rows, columns);
|
||||||
(void)columns;
|
(void)columns;
|
||||||
return rows > 16 ? (size_t)(rows - 16) : 1;
|
return rows > 18 ? (size_t)(rows - 18) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
normalize_catalog_navigation(Lardon3DAppState *state)
|
normalize_view_navigation(Lardon3DAppState *state)
|
||||||
{
|
{
|
||||||
size_t count = lardon3d_image_catalog_count(state->image_catalog);
|
lardon3d_image_view_normalize(state->image_view, catalog_page_size());
|
||||||
if (count == 0) {
|
|
||||||
state->image_selection = 0;
|
|
||||||
state->image_offset = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state->image_selection >= count) {
|
|
||||||
state->image_selection = count - 1;
|
|
||||||
}
|
|
||||||
if (state->image_offset > state->image_selection) {
|
|
||||||
state->image_offset = state->image_selection;
|
|
||||||
}
|
|
||||||
size_t page_size = catalog_page_size();
|
|
||||||
if (state->image_selection - state->image_offset >= page_size) {
|
|
||||||
state->image_offset = state->image_selection - page_size + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
reload_catalog(Lardon3DAppState *state, bool announce_success)
|
reload_catalog(Lardon3DAppState *state, bool announce_success)
|
||||||
{
|
{
|
||||||
|
Lardon3DImageSort sort = lardon3d_image_view_sort(state->image_view);
|
||||||
|
char filter[LARDON3D_IMAGE_FILTER_CAPACITY];
|
||||||
|
(void)snprintf(
|
||||||
|
filter,
|
||||||
|
sizeof(filter),
|
||||||
|
"%s",
|
||||||
|
lardon3d_image_view_filter(state->image_view)
|
||||||
|
);
|
||||||
|
size_t selected_catalog = 0;
|
||||||
|
bool preserve_selection = lardon3d_image_view_catalog_index(
|
||||||
|
state->image_view,
|
||||||
|
lardon3d_image_view_selection(state->image_view),
|
||||||
|
&selected_catalog
|
||||||
|
);
|
||||||
|
lardon3d_image_view_destroy(state->image_view);
|
||||||
|
state->image_view = NULL;
|
||||||
lardon3d_image_catalog_destroy(state->image_catalog);
|
lardon3d_image_catalog_destroy(state->image_catalog);
|
||||||
state->image_catalog = NULL;
|
state->image_catalog = NULL;
|
||||||
char message[sizeof(state->status_message)];
|
char message[sizeof(state->status_message)] = "";
|
||||||
state->image_catalog = lardon3d_image_catalog_load(
|
state->image_catalog = lardon3d_image_catalog_load(
|
||||||
state,
|
state,
|
||||||
message,
|
message,
|
||||||
sizeof(message)
|
sizeof(message)
|
||||||
);
|
);
|
||||||
normalize_catalog_navigation(state);
|
if (!state->image_catalog) {
|
||||||
if (!state->image_catalog || message[0]) {
|
|
||||||
(void)snprintf(
|
(void)snprintf(
|
||||||
state->status_message,
|
state->status_message,
|
||||||
sizeof(state->status_message),
|
sizeof(state->status_message),
|
||||||
"%s",
|
"%s",
|
||||||
message
|
message
|
||||||
);
|
);
|
||||||
return state->image_catalog != NULL;
|
return false;
|
||||||
|
}
|
||||||
|
char catalog_message[sizeof(message)];
|
||||||
|
(void)snprintf(catalog_message, sizeof(catalog_message), "%s", message);
|
||||||
|
char view_error[sizeof(message)];
|
||||||
|
state->image_view = lardon3d_image_view_create(state->image_catalog);
|
||||||
|
if (!state->image_view
|
||||||
|
|| !lardon3d_image_view_set_sort(state->image_view, sort)
|
||||||
|
|| !lardon3d_image_view_set_filter(
|
||||||
|
state->image_view,
|
||||||
|
filter,
|
||||||
|
view_error,
|
||||||
|
sizeof(view_error)
|
||||||
|
)) {
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"Erreur : impossible de construire la vue des images."
|
||||||
|
);
|
||||||
|
lardon3d_image_view_destroy(state->image_view);
|
||||||
|
state->image_view = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (preserve_selection) {
|
||||||
|
size_t count = lardon3d_image_view_count(state->image_view);
|
||||||
|
for (size_t index = 0; index < count; ++index) {
|
||||||
|
size_t catalog_index;
|
||||||
|
if (lardon3d_image_view_catalog_index(
|
||||||
|
state->image_view,
|
||||||
|
index,
|
||||||
|
&catalog_index
|
||||||
|
)
|
||||||
|
&& catalog_index == selected_catalog) {
|
||||||
|
lardon3d_image_view_select(
|
||||||
|
state->image_view,
|
||||||
|
index,
|
||||||
|
catalog_page_size()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
normalize_view_navigation(state);
|
||||||
|
if (catalog_message[0]) {
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"%s",
|
||||||
|
catalog_message
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (announce_success) {
|
if (announce_success) {
|
||||||
(void)snprintf(
|
(void)snprintf(
|
||||||
state->status_message,
|
state->status_message,
|
||||||
sizeof(state->status_message),
|
sizeof(state->status_message),
|
||||||
"Catalogue rechargé : %zu image%s.",
|
"Catalogue rechargé : %zu image%s.",
|
||||||
lardon3d_image_catalog_count(state->image_catalog),
|
lardon3d_image_view_count(state->image_view),
|
||||||
lardon3d_image_catalog_count(state->image_catalog) == 1 ? "" : "s"
|
lardon3d_image_view_count(state->image_view) == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -109,6 +162,8 @@ redraw(
|
||||||
label = "Nom du dossier projet :";
|
label = "Nom du dossier projet :";
|
||||||
} else if (input->mode == INPUT_IMPORT_DIRECTORY) {
|
} else if (input->mode == INPUT_IMPORT_DIRECTORY) {
|
||||||
label = "Dossier source :";
|
label = "Dossier source :";
|
||||||
|
} else if (input->mode == INPUT_IMAGE_FILTER) {
|
||||||
|
label = "Filtre :";
|
||||||
}
|
}
|
||||||
Lardon3DImportTaskSnapshot snapshot;
|
Lardon3DImportTaskSnapshot snapshot;
|
||||||
const Lardon3DImportTaskSnapshot *displayed_snapshot = NULL;
|
const Lardon3DImportTaskSnapshot *displayed_snapshot = NULL;
|
||||||
|
|
@ -187,6 +242,8 @@ handle_active_input(
|
||||||
message = "Ouverture du projet annulée.";
|
message = "Ouverture du projet annulée.";
|
||||||
} else if (input->mode == INPUT_IMPORT_DIRECTORY) {
|
} else if (input->mode == INPUT_IMPORT_DIRECTORY) {
|
||||||
message = "Import annulé.";
|
message = "Import annulé.";
|
||||||
|
} else if (input->mode == INPUT_IMAGE_FILTER) {
|
||||||
|
message = "Filtre annulé.";
|
||||||
}
|
}
|
||||||
input->mode = INPUT_NONE;
|
input->mode = INPUT_NONE;
|
||||||
(void)snprintf(
|
(void)snprintf(
|
||||||
|
|
@ -205,6 +262,37 @@ handle_active_input(
|
||||||
}
|
}
|
||||||
} else if (input->mode == INPUT_IMPORT_DIRECTORY) {
|
} else if (input->mode == INPUT_IMPORT_DIRECTORY) {
|
||||||
(void)start_import_task(state, input, task);
|
(void)start_import_task(state, input, task);
|
||||||
|
} else if (input->mode == INPUT_IMAGE_FILTER) {
|
||||||
|
char message[sizeof(state->status_message)];
|
||||||
|
if (lardon3d_image_view_set_filter(
|
||||||
|
state->image_view,
|
||||||
|
input->text,
|
||||||
|
message,
|
||||||
|
sizeof(message)
|
||||||
|
)) {
|
||||||
|
normalize_view_navigation(state);
|
||||||
|
if (lardon3d_image_view_count(state->image_view) == 0) {
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"Aucune image ne correspond au filtre."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"Filtre appliqué : %.230s",
|
||||||
|
input->text
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"%s",
|
||||||
|
message
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (lardon3d_project_create(state, input->text)) {
|
if (lardon3d_project_create(state, input->text)) {
|
||||||
(void)reload_catalog(state, false);
|
(void)reload_catalog(state, false);
|
||||||
|
|
@ -223,7 +311,18 @@ handle_active_input(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key >= 0 && key <= UCHAR_MAX && isprint((unsigned char)key)) {
|
if (key >= 0 && key <= UCHAR_MAX && isprint((unsigned char)key)) {
|
||||||
if (input->length + 1 >= sizeof(input->text)) {
|
size_t capacity = input->mode == INPUT_IMAGE_FILTER
|
||||||
|
? LARDON3D_IMAGE_FILTER_CAPACITY
|
||||||
|
: sizeof(input->text);
|
||||||
|
if (input->length + 1 >= capacity) {
|
||||||
|
if (input->mode == INPUT_IMAGE_FILTER) {
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"Erreur : filtre trop long."
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (input->mode == INPUT_PROJECT_OPEN) {
|
if (input->mode == INPUT_PROJECT_OPEN) {
|
||||||
if (lardon3d_project_open(state, input->text)) {
|
if (lardon3d_project_open(state, input->text)) {
|
||||||
(void)reload_catalog(state, false);
|
(void)reload_catalog(state, false);
|
||||||
|
|
@ -301,24 +400,31 @@ handle_normal_input(
|
||||||
state->screen = LARDON3D_SCREEN_HOME;
|
state->screen = LARDON3D_SCREEN_HOME;
|
||||||
return true;
|
return true;
|
||||||
case KEY_RESIZE:
|
case KEY_RESIZE:
|
||||||
normalize_catalog_navigation(state);
|
normalize_view_navigation(state);
|
||||||
return true;
|
return true;
|
||||||
case KEY_UP:
|
case KEY_UP:
|
||||||
case 'k':
|
case 'k':
|
||||||
if (state->screen == LARDON3D_SCREEN_IMPORT
|
if (state->screen == LARDON3D_SCREEN_IMPORT
|
||||||
&& state->image_selection > 0) {
|
&& lardon3d_image_view_selection(state->image_view) > 0) {
|
||||||
--state->image_selection;
|
lardon3d_image_view_select(
|
||||||
normalize_catalog_navigation(state);
|
state->image_view,
|
||||||
|
lardon3d_image_view_selection(state->image_view) - 1,
|
||||||
|
catalog_page_size()
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case KEY_DOWN:
|
case KEY_DOWN:
|
||||||
case 'j': {
|
case 'j': {
|
||||||
size_t count = lardon3d_image_catalog_count(state->image_catalog);
|
size_t count = lardon3d_image_view_count(state->image_view);
|
||||||
|
size_t selection = lardon3d_image_view_selection(state->image_view);
|
||||||
if (state->screen == LARDON3D_SCREEN_IMPORT
|
if (state->screen == LARDON3D_SCREEN_IMPORT
|
||||||
&& state->image_selection + 1 < count) {
|
&& selection < count && selection + 1 < count) {
|
||||||
++state->image_selection;
|
lardon3d_image_view_select(
|
||||||
normalize_catalog_navigation(state);
|
state->image_view,
|
||||||
|
selection + 1,
|
||||||
|
catalog_page_size()
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -326,39 +432,53 @@ handle_normal_input(
|
||||||
case KEY_PPAGE:
|
case KEY_PPAGE:
|
||||||
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
||||||
size_t page_size = catalog_page_size();
|
size_t page_size = catalog_page_size();
|
||||||
state->image_selection = state->image_selection > page_size
|
size_t selection = lardon3d_image_view_selection(state->image_view);
|
||||||
? state->image_selection - page_size
|
selection = selection > page_size
|
||||||
|
? selection - page_size
|
||||||
: 0;
|
: 0;
|
||||||
normalize_catalog_navigation(state);
|
lardon3d_image_view_select(state->image_view, selection, page_size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case KEY_NPAGE:
|
case KEY_NPAGE:
|
||||||
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
||||||
size_t count = lardon3d_image_catalog_count(state->image_catalog);
|
size_t count = lardon3d_image_view_count(state->image_view);
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
size_t remaining = count - 1 - state->image_selection;
|
size_t selection = lardon3d_image_view_selection(
|
||||||
|
state->image_view
|
||||||
|
);
|
||||||
|
size_t remaining = count - 1 - selection;
|
||||||
size_t page_size = catalog_page_size();
|
size_t page_size = catalog_page_size();
|
||||||
state->image_selection += remaining < page_size
|
selection += remaining < page_size
|
||||||
? remaining
|
? remaining
|
||||||
: page_size;
|
: page_size;
|
||||||
normalize_catalog_navigation(state);
|
lardon3d_image_view_select(
|
||||||
|
state->image_view,
|
||||||
|
selection,
|
||||||
|
page_size
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case KEY_HOME:
|
case KEY_HOME:
|
||||||
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
||||||
state->image_selection = 0;
|
lardon3d_image_view_select(
|
||||||
normalize_catalog_navigation(state);
|
state->image_view,
|
||||||
|
0,
|
||||||
|
catalog_page_size()
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case KEY_END:
|
case KEY_END:
|
||||||
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
if (state->screen == LARDON3D_SCREEN_IMPORT) {
|
||||||
size_t count = lardon3d_image_catalog_count(state->image_catalog);
|
size_t count = lardon3d_image_view_count(state->image_view);
|
||||||
state->image_selection = count > 0 ? count - 1 : 0;
|
lardon3d_image_view_select(
|
||||||
normalize_catalog_navigation(state);
|
state->image_view,
|
||||||
|
count > 0 ? count - 1 : 0,
|
||||||
|
catalog_page_size()
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -418,6 +538,59 @@ handle_normal_input(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
case 's':
|
||||||
|
case 'S':
|
||||||
|
if (state->screen == LARDON3D_SCREEN_IMPORT && state->image_view) {
|
||||||
|
Lardon3DImageSort sort = lardon3d_image_view_sort(
|
||||||
|
state->image_view
|
||||||
|
);
|
||||||
|
sort = (Lardon3DImageSort)(
|
||||||
|
((int)sort + 1) % ((int)LARDON3D_IMAGE_SORT_SIZE_DESC + 1)
|
||||||
|
);
|
||||||
|
if (lardon3d_image_view_set_sort(state->image_view, sort)) {
|
||||||
|
normalize_view_navigation(state);
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"Tri : %s.",
|
||||||
|
lardon3d_image_view_sort_name(sort)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case '/':
|
||||||
|
if (state->screen == LARDON3D_SCREEN_IMPORT && state->image_view) {
|
||||||
|
const char *filter = lardon3d_image_view_filter(state->image_view);
|
||||||
|
*input = (TuiInput) {
|
||||||
|
.mode = INPUT_IMAGE_FILTER,
|
||||||
|
.text = "",
|
||||||
|
.length = strlen(filter),
|
||||||
|
};
|
||||||
|
(void)snprintf(input->text, sizeof(input->text), "%s", filter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
if (state->screen == LARDON3D_SCREEN_IMPORT && state->image_view) {
|
||||||
|
char message[sizeof(state->status_message)];
|
||||||
|
if (lardon3d_image_view_set_filter(
|
||||||
|
state->image_view,
|
||||||
|
"",
|
||||||
|
message,
|
||||||
|
sizeof(message)
|
||||||
|
)) {
|
||||||
|
normalize_view_navigation(state);
|
||||||
|
(void)snprintf(
|
||||||
|
state->status_message,
|
||||||
|
sizeof(state->status_message),
|
||||||
|
"Filtre effacé."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
case 'c':
|
case 'c':
|
||||||
case 'C':
|
case 'C':
|
||||||
if (state->screen == LARDON3D_SCREEN_PROJECTS) {
|
if (state->screen == LARDON3D_SCREEN_PROJECTS) {
|
||||||
|
|
|
||||||
316
tests/test_image_view.c
Normal file
316
tests/test_image_view.c
Normal file
|
|
@ -0,0 +1,316 @@
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <lardon3d/app_state.h>
|
||||||
|
#include <lardon3d/image_catalog.h>
|
||||||
|
#include <lardon3d/image_view.h>
|
||||||
|
|
||||||
|
#define CHECK(condition) \
|
||||||
|
do { \
|
||||||
|
if (!(condition)) { \
|
||||||
|
(void)fprintf(stderr, "Échec ligne %d : %s\n", __LINE__, #condition); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static bool
|
||||||
|
join_path(char destination[PATH_MAX], const char *parent, const char *child)
|
||||||
|
{
|
||||||
|
int written = snprintf(destination, PATH_MAX, "%s/%s", parent, child);
|
||||||
|
return written >= 0 && (size_t)written < PATH_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
create_file(const char *path, size_t size)
|
||||||
|
{
|
||||||
|
int descriptor = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||||
|
if (descriptor < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char bytes[16] = {0};
|
||||||
|
size_t remaining = size;
|
||||||
|
while (remaining > 0) {
|
||||||
|
size_t length = remaining < sizeof(bytes) ? remaining : sizeof(bytes);
|
||||||
|
ssize_t written = write(descriptor, bytes, length);
|
||||||
|
if (written < 0 && errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (written <= 0) {
|
||||||
|
(void)close(descriptor);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
remaining -= (size_t)written;
|
||||||
|
}
|
||||||
|
return close(descriptor) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
write_manifest(const char *path, const char *content)
|
||||||
|
{
|
||||||
|
int descriptor = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||||
|
if (descriptor < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t length = strlen(content);
|
||||||
|
size_t total = 0;
|
||||||
|
while (total < length) {
|
||||||
|
ssize_t written = write(descriptor, content + total, length - total);
|
||||||
|
if (written < 0 && errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (written <= 0) {
|
||||||
|
(void)close(descriptor);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
total += (size_t)written;
|
||||||
|
}
|
||||||
|
return close(descriptor) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
remove_tree(const char *path)
|
||||||
|
{
|
||||||
|
struct stat info;
|
||||||
|
if (lstat(path, &info) != 0) {
|
||||||
|
return errno == ENOENT;
|
||||||
|
}
|
||||||
|
if (!S_ISDIR(info.st_mode)) {
|
||||||
|
return unlink(path) == 0;
|
||||||
|
}
|
||||||
|
DIR *directory = opendir(path);
|
||||||
|
if (!directory) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = true;
|
||||||
|
for (struct dirent *entry = readdir(directory);
|
||||||
|
entry;
|
||||||
|
entry = readdir(directory)) {
|
||||||
|
if (strcmp(entry->d_name, ".") == 0
|
||||||
|
|| strcmp(entry->d_name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char child[PATH_MAX];
|
||||||
|
if (!join_path(child, path, entry->d_name) || !remove_tree(child)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (closedir(directory) != 0 || rmdir(path) != 0) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Lardon3DImageCatalog *
|
||||||
|
load_catalog(const char *project)
|
||||||
|
{
|
||||||
|
Lardon3DAppState state;
|
||||||
|
lardon3d_app_state_init(&state);
|
||||||
|
state.project_loaded = true;
|
||||||
|
(void)snprintf(state.project_path, sizeof(state.project_path), "%s", project);
|
||||||
|
char error[256];
|
||||||
|
return lardon3d_image_catalog_load(&state, error, sizeof(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
expect_names(
|
||||||
|
const Lardon3DImageView *view,
|
||||||
|
const char *const *names,
|
||||||
|
size_t count
|
||||||
|
)
|
||||||
|
{
|
||||||
|
CHECK(lardon3d_image_view_count(view) == count);
|
||||||
|
for (size_t index = 0; index < count; ++index) {
|
||||||
|
const Lardon3DImageEntry *entry = lardon3d_image_view_get(view, index);
|
||||||
|
CHECK(entry && strcmp(entry->filename, names[index]) == 0);
|
||||||
|
}
|
||||||
|
CHECK(lardon3d_image_view_get(view, count) == NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
run_test(void)
|
||||||
|
{
|
||||||
|
char base[] = "/tmp/lardon3d-image-view.XXXXXX";
|
||||||
|
CHECK(mkdtemp(base));
|
||||||
|
char project[PATH_MAX];
|
||||||
|
char images[PATH_MAX];
|
||||||
|
char originals[PATH_MAX];
|
||||||
|
char manifest[PATH_MAX];
|
||||||
|
CHECK(join_path(project, base, "project"));
|
||||||
|
CHECK(join_path(images, project, "images"));
|
||||||
|
CHECK(join_path(originals, images, "originals"));
|
||||||
|
CHECK(join_path(manifest, images, "manifest.tsv"));
|
||||||
|
CHECK(mkdir(project, 0755) == 0);
|
||||||
|
CHECK(mkdir(images, 0755) == 0);
|
||||||
|
CHECK(mkdir(originals, 0755) == 0);
|
||||||
|
|
||||||
|
Lardon3DImageCatalog *empty_catalog = load_catalog(project);
|
||||||
|
CHECK(empty_catalog);
|
||||||
|
Lardon3DImageView *view = lardon3d_image_view_create(empty_catalog);
|
||||||
|
CHECK(view);
|
||||||
|
CHECK(lardon3d_image_view_count(view) == 0);
|
||||||
|
CHECK(lardon3d_image_view_total_size(view) == 0);
|
||||||
|
lardon3d_image_view_normalize(view, 0);
|
||||||
|
CHECK(lardon3d_image_view_selection(view) == 0);
|
||||||
|
CHECK(lardon3d_image_view_offset(view) == 0);
|
||||||
|
lardon3d_image_view_destroy(view);
|
||||||
|
lardon3d_image_catalog_destroy(empty_catalog);
|
||||||
|
lardon3d_image_view_destroy(NULL);
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *name;
|
||||||
|
size_t size;
|
||||||
|
} files[] = {
|
||||||
|
{"Zulu.JPG", 5},
|
||||||
|
{"alpha.png", 2},
|
||||||
|
{"Beta.jpg", 5},
|
||||||
|
{"été.png", 3},
|
||||||
|
{"motor final.TIF", 8},
|
||||||
|
};
|
||||||
|
for (size_t index = 0; index < sizeof(files) / sizeof(files[0]); ++index) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
CHECK(join_path(path, originals, files[index].name));
|
||||||
|
CHECK(create_file(path, files[index].size));
|
||||||
|
}
|
||||||
|
CHECK(write_manifest(
|
||||||
|
manifest,
|
||||||
|
"filename\tsize_bytes\tsource_path\n"
|
||||||
|
"Zulu.JPG\t5\t/tmp/source/Zulu.JPG\n"
|
||||||
|
"alpha.png\t2\t/tmp/source/alpha.png\n"
|
||||||
|
"Beta.jpg\t5\t/tmp/source/Beta.jpg\n"
|
||||||
|
"été.png\t3\t/tmp/source avec espaces/été.png\n"
|
||||||
|
"motor final.TIF\t8\t/tmp/source/motor final.TIF\n"
|
||||||
|
));
|
||||||
|
Lardon3DImageCatalog *catalog = load_catalog(project);
|
||||||
|
CHECK(catalog && lardon3d_image_catalog_count(catalog) == 5);
|
||||||
|
const Lardon3DImageEntry *original_entries[5];
|
||||||
|
for (size_t index = 0; index < 5; ++index) {
|
||||||
|
original_entries[index] = lardon3d_image_catalog_get(catalog, index);
|
||||||
|
}
|
||||||
|
view = lardon3d_image_view_create(catalog);
|
||||||
|
CHECK(view);
|
||||||
|
|
||||||
|
static const char *const import_order[] = {
|
||||||
|
"Zulu.JPG", "alpha.png", "Beta.jpg", "été.png", "motor final.TIF",
|
||||||
|
};
|
||||||
|
static const char *const name_ascending[] = {
|
||||||
|
"Beta.jpg", "Zulu.JPG", "alpha.png", "motor final.TIF", "été.png",
|
||||||
|
};
|
||||||
|
static const char *const name_descending[] = {
|
||||||
|
"été.png", "motor final.TIF", "alpha.png", "Zulu.JPG", "Beta.jpg",
|
||||||
|
};
|
||||||
|
static const char *const size_ascending[] = {
|
||||||
|
"alpha.png", "été.png", "Beta.jpg", "Zulu.JPG", "motor final.TIF",
|
||||||
|
};
|
||||||
|
static const char *const size_descending[] = {
|
||||||
|
"motor final.TIF", "Beta.jpg", "Zulu.JPG", "été.png", "alpha.png",
|
||||||
|
};
|
||||||
|
CHECK(expect_names(view, import_order, 5));
|
||||||
|
CHECK(lardon3d_image_view_total_size(view) == 23);
|
||||||
|
CHECK(lardon3d_image_view_rebuild(view));
|
||||||
|
CHECK(expect_names(view, import_order, 5));
|
||||||
|
CHECK(lardon3d_image_view_set_sort(view, LARDON3D_IMAGE_SORT_NAME_ASC));
|
||||||
|
CHECK(expect_names(view, name_ascending, 5));
|
||||||
|
CHECK(lardon3d_image_view_set_sort(view, LARDON3D_IMAGE_SORT_NAME_DESC));
|
||||||
|
CHECK(expect_names(view, name_descending, 5));
|
||||||
|
CHECK(lardon3d_image_view_set_sort(view, LARDON3D_IMAGE_SORT_SIZE_ASC));
|
||||||
|
CHECK(expect_names(view, size_ascending, 5));
|
||||||
|
CHECK(lardon3d_image_view_set_sort(view, LARDON3D_IMAGE_SORT_SIZE_DESC));
|
||||||
|
CHECK(expect_names(view, size_descending, 5));
|
||||||
|
CHECK(!lardon3d_image_view_set_sort(view, (Lardon3DImageSort)99));
|
||||||
|
|
||||||
|
char error[256];
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "", error, sizeof(error)));
|
||||||
|
CHECK(lardon3d_image_view_count(view) == 5);
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "JPG", error, sizeof(error)));
|
||||||
|
CHECK(lardon3d_image_view_count(view) == 2);
|
||||||
|
CHECK(lardon3d_image_view_total_size(view) == 10);
|
||||||
|
CHECK(lardon3d_image_view_ascii_contains("Alpha.JPEG", "pHa.j"));
|
||||||
|
CHECK(!lardon3d_image_view_ascii_contains("été.png", "ÉTÉ"));
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "été", error, sizeof(error)));
|
||||||
|
CHECK(expect_names(view, (const char *const[]){"été.png"}, 1));
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "absent", error, sizeof(error)));
|
||||||
|
CHECK(lardon3d_image_view_count(view) == 0);
|
||||||
|
CHECK(lardon3d_image_view_selection(view) == 0);
|
||||||
|
CHECK(lardon3d_image_view_offset(view) == 0);
|
||||||
|
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "", error, sizeof(error)));
|
||||||
|
CHECK(lardon3d_image_view_set_sort(view, LARDON3D_IMAGE_SORT_IMPORT_ORDER));
|
||||||
|
lardon3d_image_view_select(view, 2, 2);
|
||||||
|
size_t selected_catalog;
|
||||||
|
CHECK(lardon3d_image_view_catalog_index(view, 2, &selected_catalog));
|
||||||
|
CHECK(selected_catalog == 2);
|
||||||
|
CHECK(!lardon3d_image_view_catalog_index(view, 99, &selected_catalog));
|
||||||
|
CHECK(lardon3d_image_view_set_sort(view, LARDON3D_IMAGE_SORT_NAME_ASC));
|
||||||
|
CHECK(strcmp(
|
||||||
|
lardon3d_image_view_get(
|
||||||
|
view,
|
||||||
|
lardon3d_image_view_selection(view)
|
||||||
|
)->filename,
|
||||||
|
"Beta.jpg"
|
||||||
|
) == 0);
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "beta", error, sizeof(error)));
|
||||||
|
CHECK(lardon3d_image_view_selection(view) == 0);
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "alpha", error, sizeof(error)));
|
||||||
|
CHECK(lardon3d_image_view_selection(view) == 0);
|
||||||
|
CHECK(strcmp(lardon3d_image_view_get(view, 0)->filename, "alpha.png") == 0);
|
||||||
|
|
||||||
|
char too_long[LARDON3D_IMAGE_FILTER_CAPACITY + 1];
|
||||||
|
(void)memset(too_long, 'a', sizeof(too_long) - 1);
|
||||||
|
too_long[sizeof(too_long) - 1] = '\0';
|
||||||
|
CHECK(!lardon3d_image_view_set_filter(view, too_long, error, sizeof(error)));
|
||||||
|
CHECK(error[0]);
|
||||||
|
CHECK(strcmp(lardon3d_image_view_filter(view), "alpha") == 0);
|
||||||
|
|
||||||
|
for (size_t index = 0; index < 5; ++index) {
|
||||||
|
CHECK(lardon3d_image_catalog_get(catalog, index) == original_entries[index]);
|
||||||
|
CHECK(strcmp(original_entries[index]->filename, import_order[index]) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
char small_project[PATH_MAX];
|
||||||
|
char small_images[PATH_MAX];
|
||||||
|
char small_originals[PATH_MAX];
|
||||||
|
char small_manifest[PATH_MAX];
|
||||||
|
CHECK(join_path(small_project, base, "small"));
|
||||||
|
CHECK(join_path(small_images, small_project, "images"));
|
||||||
|
CHECK(join_path(small_originals, small_images, "originals"));
|
||||||
|
CHECK(join_path(small_manifest, small_images, "manifest.tsv"));
|
||||||
|
CHECK(mkdir(small_project, 0755) == 0);
|
||||||
|
CHECK(mkdir(small_images, 0755) == 0);
|
||||||
|
CHECK(mkdir(small_originals, 0755) == 0);
|
||||||
|
char only[PATH_MAX];
|
||||||
|
CHECK(join_path(only, small_originals, "only.jpg"));
|
||||||
|
CHECK(create_file(only, 1));
|
||||||
|
CHECK(write_manifest(
|
||||||
|
small_manifest,
|
||||||
|
"filename\tsize_bytes\tsource_path\nonly.jpg\t1\t/tmp/only.jpg\n"
|
||||||
|
));
|
||||||
|
Lardon3DImageCatalog *small_catalog = load_catalog(small_project);
|
||||||
|
CHECK(small_catalog);
|
||||||
|
CHECK(lardon3d_image_view_set_filter(view, "", error, sizeof(error)));
|
||||||
|
CHECK(lardon3d_image_view_set_catalog(view, small_catalog));
|
||||||
|
CHECK(lardon3d_image_view_count(view) == 1);
|
||||||
|
CHECK(strcmp(lardon3d_image_view_get(view, 0)->filename, "only.jpg") == 0);
|
||||||
|
lardon3d_image_view_select(view, 100, 3);
|
||||||
|
CHECK(lardon3d_image_view_selection(view) == 0);
|
||||||
|
CHECK(lardon3d_image_view_offset(view) == 0);
|
||||||
|
|
||||||
|
lardon3d_image_view_destroy(view);
|
||||||
|
lardon3d_image_catalog_destroy(small_catalog);
|
||||||
|
lardon3d_image_catalog_destroy(catalog);
|
||||||
|
CHECK(remove_tree(base));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
return run_test() ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue