Compare commits
31
Commits
d6142950d9
...
0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b5b690435 | ||
|
|
c6543cc583 | ||
|
|
05237cc7f1 | ||
|
|
176c2d8be5 | ||
|
|
5db402c7cf | ||
|
|
1a15d93f76 | ||
|
|
0471375aab | ||
|
|
2427831b9b | ||
|
|
42f3b43a68 | ||
|
|
c69c420781 | ||
|
|
673b58caec | ||
|
|
32365b9617 | ||
|
|
90b4e7db00 | ||
|
|
e4f307f183 | ||
|
|
fac33176ed | ||
|
|
e8d5e7d2b1 | ||
|
|
a0314628bd | ||
|
|
b5d9009ce1 | ||
|
|
7eee20414c | ||
|
|
d82b5466b0 | ||
|
|
7c7859dc90 | ||
|
|
2f3ef9736c | ||
|
|
07c7235831 | ||
|
|
f18d0cf043 | ||
|
|
619774c809 | ||
|
|
29c375b20c | ||
|
|
750f7a9149 | ||
|
|
5508532619 | ||
|
|
499b3787c6 | ||
|
|
4d9eba1512 | ||
|
|
99d5e5244c |
No files matched your search
@@ -0,0 +1,4 @@
|
|||||||
|
build/
|
||||||
|
.vscode/
|
||||||
|
productManager
|
||||||
|
test
|
||||||
Vendored
-11
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"makefile.configureOnOpen": false,
|
|
||||||
"files.associations": {
|
|
||||||
"ncurses.h": "c",
|
|
||||||
"logic.h": "c",
|
|
||||||
"stdio.h": "c",
|
|
||||||
"globals.h": "c",
|
|
||||||
"windows.h": "c",
|
|
||||||
"stdlib.h": "c"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Product Manager
|
||||||
|
Un gestore di inventario e vendite minimale
|
||||||
|
|
||||||
|
## Scaricarlo
|
||||||
|
Disponibile solo per linux.
|
||||||
|
Scarica l'ultima versione dalla pagina delle releases!
|
||||||
|
Avvia l'eseguibile da un terminale.
|
||||||
|
|
||||||
|
***questo codice molto probabilmente NON è sicuro, utilizzarlo a propria indiscrezione***
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
wip
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
build/globals.o: src/globals.c src/../include/globals.h
|
|
||||||
src/../include/globals.h:
|
|
||||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
build/logic.o: src/logic.c src/../include/logic.h \
|
|
||||||
src/../include/globals.h
|
|
||||||
src/../include/logic.h:
|
|
||||||
src/../include/globals.h:
|
|
||||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
|||||||
build/main.o: src/main.c include/windows.h include/logic.h
|
|
||||||
include/windows.h:
|
|
||||||
include/logic.h:
|
|
||||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
build/testLogic.o: src/testLogic.c src/../include/logic.h \
|
|
||||||
src/../include/globals.h
|
|
||||||
src/../include/logic.h:
|
|
||||||
src/../include/globals.h:
|
|
||||||
Binary file not shown.
@@ -1,5 +0,0 @@
|
|||||||
build/windows.o: src/windows.c src/../include/windows.h \
|
|
||||||
src/../include/logic.h src/../include/globals.h
|
|
||||||
src/../include/windows.h:
|
|
||||||
src/../include/logic.h:
|
|
||||||
src/../include/globals.h:
|
|
||||||
Binary file not shown.
@@ -6,5 +6,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
extern char productPricesFileLocation[64]; // Dichiarazione extern
|
extern char productPricesFileLocation[64]; // Dichiarazione extern
|
||||||
|
extern char countFileLocation[64];
|
||||||
|
extern char configFileLocation[64];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef JSONFILE_H
|
||||||
|
#define JSONFILE_H
|
||||||
|
|
||||||
|
#include <json-c/json.h>
|
||||||
|
|
||||||
|
int existsFile(const char *fname);
|
||||||
|
int createFile(const char *fname);
|
||||||
|
int renameDataFile(const char *oldName, const char *newName);
|
||||||
|
|
||||||
|
json_object *loadJsonArray(const char *fname);
|
||||||
|
int saveJsonArray(const char *fname, json_object *arr);
|
||||||
|
|
||||||
|
json_object *findItemByName(json_object *arr, const char *name);
|
||||||
|
int removeItemByName(json_object *arr, const char *name);
|
||||||
|
int renameItemByName(json_object *arr, const char *oldName, const char *newName);
|
||||||
|
|
||||||
|
#endif
|
||||||
+35
-6
@@ -1,19 +1,48 @@
|
|||||||
#ifndef LOGIC_H
|
#ifndef LOGIC_H
|
||||||
#define LOGIC_H
|
#define LOGIC_H
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
#include "jsonFile.h"
|
||||||
|
|
||||||
|
#define PRODUCT_STOCK_UNLIMITED (-1)
|
||||||
|
|
||||||
struct product {
|
struct product {
|
||||||
float cost;
|
float cost;
|
||||||
int numberSold;
|
int numberSold;
|
||||||
char name;
|
char name;
|
||||||
};
|
};
|
||||||
|
|
||||||
int getCount();
|
/* Prodotti */
|
||||||
int existsFile(const char *fname);
|
json_object *getProducts(const char *fname);
|
||||||
void createFile(const char *fname);
|
void listProducts(const char *fname);
|
||||||
|
int addProduct(const char *fname, const char *name, double price);
|
||||||
|
int addProductWithInventory(const char *fname, const char *name, double price, int stock);
|
||||||
|
int addBundleProduct(const char *fname, const char *name, double price, json_object *components);
|
||||||
|
double getPrice(const char *fname, const char *name);
|
||||||
|
int updateProduct(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price);
|
||||||
|
int updateProductWithInventory(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price, int stock);
|
||||||
|
int getProductInventory(const char *fname, const char *name, int *stock, int *tracked);
|
||||||
|
int setProductInventory(const char *fname, const char *name, int stock, int tracked);
|
||||||
|
int consumeProductInventory(const char *fname, const char *name, int qty);
|
||||||
|
|
||||||
|
/* Copie vendute */
|
||||||
|
int getCount(const char *fname, const char *name);
|
||||||
|
void listSold(const char *fname);
|
||||||
|
int addSold(const char *fname, const char *name, int qty);
|
||||||
|
int updateSold(const char *fname, const char *name, int qty);
|
||||||
|
int deleteSold(const char *fname, const char *name);
|
||||||
|
int recordSale(const char *productsFile, const char *soldFile, const char *name, int qty);
|
||||||
|
|
||||||
|
/* Entrate */
|
||||||
|
double getTotalRevenue(const char *productsFile, const char *soldFile);
|
||||||
|
|
||||||
|
/* Config */
|
||||||
|
int ensureAppConfig(const char *fname);
|
||||||
|
int loadAppConfig(const char *fname);
|
||||||
|
int saveAppConfig(const char *fname);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
#ifndef WINDOWS_H
|
#ifndef WINDOWSBACK_H
|
||||||
#define WINDOWS_H
|
#define WINDOWSBACK_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
int runMainMenu();
|
WINDOW *create_newwin(int height, int width, int starty, int startx);
|
||||||
void runOptionsMenu();
|
void destroy_win(WINDOW *local_win);
|
||||||
|
|
||||||
|
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str);
|
||||||
|
int uiToggleSwitch(WINDOW *win, int y, int x, int state);
|
||||||
|
void uiTextInput(WINDOW *win, int y, int x, char *input, int max_len);
|
||||||
|
int uiSelectMenu(WINDOW *win, int start_y, int start_x, char **options, int n_options);
|
||||||
|
void print_menu(WINDOW *menu_win, int y, int x, int highlight);
|
||||||
|
|
||||||
void warningf(const char *fmt, ...);
|
void warningf(const char *fmt, ...);
|
||||||
void warning(char *string);
|
void warning(char *string);
|
||||||
void statusf(const char *fmt, ...);
|
void statusf(const char *fmt, ...);
|
||||||
void status(char *msg);
|
void status(char *msg);
|
||||||
WINDOW *create_newwin(int height, int width, int starty, int startx);
|
|
||||||
void destroy_win(WINDOW *local_win);
|
|
||||||
void print_menu(WINDOW *menu_win, int y, int x, int highlight);
|
|
||||||
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef WINDOWSDECLARATION_H
|
||||||
|
#define WINDOWSDECLARATION_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
int runMainMenu();
|
||||||
|
void runOptionsMenu();
|
||||||
|
void runSellWindow();
|
||||||
|
void runCreateBundleWindow();
|
||||||
|
void runAboutWindow();
|
||||||
|
void runViewSalesWindow();
|
||||||
|
void runViewProductsWindow();
|
||||||
|
void runAddProductWindow();
|
||||||
|
void example_window();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
CC = gcc
|
CC ?= gcc
|
||||||
CFLAGS = -I./include -Wall -Wextra -MMD -MP
|
PKG_CONFIG ?= pkg-config
|
||||||
LDLIBS = -lncurses
|
|
||||||
|
CFLAGS = -I./include -Wall -Wextra -MMD -MP $(shell $(PKG_CONFIG) --cflags json-c ncurses)
|
||||||
|
LDLIBS = $(shell $(PKG_CONFIG) --libs json-c ncurses)
|
||||||
|
|
||||||
|
BUNDLE_LDFLAGS = -Wl,--dynamic-linker=/lib64/ld-linux-x86-64.so.2 -Wl,-rpath,'$$ORIGIN/lib'
|
||||||
|
|
||||||
SRC_DIR = src
|
SRC_DIR = src
|
||||||
BUILD_DIR = build
|
BUILD_DIR = build
|
||||||
SOURCES = $(wildcard $(SRC_DIR)/*.c)
|
SOURCES = $(filter-out $(SRC_DIR)/testLogic.c, $(wildcard $(SRC_DIR)/*.c))
|
||||||
OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SOURCES))
|
OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SOURCES))
|
||||||
EXEC = productManager
|
EXEC = productManager
|
||||||
EXECTEST = test
|
|
||||||
|
|
||||||
DEPS = $(OBJECTS:.o=.d)
|
DEPS = $(OBJECTS:.o=.d)
|
||||||
|
|
||||||
all: $(EXEC)
|
all: $(EXEC)
|
||||||
|
|
||||||
|
# 1. Per testare in locale su NixOS
|
||||||
$(EXEC): $(OBJECTS)
|
$(EXEC): $(OBJECTS)
|
||||||
$(CC) $^ -o $@ $(LDLIBS)
|
$(CC) $^ -o $@ $(LDLIBS)
|
||||||
|
|
||||||
@@ -21,10 +25,17 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
|||||||
|
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
test:
|
# 2. Per creare il pacchetto portabile da inviare
|
||||||
$(CC) $(CFLAGS) src/logic.c src/testLogic.c -o $(EXECTEST)
|
bundle: $(OBJECTS)
|
||||||
|
@mkdir -p dist/lib
|
||||||
|
$(CC) $^ -o dist/$(EXEC) $(BUNDLE_LDFLAGS) $(LDLIBS)
|
||||||
|
@cp -L $$( $(PKG_CONFIG) --variable=libdir json-c )/libjson-c.so* dist/lib/
|
||||||
|
@cp -L $$( $(PKG_CONFIG) --variable=libdir ncurses )/libncurses*.so* dist/lib/
|
||||||
|
@tar czf productManager.tar.gz --directory dist/ .
|
||||||
|
@echo "=== Pacchetto portabile creato con successo in productManager.tar.gz ==="
|
||||||
|
@rm -fr dist/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR) $(EXEC)
|
rm -rf $(BUILD_DIR) $(EXEC) dist *.json
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean bundle
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
{ pkgs ? import <nixpkgs> {} }:
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
buildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.gcc
|
pkgs.gcc
|
||||||
pkgs.gnumake
|
pkgs.gnumake
|
||||||
pkgs.ncurses
|
pkgs.pkgconf
|
||||||
];
|
];
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.ncurses
|
||||||
|
pkgs.json_c
|
||||||
|
];
|
||||||
}
|
}
|
||||||
+4
-2
@@ -1,4 +1,6 @@
|
|||||||
#include "../include/globals.h"
|
#include "../include/globals.h"
|
||||||
|
|
||||||
char productPricesFileLocation[64]="./productPricesSettings.fpm";
|
char productPricesFileLocation[64]="./pm-item-price-default.json";
|
||||||
char countFileLocation[64]="./count.fpm";
|
char countFileLocation[64]="./pm-item-sold-count-default.json";
|
||||||
|
char configFileLocation[64]=".fpm";
|
||||||
|
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <json-c/json.h>
|
||||||
|
|
||||||
|
#include "../include/jsonFile.h"
|
||||||
|
|
||||||
|
int existsFile(const char *fname) {
|
||||||
|
FILE *file = fopen(fname, "r");
|
||||||
|
if (file) {
|
||||||
|
fclose(file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int createFile(const char *fname) {
|
||||||
|
json_object *arr = json_object_new_array();
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int res = json_object_to_file_ext(fname, arr, JSON_C_TO_STRING_PRETTY);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int renameDataFile(const char *oldName, const char *newName) {
|
||||||
|
return (rename(oldName, newName) == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_object *loadRawArray(const char *fname) {
|
||||||
|
if (!existsFile(fname) && createFile(fname) != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
FILE *f = fopen(fname, "r");
|
||||||
|
if (!f)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int empty = (fgetc(f) == EOF);
|
||||||
|
fclose(f);
|
||||||
|
if (empty)
|
||||||
|
return json_object_new_array();
|
||||||
|
|
||||||
|
json_object *arr = json_object_from_file(fname);
|
||||||
|
if (!arr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!json_object_is_type(arr, json_type_array)) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *loadJsonArray(const char *fname) {
|
||||||
|
return loadRawArray(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
int saveJsonArray(const char *fname, json_object *arr) {
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
return (json_object_to_file_ext(fname, arr, JSON_C_TO_STRING_PRETTY) == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *findItemByName(json_object *arr, const char *name) {
|
||||||
|
if (!arr || !name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t n = json_object_array_length(arr);
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
json_object *item = json_object_array_get_idx(arr, i);
|
||||||
|
json_object *jname = NULL;
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||||
|
strcmp(json_object_get_string(jname), name) == 0)
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int removeItemByName(json_object *arr, const char *name) {
|
||||||
|
if (!arr || !name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size_t n = json_object_array_length(arr);
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
json_object *item = json_object_array_get_idx(arr, i);
|
||||||
|
json_object *jname = NULL;
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||||
|
strcmp(json_object_get_string(jname), name) == 0) {
|
||||||
|
json_object_array_del_idx(arr, (int)i, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int renameItemByName(json_object *arr, const char *oldName, const char *newName) {
|
||||||
|
if (!arr || !oldName || !newName)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object *item = findItemByName(arr, oldName);
|
||||||
|
if (!item)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
json_object_object_add(item, "name", json_object_new_string(newName));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+785
-37
@@ -2,61 +2,809 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
#include <json-c/json.h>
|
||||||
|
|
||||||
#include "../include/logic.h"
|
#include "../include/logic.h"
|
||||||
#include "../include/globals.h"
|
#include "../include/globals.h"
|
||||||
|
#include "../include/jsonFile.h"
|
||||||
|
|
||||||
/*
|
static int isBundleItem(json_object *item);
|
||||||
* TODO: Listare prodotti con prezzo da file
|
|
||||||
* TODO: Aggiungere prodotto con prezzo a file
|
|
||||||
* TODO: Listare copie vendute di prodotto da file
|
|
||||||
* TODO: Aggiungere copie venduto di prodotto a file
|
|
||||||
* TODO: Cambiare nome dei file con interagisce
|
|
||||||
* TODO: Calcolare la somma delle entrate dati i file
|
|
||||||
*/
|
|
||||||
|
|
||||||
char *products;
|
static int readInventory(json_object *item, int *stock, int *tracked) {
|
||||||
|
json_object *jstock = NULL;
|
||||||
|
|
||||||
int existsFile(const char *fname) {
|
if (!item || !stock || !tracked)
|
||||||
FILE *file;
|
return -1;
|
||||||
if ((file = fopen(fname, "r")))
|
|
||||||
{
|
if (isBundleItem(item)) {
|
||||||
fclose(file);
|
*stock = PRODUCT_STOCK_UNLIMITED;
|
||||||
return 1;
|
*tracked = 0;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(item, "stock", &jstock) || !jstock) {
|
||||||
|
*stock = PRODUCT_STOCK_UNLIMITED;
|
||||||
|
*tracked = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*stock = json_object_get_int(jstock);
|
||||||
|
if (*stock < 0) {
|
||||||
|
*stock = PRODUCT_STOCK_UNLIMITED;
|
||||||
|
*tracked = 0;
|
||||||
|
} else {
|
||||||
|
*tracked = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void createFile(const char *fname) {
|
static void writeInventory(json_object *item, int stock, int tracked) {
|
||||||
FILE *file;
|
if (!item)
|
||||||
file = fopen(fname, "w");
|
return;
|
||||||
fclose(file);
|
|
||||||
|
json_object_object_add(item, "stock",
|
||||||
|
json_object_new_int(tracked ? stock : PRODUCT_STOCK_UNLIMITED));
|
||||||
}
|
}
|
||||||
|
|
||||||
char* getProducts(const char *fname) {
|
static int loadProductItem(const char *fname, const char *name, json_object **arr_out, json_object **item_out) {
|
||||||
/* FILE *file;
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
if((file=fopen(fname, "r"))==NULL) {
|
json_object *item = findItemByName(arr, name);
|
||||||
printf("Errore nell'apertura del file");
|
if (!item) {
|
||||||
exit(1);
|
json_object_put(arr);
|
||||||
}
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO QUESTO CODICE NON E' MIO, RICONTROLLARE FUNZIONAMENTO fread()
|
*arr_out = arr;
|
||||||
|
*item_out = item;
|
||||||
while(!feof(file)){
|
return 0;
|
||||||
n = fread(buffer, sizeof(char), sizeof(buffer), file);
|
|
||||||
for (i=0; i<n; i++)
|
|
||||||
if (buffer[i] == c)
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int getCount(){
|
static json_object *buildAppConfigObject(void) {
|
||||||
|
json_object *obj = json_object_new_object();
|
||||||
|
if (!obj)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
return 0;
|
json_object_object_add(obj, "productsFile", json_object_new_string(productPricesFileLocation));
|
||||||
|
json_object_object_add(obj, "salesFile", json_object_new_string(countFileLocation));
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int writeAppConfigObject(const char *fname, json_object *obj) {
|
||||||
|
if (!obj)
|
||||||
|
return -1;
|
||||||
|
return (json_object_to_file_ext(fname, obj, JSON_C_TO_STRING_PRETTY) == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int readConfigString(json_object *obj, const char *key, char *out, size_t outSize) {
|
||||||
|
json_object *v = NULL;
|
||||||
|
const char *s = NULL;
|
||||||
|
|
||||||
|
if (!obj || !key || !out || outSize == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(obj, key, &v) && v)
|
||||||
|
s = json_object_get_string(v);
|
||||||
|
if (!s || s[0] == '\0')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
snprintf(out, outSize, "%s", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isBundleItem(json_object *item) {
|
||||||
|
json_object *v = NULL;
|
||||||
|
return item && json_object_object_get_ex(item, "bundle", &v) && json_object_get_boolean(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getComponentQty(json_object *component) {
|
||||||
|
json_object *v = NULL;
|
||||||
|
|
||||||
|
if (!component)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(component, "qty", &v) || !v)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return json_object_get_int(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_object *getBundleComponents(json_object *bundleItem) {
|
||||||
|
json_object *components = NULL;
|
||||||
|
|
||||||
|
if (!bundleItem)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(bundleItem, "components", &components) || !components)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!json_object_is_type(components, json_type_array))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *bundleTagText(json_object *item) {
|
||||||
|
return isBundleItem(item) ? " [BUNDLE]" : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void formatProductLabel(json_object *item, char *buf, size_t size) {
|
||||||
|
json_object *v = NULL;
|
||||||
|
int tracked = 0;
|
||||||
|
int stock = PRODUCT_STOCK_UNLIMITED;
|
||||||
|
|
||||||
|
if (!buf || size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isBundleItem(item)) {
|
||||||
|
snprintf(buf, size, "X");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "stock", &v) && v) {
|
||||||
|
stock = json_object_get_int(v);
|
||||||
|
tracked = stock >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tracked)
|
||||||
|
snprintf(buf, size, "%d", stock);
|
||||||
|
else
|
||||||
|
snprintf(buf, size, "X");
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_object *findItemByNameExcluding(json_object *arr, const char *name, json_object *exclude) {
|
||||||
|
if (!arr || !name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t n = json_object_array_length(arr);
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
json_object *item = json_object_array_get_idx(arr, i);
|
||||||
|
json_object *jname = NULL;
|
||||||
|
|
||||||
|
if (item == exclude)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||||
|
strcmp(json_object_get_string(jname), name) == 0)
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int canConsumeTree(json_object *products, json_object *item, int qty) {
|
||||||
|
if (!products || !item || qty <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!isBundleItem(item)) {
|
||||||
|
json_object *jstock = NULL;
|
||||||
|
int stock = PRODUCT_STOCK_UNLIMITED;
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(item, "stock", &jstock) || !jstock)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
stock = json_object_get_int(jstock);
|
||||||
|
if (stock < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (stock >= qty) ? 0 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *components = getBundleComponents(item);
|
||||||
|
if (!components)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t count = json_object_array_length(components);
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
json_object *component = json_object_array_get_idx(components, i);
|
||||||
|
json_object *jname = NULL;
|
||||||
|
json_object *child = NULL;
|
||||||
|
int componentQty = getComponentQty(component);
|
||||||
|
|
||||||
|
if (componentQty <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(component, "name", &jname) || !jname)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
child = findItemByName(products, json_object_get_string(jname));
|
||||||
|
if (!child)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
int res = canConsumeTree(products, child, qty * componentQty);
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int consumeTree(json_object *products, json_object *item, int qty) {
|
||||||
|
if (!products || !item || qty <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!isBundleItem(item)) {
|
||||||
|
json_object *jstock = NULL;
|
||||||
|
int stock = PRODUCT_STOCK_UNLIMITED;
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(item, "stock", &jstock) || !jstock)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
stock = json_object_get_int(jstock);
|
||||||
|
if (stock < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
json_object_object_add(item, "stock", json_object_new_int(stock - qty));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *components = getBundleComponents(item);
|
||||||
|
if (!components)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t count = json_object_array_length(components);
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
json_object *component = json_object_array_get_idx(components, i);
|
||||||
|
json_object *jname = NULL;
|
||||||
|
json_object *child = NULL;
|
||||||
|
int componentQty = getComponentQty(component);
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(component, "name", &jname) || !jname)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
child = findItemByName(products, json_object_get_string(jname));
|
||||||
|
if (!child)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
int res = consumeTree(products, child, qty * componentQty);
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *loadFileText(const char *fname) {
|
||||||
|
FILE *f = fopen(fname, "rb");
|
||||||
|
long size;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (fseek(f, 0, SEEK_END) != 0) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = ftell(f);
|
||||||
|
if (size < 0) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rewind(f);
|
||||||
|
buf = malloc((size_t)size + 1);
|
||||||
|
if (!buf) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 0 && fread(buf, 1, (size_t)size, f) != (size_t)size) {
|
||||||
|
free(buf);
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[size] = '\0';
|
||||||
|
fclose(f);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int saveFileText(const char *fname, const char *text) {
|
||||||
|
FILE *f = fopen(fname, "wb");
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
len = strlen(text);
|
||||||
|
if (len > 0 && fwrite(text, 1, len, f) != len) {
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Formato dei file (JSON):
|
||||||
|
*
|
||||||
|
* File prodotti: [ {"name": "Maglietta S", "price": 19.90, "stock": 12}, ... ]
|
||||||
|
* stock = numero intero, oppure -1 per inventario infinito
|
||||||
|
* bundle = 1, components = [ {"name": "...", "qty": 2}, ... ]
|
||||||
|
* File vendite: [ {"name": "Maglietta S", "sold": 12}, ... ]
|
||||||
|
*
|
||||||
|
* Compilazione: aggiungere -ljson-c (e -lncurses) al linker.
|
||||||
|
*
|
||||||
|
* TODO: Aggiungere la possibilità di registrare gli acquisti non compiuti
|
||||||
|
* con intenzioni di acquisto.
|
||||||
|
* e.g. persona vuole acquistare maglietta S ma non c'è la taglia
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Prodotti */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ritorna l'array JSON dei prodotti (da liberare con json_object_put()),
|
||||||
|
* oppure NULL in caso di errore.
|
||||||
|
*/
|
||||||
|
json_object *getProducts(const char *fname) {
|
||||||
|
return loadJsonArray(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stampa a schermo (ncurses) la lista dei prodotti con il relativo prezzo. */
|
||||||
|
void listProducts(const char *fname) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr) {
|
||||||
|
printw("Errore nella lettura del file %s\n", fname);
|
||||||
|
refresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t n = json_object_array_length(arr);
|
||||||
|
if (n == 0)
|
||||||
|
printw("Nessun prodotto presente.\n");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
json_object *item = json_object_array_get_idx(arr, i);
|
||||||
|
json_object *jname, *jprice;
|
||||||
|
char stockBuf[16];
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||||
|
json_object_object_get_ex(item, "price", &jprice))
|
||||||
|
formatProductLabel(item, stockBuf, sizeof stockBuf);
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||||
|
json_object_object_get_ex(item, "price", &jprice))
|
||||||
|
printw("%zu) %-30s %8.2f [%s]%s\n", i + 1,
|
||||||
|
json_object_get_string(jname),
|
||||||
|
json_object_get_double(jprice),
|
||||||
|
stockBuf,
|
||||||
|
bundleTagText(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
json_object_put(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Aggiunge un prodotto con il suo prezzo.
|
||||||
|
* Ritorna 0 se ok, -1 se errore, 1 se il prodotto esiste già.
|
||||||
|
*/
|
||||||
|
int addProduct(const char *fname, const char *name, double price) {
|
||||||
|
return addProductWithInventory(fname, name, price, PRODUCT_STOCK_UNLIMITED);
|
||||||
|
}
|
||||||
|
|
||||||
|
int addProductWithInventory(const char *fname, const char *name, double price, int stock) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (findItemByName(arr, name)) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *item = json_object_new_object();
|
||||||
|
json_object_object_add(item, "name", json_object_new_string(name));
|
||||||
|
json_object_object_add(item, "price", json_object_new_double(price));
|
||||||
|
writeInventory(item, stock, stock != PRODUCT_STOCK_UNLIMITED);
|
||||||
|
json_object_array_add(arr, item);
|
||||||
|
|
||||||
|
int res = saveJsonArray(fname, arr);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int addBundleProduct(const char *fname, const char *name, double price, json_object *components) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (findItemByName(arr, name)) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!components || !json_object_is_type(components, json_type_array) || json_object_array_length(components) == 0) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *item = json_object_new_object();
|
||||||
|
json_object_object_add(item, "name", json_object_new_string(name));
|
||||||
|
json_object_object_add(item, "price", json_object_new_double(price));
|
||||||
|
json_object_object_add(item, "bundle", json_object_new_int(1));
|
||||||
|
json_object_object_add(item, "stock", json_object_new_int(PRODUCT_STOCK_UNLIMITED));
|
||||||
|
json_object_object_add(item, "components", components);
|
||||||
|
json_object_array_add(arr, item);
|
||||||
|
|
||||||
|
int res = saveJsonArray(fname, arr);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ritorna il prezzo di un prodotto, oppure -1.0 se non trovato/errore. */
|
||||||
|
double getPrice(const char *fname, const char *name) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1.0;
|
||||||
|
|
||||||
|
double price = -1.0;
|
||||||
|
json_object *item = findItemByName(arr, name);
|
||||||
|
json_object *jprice;
|
||||||
|
|
||||||
|
if (item && json_object_object_get_ex(item, "price", &jprice))
|
||||||
|
price = json_object_get_double(jprice);
|
||||||
|
|
||||||
|
json_object_put(arr);
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updateProduct(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price) {
|
||||||
|
json_object *products = NULL;
|
||||||
|
json_object *item = NULL;
|
||||||
|
int stock = PRODUCT_STOCK_UNLIMITED;
|
||||||
|
int tracked = 0;
|
||||||
|
|
||||||
|
if (loadProductItem(productsFile, oldName, &products, &item) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
readInventory(item, &stock, &tracked);
|
||||||
|
json_object_put(products);
|
||||||
|
|
||||||
|
return updateProductWithInventory(productsFile, soldFile, oldName, newName, price,
|
||||||
|
tracked ? stock : PRODUCT_STOCK_UNLIMITED);
|
||||||
|
}
|
||||||
|
|
||||||
|
int updateProductWithInventory(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price, int stock) {
|
||||||
|
json_object *products = loadJsonArray(productsFile);
|
||||||
|
if (!products)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object *item = findItemByName(products, oldName);
|
||||||
|
if (!item) {
|
||||||
|
json_object_put(products);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(oldName, newName) != 0 && findItemByNameExcluding(products, newName, item)) {
|
||||||
|
json_object_put(products);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_object_add(item, "name", json_object_new_string(newName));
|
||||||
|
json_object_object_add(item, "price", json_object_new_double(price));
|
||||||
|
writeInventory(item, stock, stock != PRODUCT_STOCK_UNLIMITED);
|
||||||
|
|
||||||
|
if (saveJsonArray(productsFile, products) != 0) {
|
||||||
|
json_object_put(products);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
json_object_put(products);
|
||||||
|
|
||||||
|
if (strcmp(oldName, newName) != 0) {
|
||||||
|
json_object *sales = loadJsonArray(soldFile);
|
||||||
|
if (!sales)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (renameItemByName(sales, oldName, newName) != 0) {
|
||||||
|
json_object_put(sales);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saveJsonArray(soldFile, sales) != 0) {
|
||||||
|
json_object_put(sales);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
json_object_put(sales);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getProductInventory(const char *fname, const char *name, int *stock, int *tracked) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object *item = findItemByName(arr, name);
|
||||||
|
if (!item) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = readInventory(item, stock, tracked);
|
||||||
|
json_object_put(arr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int setProductInventory(const char *fname, const char *name, int stock, int tracked) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object *item = findItemByName(arr, name);
|
||||||
|
if (!item) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeInventory(item, stock, tracked);
|
||||||
|
|
||||||
|
int res = saveJsonArray(fname, arr);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int consumeProductInventory(const char *fname, const char *name, int qty) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object *item = findItemByName(arr, name);
|
||||||
|
if (!item) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int check = canConsumeTree(arr, item, qty);
|
||||||
|
if (check != 0) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return check;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = consumeTree(arr, item, qty);
|
||||||
|
if (res != 0) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = saveJsonArray(fname, arr);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Copie vendute */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Ritorna le copie vendute di un prodotto (0 se non presente, -1 se errore). */
|
||||||
|
int getCount(const char *fname, const char *name) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
json_object *item = findItemByName(arr, name);
|
||||||
|
json_object *jsold;
|
||||||
|
|
||||||
|
if (item && json_object_object_get_ex(item, "sold", &jsold))
|
||||||
|
count = json_object_get_int(jsold);
|
||||||
|
|
||||||
|
json_object_put(arr);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stampa a schermo (ncurses) le copie vendute di ogni prodotto. */
|
||||||
|
void listSold(const char *fname) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr) {
|
||||||
|
printw("Errore nella lettura del file %s\n", fname);
|
||||||
|
refresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t n = json_object_array_length(arr);
|
||||||
|
if (n == 0)
|
||||||
|
printw("Nessuna vendita registrata.\n");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
json_object *item = json_object_array_get_idx(arr, i);
|
||||||
|
json_object *jname, *jsold;
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||||
|
json_object_object_get_ex(item, "sold", &jsold))
|
||||||
|
printw("%zu) %-30s %5d\n", i + 1,
|
||||||
|
json_object_get_string(jname),
|
||||||
|
json_object_get_int(jsold));
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
json_object_put(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Aggiunge "qty" copie vendute al prodotto indicato
|
||||||
|
* (crea la voce se non esiste ancora).
|
||||||
|
* Ritorna 0 se ok, -1 se errore.
|
||||||
|
*/
|
||||||
|
int addSold(const char *fname, const char *name, int qty) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object *item = findItemByName(arr, name);
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
json_object *jsold;
|
||||||
|
int current = 0;
|
||||||
|
|
||||||
|
if (json_object_object_get_ex(item, "sold", &jsold))
|
||||||
|
current = json_object_get_int(jsold);
|
||||||
|
|
||||||
|
json_object_object_add(item, "sold", json_object_new_int(current + qty));
|
||||||
|
} else {
|
||||||
|
item = json_object_new_object();
|
||||||
|
json_object_object_add(item, "name", json_object_new_string(name));
|
||||||
|
json_object_object_add(item, "sold", json_object_new_int(qty));
|
||||||
|
json_object_array_add(arr, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = saveJsonArray(fname, arr);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int recordSale(const char *productsFile, const char *soldFile, const char *name, int qty) {
|
||||||
|
char *backup = NULL;
|
||||||
|
int consumeRes;
|
||||||
|
|
||||||
|
if (qty <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!existsFile(productsFile) && createFile(productsFile) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
backup = loadFileText(productsFile);
|
||||||
|
if (!backup)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
consumeRes = consumeProductInventory(productsFile, name, qty);
|
||||||
|
if (consumeRes != 0) {
|
||||||
|
free(backup);
|
||||||
|
return consumeRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addSold(soldFile, name, qty) != 0) {
|
||||||
|
saveFileText(productsFile, backup);
|
||||||
|
free(backup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(backup);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int updateSold(const char *fname, const char *name, int qty) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object *item = findItemByName(arr, name);
|
||||||
|
if (!item) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_object_add(item, "sold", json_object_new_int(qty));
|
||||||
|
|
||||||
|
int res = saveJsonArray(fname, arr);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int deleteSold(const char *fname, const char *name) {
|
||||||
|
json_object *arr = loadJsonArray(fname);
|
||||||
|
if (!arr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int res = removeItemByName(arr, name);
|
||||||
|
if (res != 0) {
|
||||||
|
json_object_put(arr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = saveJsonArray(fname, arr);
|
||||||
|
json_object_put(arr);
|
||||||
|
return (res == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Entrate */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calcola le entrate totali: somma di (prezzo * copie vendute)
|
||||||
|
* per ogni prodotto. Ritorna -1.0 in caso di errore.
|
||||||
|
*/
|
||||||
|
double getTotalRevenue(const char *productsFile, const char *soldFile) {
|
||||||
|
json_object *products = loadJsonArray(productsFile);
|
||||||
|
if (!products)
|
||||||
|
return -1.0;
|
||||||
|
|
||||||
|
json_object *sales = loadJsonArray(soldFile);
|
||||||
|
if (!sales) {
|
||||||
|
json_object_put(products);
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double total = 0.0;
|
||||||
|
size_t n = json_object_array_length(sales);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
json_object *sale = json_object_array_get_idx(sales, i);
|
||||||
|
json_object *jname, *jsold, *jprice;
|
||||||
|
|
||||||
|
if (!json_object_object_get_ex(sale, "name", &jname) ||
|
||||||
|
!json_object_object_get_ex(sale, "sold", &jsold))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
json_object *prod = findItemByName(products, json_object_get_string(jname));
|
||||||
|
if (prod && json_object_object_get_ex(prod, "price", &jprice))
|
||||||
|
total += json_object_get_double(jprice) * json_object_get_int(jsold);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_put(products);
|
||||||
|
json_object_put(sales);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
int saveAppConfig(const char *fname) {
|
||||||
|
json_object *obj = buildAppConfigObject();
|
||||||
|
if (!obj)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int res = writeAppConfigObject(fname, obj);
|
||||||
|
json_object_put(obj);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ensureAppConfig(const char *fname) {
|
||||||
|
if (!existsFile(fname))
|
||||||
|
return saveAppConfig(fname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int loadAppConfig(const char *fname) {
|
||||||
|
json_object *obj = NULL;
|
||||||
|
json_object *loaded = NULL;
|
||||||
|
|
||||||
|
if (ensureAppConfig(fname) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
loaded = json_object_from_file(fname);
|
||||||
|
if (!loaded || !json_object_is_type(loaded, json_type_object)) {
|
||||||
|
if (loaded)
|
||||||
|
json_object_put(loaded);
|
||||||
|
return saveAppConfig(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
readConfigString(loaded, "productsFile", productPricesFileLocation, sizeof productPricesFileLocation);
|
||||||
|
readConfigString(loaded, "salesFile", countFileLocation, sizeof countFileLocation);
|
||||||
|
|
||||||
|
obj = buildAppConfigObject();
|
||||||
|
if (!obj) {
|
||||||
|
json_object_put(loaded);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
json_object_put(obj);
|
||||||
|
json_object_put(loaded);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+8
-22
@@ -19,8 +19,10 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
#include <windows.h>
|
#include "../include/windowsDeclaration.h"
|
||||||
#include <logic.h>
|
#include "../include/windowsBack.h"
|
||||||
|
#include "../include/logic.h"
|
||||||
|
#include "../include/globals.h"
|
||||||
|
|
||||||
void printLogoBG();
|
void printLogoBG();
|
||||||
|
|
||||||
@@ -42,31 +44,15 @@ int main(){
|
|||||||
noecho();
|
noecho();
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
raw();
|
raw();
|
||||||
|
if (loadAppConfig(configFileLocation) != 0)
|
||||||
|
warningf("Impossibile caricare la config '%s', uso i valori di default.", configFileLocation);
|
||||||
printLogoBG();
|
printLogoBG();
|
||||||
statusf(" ");
|
statusf(" ");
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
|
while (runMainMenu()) {
|
||||||
int running = 0;
|
printLogoBG();
|
||||||
while(running != 6){
|
|
||||||
refresh();
|
refresh();
|
||||||
running = runMainMenu();
|
|
||||||
switch(running) {
|
|
||||||
case 1:
|
|
||||||
warning("Questa e' una stringa molto lunga . . . Ma anche un test, che ti devo dire, ci proviamo a fare cose che non sono mai state fatte, oltretutto che divertimento ci sarebbe senza esperienze nuove?");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
runOptionsMenu();
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endwin();
|
endwin();
|
||||||
|
|||||||
+73
-7
@@ -6,12 +6,78 @@
|
|||||||
#include "../include/logic.h"
|
#include "../include/logic.h"
|
||||||
#include "../include/globals.h"
|
#include "../include/globals.h"
|
||||||
|
|
||||||
int main() {
|
#define PRODUCTS_FILE "test_products.fpm"
|
||||||
/* Working
|
#define SOLD_FILE "test_sold.fpm"
|
||||||
printf("%d", existsFile("qwestoeuntest"));
|
#define RENAMED_FILE "test_renamed.fpm"
|
||||||
createFile("qwestoeuntest");
|
|
||||||
printf("%d", existsFile("qwestoeuntest"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
return 0;
|
static int passed = 0;
|
||||||
|
static int failed = 0;
|
||||||
|
|
||||||
|
#define CHECK(cond, desc) do { \
|
||||||
|
if (cond) { passed++; printf(" [ OK ] %s\n", desc); } \
|
||||||
|
else { failed++; printf(" [FAIL] %s (riga %d)\n", desc, __LINE__); } \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static int near(double a, double b) {
|
||||||
|
return (a - b) < 1e-9 && (b - a) < 1e-9;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup(void) {
|
||||||
|
remove(PRODUCTS_FILE);
|
||||||
|
remove(SOLD_FILE);
|
||||||
|
remove(RENAMED_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
printf("== File ==\n");
|
||||||
|
CHECK(existsFile(PRODUCTS_FILE) == 0, "il file non esiste all'inizio");
|
||||||
|
CHECK(createFile(PRODUCTS_FILE) == 0, "createFile ritorna 0");
|
||||||
|
CHECK(existsFile(PRODUCTS_FILE) == 1, "il file esiste dopo createFile");
|
||||||
|
|
||||||
|
/* File completamente vuoto (non JSON): deve essere trattato come array vuoto */
|
||||||
|
FILE *f = fopen(SOLD_FILE, "w");
|
||||||
|
if (f) fclose(f);
|
||||||
|
CHECK(getCount(SOLD_FILE, "Qualsiasi") == 0, "file vuoto trattato come lista vuota");
|
||||||
|
|
||||||
|
printf("== Prodotti ==\n");
|
||||||
|
CHECK(addProduct(PRODUCTS_FILE, "Maglietta S", 20.00) == 0, "aggiunta Maglietta S");
|
||||||
|
CHECK(addProduct(PRODUCTS_FILE, "Pantaloni", 35.50) == 0, "aggiunta Pantaloni");
|
||||||
|
CHECK(addProduct(PRODUCTS_FILE, "Maglietta S", 99.99) == 1, "duplicato rifiutato (ritorna 1)");
|
||||||
|
CHECK(near(getPrice(PRODUCTS_FILE, "Maglietta S"), 20.00), "prezzo Maglietta S = 20.00");
|
||||||
|
CHECK(near(getPrice(PRODUCTS_FILE, "Pantaloni"), 35.50), "prezzo Pantaloni = 35.50");
|
||||||
|
CHECK(near(getPrice(PRODUCTS_FILE, "Inesistente"), -1.0), "prodotto inesistente -> -1.0");
|
||||||
|
|
||||||
|
json_object *arr = getProducts(PRODUCTS_FILE);
|
||||||
|
CHECK(arr != NULL && json_object_array_length(arr) == 2, "getProducts ritorna 2 prodotti");
|
||||||
|
if (arr) json_object_put(arr);
|
||||||
|
|
||||||
|
printf("== Copie vendute ==\n");
|
||||||
|
CHECK(getCount(SOLD_FILE, "Maglietta S") == 0, "nessuna vendita all'inizio");
|
||||||
|
CHECK(addSold(SOLD_FILE, "Maglietta S", 2) == 0, "addSold Maglietta S +2");
|
||||||
|
CHECK(addSold(SOLD_FILE, "Maglietta S", 1) == 0, "addSold Maglietta S +1");
|
||||||
|
CHECK(addSold(SOLD_FILE, "Pantaloni", 2) == 0, "addSold Pantaloni +2");
|
||||||
|
CHECK(getCount(SOLD_FILE, "Maglietta S") == 3, "Maglietta S: 3 copie");
|
||||||
|
CHECK(getCount(SOLD_FILE, "Pantaloni") == 2, "Pantaloni: 2 copie");
|
||||||
|
CHECK(getCount(SOLD_FILE, "Inesistente") == 0, "prodotto mai venduto: 0 copie");
|
||||||
|
|
||||||
|
printf("== Entrate ==\n");
|
||||||
|
/* 3 * 20.00 + 2 * 35.50 = 131.00 */
|
||||||
|
CHECK(near(getTotalRevenue(PRODUCTS_FILE, SOLD_FILE), 131.00), "entrate totali = 131.00");
|
||||||
|
|
||||||
|
/* Una vendita di un prodotto non presente nel listino non deve contare */
|
||||||
|
addSold(SOLD_FILE, "Fantasma", 10);
|
||||||
|
CHECK(near(getTotalRevenue(PRODUCTS_FILE, SOLD_FILE), 131.00), "vendite senza prezzo ignorate");
|
||||||
|
|
||||||
|
printf("== Rinomina ==\n");
|
||||||
|
CHECK(renameDataFile(PRODUCTS_FILE, RENAMED_FILE) == 0, "renameDataFile ritorna 0");
|
||||||
|
CHECK(existsFile(PRODUCTS_FILE) == 0 && existsFile(RENAMED_FILE) == 1, "vecchio nome sparito, nuovo presente");
|
||||||
|
CHECK(near(getPrice(RENAMED_FILE, "Pantaloni"), 35.50), "i dati sopravvivono alla rinomina");
|
||||||
|
CHECK(renameDataFile("non_esiste.fpm", "altro.fpm") == -1, "rinomina di file inesistente fallisce");
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
printf("\nRisultato: %d ok, %d falliti\n", passed, failed);
|
||||||
|
return failed == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
-276
@@ -1,276 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ncurses.h>
|
|
||||||
|
|
||||||
#include "../include/windows.h"
|
|
||||||
#include "../include/logic.h"
|
|
||||||
#include "../include/globals.h"
|
|
||||||
#define MAX_LEN 64
|
|
||||||
|
|
||||||
char *mainMenu[] = {
|
|
||||||
"Segna vendita",
|
|
||||||
"Vedi vendite",
|
|
||||||
"Vedi prodotti",
|
|
||||||
"Gestisci prodotti",
|
|
||||||
"Opzioni",
|
|
||||||
"Exit"
|
|
||||||
};
|
|
||||||
int n_mainMenu = sizeof(mainMenu) / sizeof(char *);
|
|
||||||
|
|
||||||
|
|
||||||
int runMainMenu(){
|
|
||||||
WINDOW *mainWin;
|
|
||||||
int startx, starty, width, height;
|
|
||||||
height = LINES / 2;
|
|
||||||
width = COLS / 2;
|
|
||||||
starty = 0;
|
|
||||||
startx = 0;
|
|
||||||
mainWin = create_newwin(height, width, starty, startx);
|
|
||||||
|
|
||||||
int highlight = 1;
|
|
||||||
int choice = 0;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
keypad(mainWin, TRUE);
|
|
||||||
wwrap_print(mainWin, 1, 2, "Usa le FRECCETTE ed INVIO per scegliere un'opzione,\nin alternativa ci sono i movement di vim e SPAZIO.");
|
|
||||||
refresh();
|
|
||||||
print_menu(mainWin, height / 2 - 3, width / 2 - 9, highlight);
|
|
||||||
while(1)
|
|
||||||
{ c = wgetch(mainWin);
|
|
||||||
switch(c)
|
|
||||||
{ case KEY_UP:
|
|
||||||
case KEY_LEFT:
|
|
||||||
case 'h':
|
|
||||||
case 'k':
|
|
||||||
if(highlight == 1)
|
|
||||||
highlight = n_mainMenu;
|
|
||||||
else
|
|
||||||
--highlight;
|
|
||||||
break;
|
|
||||||
case KEY_DOWN:
|
|
||||||
case KEY_RIGHT:
|
|
||||||
case 'l':
|
|
||||||
case 'j':
|
|
||||||
if(highlight == n_mainMenu)
|
|
||||||
highlight = 1;
|
|
||||||
else
|
|
||||||
++highlight;
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
case 32:
|
|
||||||
choice = highlight;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
statusf("Il tasto '%c' non e' un tasto valido", c);
|
|
||||||
refresh();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
print_menu(mainWin, height / 2 - 3, width / 2 - 9, highlight);
|
|
||||||
if(choice != 0) /* User did a choice come out of the infinite loop */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
statusf("Avvio opzione '%s'", mainMenu[choice - 1]);
|
|
||||||
clrtoeol();
|
|
||||||
refresh();
|
|
||||||
destroy_win(mainWin);
|
|
||||||
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|
||||||
void runOptionsMenu(){
|
|
||||||
WINDOW *optionsWin;
|
|
||||||
int startx, starty, width, height;
|
|
||||||
height = LINES / 2;
|
|
||||||
width = COLS / 2;
|
|
||||||
starty = 0;
|
|
||||||
startx = COLS / 2;
|
|
||||||
optionsWin = create_newwin(height, width, starty, startx);
|
|
||||||
|
|
||||||
char input[MAX_LEN + 1];
|
|
||||||
strcpy(input, productPricesFileLocation);
|
|
||||||
int ch, pos = strlen(input);
|
|
||||||
|
|
||||||
mvwprintw(optionsWin, 1, 2, "Posizione file prodotti:");
|
|
||||||
wattron(optionsWin, COLOR_PAIR(2));
|
|
||||||
for (int i = 0; i < MAX_LEN; i++) {
|
|
||||||
mvwaddch(optionsWin, 2, 2 + i, ' ');
|
|
||||||
}
|
|
||||||
mvwprintw(optionsWin, 2, 2, "%s", input);
|
|
||||||
wmove(optionsWin, 2, 2 + strlen(input));
|
|
||||||
wrefresh(optionsWin);
|
|
||||||
|
|
||||||
pos = strlen(input);
|
|
||||||
|
|
||||||
// Input nella finestra
|
|
||||||
curs_set(1);
|
|
||||||
while ((ch = wgetch(optionsWin)) != '\n') {
|
|
||||||
if (ch == KEY_BACKSPACE || ch == 127 || ch == '\b') {
|
|
||||||
if (pos > 0) {
|
|
||||||
pos--;
|
|
||||||
input[pos] = '\0';
|
|
||||||
mvwaddch(optionsWin, 2, 2 + pos, ' ');
|
|
||||||
wmove(optionsWin, 2, 2 + pos);
|
|
||||||
}
|
|
||||||
} else if (pos < MAX_LEN && ch >= 32 && ch <= 126) {
|
|
||||||
input[pos++] = ch;
|
|
||||||
input[pos] = '\0';
|
|
||||||
mvwaddch(optionsWin, 2, 2 + pos - 1, ch);
|
|
||||||
}
|
|
||||||
wrefresh(optionsWin);
|
|
||||||
}
|
|
||||||
curs_set(0);
|
|
||||||
wattroff(optionsWin, COLOR_PAIR(2));
|
|
||||||
|
|
||||||
if (exists(input)) {
|
|
||||||
statusf("Il file '%s' esiste e verra' utilizzato al posto di '%s'", input, productPricesFileLocation);
|
|
||||||
strcpy(productPricesFileLocation, input);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
warningf("Il file '%s' non esiste, utilizzo il vecchio file '%s'", input, productPricesFileLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
werase(optionsWin);
|
|
||||||
destroy_win(optionsWin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void warningf(const char *fmt, ...) {
|
|
||||||
char buf[256];
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
warning(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void warning(char *string){
|
|
||||||
WINDOW *warningWin;
|
|
||||||
int startx, starty, width, height;
|
|
||||||
height = LINES / 2 - 2;
|
|
||||||
width = COLS / 2;
|
|
||||||
starty = LINES / 2;
|
|
||||||
startx = 0;
|
|
||||||
warningWin = create_newwin(height, width, starty, startx);
|
|
||||||
wbkgd(warningWin, COLOR_PAIR(1));
|
|
||||||
|
|
||||||
mvwprintw(warningWin, 2, 2, "ATTENZIONE!!! E' avvenuto un errore! ");
|
|
||||||
wwrap_print(warningWin, 4, 3, string);
|
|
||||||
mvwprintw(warningWin, height - 1, 3, " Premi un tasto per continuare . . . ");
|
|
||||||
|
|
||||||
wrefresh(warningWin);
|
|
||||||
getch();
|
|
||||||
werase(warningWin);
|
|
||||||
destroy_win(warningWin);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void statusf(const char *fmt, ...) {
|
|
||||||
char buf[256];
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
status(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void status(char *msg) {
|
|
||||||
attron(COLOR_PAIR(3));
|
|
||||||
mvprintw(LINES -2, 0, "%-*s", COLS, msg);
|
|
||||||
attroff(COLOR_PAIR(3));
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WINDOW *create_newwin(int height, int width, int starty, int startx)
|
|
||||||
{ WINDOW *local_win;
|
|
||||||
|
|
||||||
local_win = newwin(height, width, starty, startx);
|
|
||||||
box(local_win, 0 , 0);
|
|
||||||
wrefresh(local_win);
|
|
||||||
|
|
||||||
return local_win;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_win(WINDOW *local_win)
|
|
||||||
{
|
|
||||||
wborder(local_win, ':', ':', '.', '.', '.', '.', '.', '.');
|
|
||||||
wrefresh(local_win);
|
|
||||||
delwin(local_win);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_menu(WINDOW *menu_win, int y, int x, int highlight)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
box(menu_win, 0, 0);
|
|
||||||
for(i = 0; i < n_mainMenu; ++i)
|
|
||||||
{ if(highlight == i + 1) /* High light the present choice */
|
|
||||||
{ wattron(menu_win, A_REVERSE);
|
|
||||||
mvwprintw(menu_win, y, x, "%s", mainMenu[i]);
|
|
||||||
wattroff(menu_win, A_REVERSE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mvwprintw(menu_win, y, x, "%s", mainMenu[i]);
|
|
||||||
++y;
|
|
||||||
}
|
|
||||||
wrefresh(menu_win);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str) {
|
|
||||||
int max_width = getmaxx(win) - (start_y + 1);
|
|
||||||
int y = start_y;
|
|
||||||
int x = start_x;
|
|
||||||
int line_length = 0;
|
|
||||||
int word_length = 0;
|
|
||||||
const char *word_start = str;
|
|
||||||
|
|
||||||
while (*str) {
|
|
||||||
// Trova la prossima parola
|
|
||||||
if (*str != ' ' && *str != '\n' && *str != '\t') {
|
|
||||||
str++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calcola lunghezza parola corrente
|
|
||||||
word_length = str - word_start;
|
|
||||||
|
|
||||||
// Gestione wrap
|
|
||||||
if (line_length + word_length > max_width) {
|
|
||||||
y++;
|
|
||||||
x = start_x;
|
|
||||||
line_length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stampa parola
|
|
||||||
for (int i = 0; i < word_length; i++) {
|
|
||||||
mvwaddch(win, y, x++, word_start[i]);
|
|
||||||
}
|
|
||||||
line_length += word_length;
|
|
||||||
|
|
||||||
// Gestione spazi
|
|
||||||
if (*str == ' ' || *str == '\t') {
|
|
||||||
if (line_length < max_width) {
|
|
||||||
mvwaddch(win, y, x++, ' ');
|
|
||||||
line_length++;
|
|
||||||
} else {
|
|
||||||
y++;
|
|
||||||
x = start_x;
|
|
||||||
line_length = 0;
|
|
||||||
}
|
|
||||||
} else if (*str == '\n') {
|
|
||||||
y++;
|
|
||||||
x = start_x;
|
|
||||||
line_length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
word_start = ++str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stampa ultima parola
|
|
||||||
word_length = strlen(word_start);
|
|
||||||
if (line_length + word_length > max_width) {
|
|
||||||
y++;
|
|
||||||
x = start_x;
|
|
||||||
}
|
|
||||||
mvwprintw(win, y, x, "%s", word_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
#include "../include/windowsBack.h"
|
||||||
|
#include "../include/logic.h"
|
||||||
|
#include "../include/globals.h"
|
||||||
|
#define MAX_LEN 64
|
||||||
|
|
||||||
|
// Scrivi in una finestra di warning
|
||||||
|
void warningf(const char *fmt, ...) {
|
||||||
|
char buf[256];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
warning(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dichiara una finestra di warning, avviato da warningf
|
||||||
|
void warning(char *string){
|
||||||
|
WINDOW *warningWin;
|
||||||
|
int startx, starty, width, height;
|
||||||
|
height = LINES / 2 - 2;
|
||||||
|
width = COLS / 2;
|
||||||
|
starty = LINES / 2;
|
||||||
|
startx = 0;
|
||||||
|
warningWin = create_newwin(height, width, starty, startx);
|
||||||
|
wbkgd(warningWin, COLOR_PAIR(1));
|
||||||
|
|
||||||
|
mvwprintw(warningWin, 2, 2, "ATTENZIONE!!! E' avvenuto un errore! ");
|
||||||
|
wwrap_print(warningWin, 4, 3, string);
|
||||||
|
mvwprintw(warningWin, height - 1, 3, " Premi un tasto per continuare . . . ");
|
||||||
|
|
||||||
|
wrefresh(warningWin);
|
||||||
|
getch();
|
||||||
|
werase(warningWin);
|
||||||
|
destroy_win(warningWin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrivi nella barra di status
|
||||||
|
void statusf(const char *fmt, ...) {
|
||||||
|
char buf[256];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
status(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Posiziona il testo indicato in statusf nella barra di status
|
||||||
|
void status(char *msg) {
|
||||||
|
attron(COLOR_PAIR(3));
|
||||||
|
mvprintw(LINES -2, 0, "%-*s", COLS, msg);
|
||||||
|
attroff(COLOR_PAIR(3));
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
WINDOW *create_newwin(int height, int width, int starty, int startx)
|
||||||
|
{ WINDOW *local_win;
|
||||||
|
|
||||||
|
local_win = newwin(height, width, starty, startx);
|
||||||
|
box(local_win, 0 , 0);
|
||||||
|
wrefresh(local_win);
|
||||||
|
|
||||||
|
return local_win;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_win(WINDOW *local_win)
|
||||||
|
{
|
||||||
|
//wborder(local_win, ':', ':', '.', '.', '.', '.', '.', '.');
|
||||||
|
wrefresh(local_win);
|
||||||
|
delwin(local_win);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrive in una finestra facendo il wrapping a parola del testo in modo che non esca dalla finestra
|
||||||
|
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str) {
|
||||||
|
int max_width = getmaxx(win) - (start_y + 1);
|
||||||
|
int y = start_y;
|
||||||
|
int x = start_x;
|
||||||
|
int line_length = 0;
|
||||||
|
int word_length = 0;
|
||||||
|
const char *word_start = str;
|
||||||
|
|
||||||
|
while (*str) {
|
||||||
|
// Trova la prossima parola
|
||||||
|
if (*str != ' ' && *str != '\n' && *str != '\t') {
|
||||||
|
str++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcola lunghezza parola corrente
|
||||||
|
word_length = str - word_start;
|
||||||
|
|
||||||
|
// Gestione wrap
|
||||||
|
if (line_length + word_length > max_width) {
|
||||||
|
y++;
|
||||||
|
x = start_x;
|
||||||
|
line_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stampa parola
|
||||||
|
for (int i = 0; i < word_length; i++) {
|
||||||
|
mvwaddch(win, y, x++, word_start[i]);
|
||||||
|
}
|
||||||
|
line_length += word_length;
|
||||||
|
|
||||||
|
// Gestione spazi
|
||||||
|
if (*str == ' ' || *str == '\t') {
|
||||||
|
if (line_length < max_width) {
|
||||||
|
mvwaddch(win, y, x++, ' ');
|
||||||
|
line_length++;
|
||||||
|
} else {
|
||||||
|
y++;
|
||||||
|
x = start_x;
|
||||||
|
line_length = 0;
|
||||||
|
}
|
||||||
|
} else if (*str == '\n') {
|
||||||
|
y++;
|
||||||
|
x = start_x;
|
||||||
|
line_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
word_start = ++str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stampa ultima parola
|
||||||
|
word_length = strlen(word_start);
|
||||||
|
if (line_length + word_length > max_width) {
|
||||||
|
y++;
|
||||||
|
x = start_x;
|
||||||
|
}
|
||||||
|
mvwprintw(win, y, x, "%s", word_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elemento di UI; Menu a scelta multipla
|
||||||
|
int uiSelectMenu(WINDOW *win, int start_y, int start_x, char **options, int n_options) {
|
||||||
|
int highlight = 1, choice = 0, c;
|
||||||
|
keypad(win, TRUE);
|
||||||
|
while(1) {
|
||||||
|
for(int i = 0; i < n_options; ++i) {
|
||||||
|
if(highlight == i + 1) wattron(win, A_REVERSE);
|
||||||
|
mvwprintw(win, start_y + i, start_x, "%s", options[i]);
|
||||||
|
if(highlight == i + 1) wattroff(win, A_REVERSE);
|
||||||
|
}
|
||||||
|
wrefresh(win);
|
||||||
|
c = wgetch(win);
|
||||||
|
switch(c) {
|
||||||
|
case KEY_UP: case 'k': highlight = (highlight == 1) ? n_options : highlight - 1; break;
|
||||||
|
case KEY_DOWN: case 'j': highlight = (highlight == n_options) ? 1 : highlight + 1; break;
|
||||||
|
case 10: case 32: choice = highlight; break;
|
||||||
|
}
|
||||||
|
if(choice != 0) break;
|
||||||
|
}
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elemento di UI; Campo di inserimento del testo
|
||||||
|
void uiTextInput(WINDOW *win, int y, int x, char *input, int max_len) {
|
||||||
|
int ch, pos = strlen(input);
|
||||||
|
wattron(win, COLOR_PAIR(2));
|
||||||
|
curs_set(1);
|
||||||
|
for(int i = 0; i < max_len; i++) mvwaddch(win, y, x + i, ' ');
|
||||||
|
mvwprintw(win, y, x, "%s", input);
|
||||||
|
wmove(win, y, x + pos);
|
||||||
|
wrefresh(win);
|
||||||
|
while ((ch = wgetch(win)) != '\n') {
|
||||||
|
if (ch == KEY_BACKSPACE || ch == 127 || ch == '\b') {
|
||||||
|
if (pos > 0) {
|
||||||
|
pos--;
|
||||||
|
input[pos] = '\0';
|
||||||
|
mvwaddch(win, y, x + pos, ' ');
|
||||||
|
wmove(win, y, x + pos);
|
||||||
|
}
|
||||||
|
} else if (pos < max_len && ch >= 32 && ch <= 126) {
|
||||||
|
input[pos++] = ch;
|
||||||
|
input[pos] = '\0';
|
||||||
|
mvwaddch(win, y, x + pos - 1, ch);
|
||||||
|
}
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
curs_set(0);
|
||||||
|
wattroff(win, COLOR_PAIR(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elemento di UI; Tasto a 2 stati (ON/OFF)
|
||||||
|
int uiToggleSwitch(WINDOW *win, int y, int x, int state) {
|
||||||
|
int c;
|
||||||
|
keypad(win, TRUE);
|
||||||
|
while(1) {
|
||||||
|
wattron(win, A_REVERSE);
|
||||||
|
mvwprintw(win, y, x, state ? "[ ON ] " : "[ OFF ]");
|
||||||
|
wattroff(win, A_REVERSE);
|
||||||
|
wrefresh(win);
|
||||||
|
c = wgetch(win);
|
||||||
|
if (c == 10 || c == 32) break;
|
||||||
|
if (c == KEY_LEFT || c == KEY_RIGHT || c == 'h' || c == 'j' || c == 'k' || c == 'l') state = !state;
|
||||||
|
}
|
||||||
|
mvwprintw(win, y, x, state ? "[ ON ] " : "[ OFF ]");
|
||||||
|
wrefresh(win);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user