Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,11 @@
|
||||
# Product Manager
|
||||
Un gestore di inventario e vendite minimale
|
||||
|
||||
## Scaricarlo
|
||||
Disponibile solo per linux.
|
||||
Scarica l'ultima versione da [qui](https://git.lug.uniroma2.it/rexeref/productManager/releases)!
|
||||
Avvia l'eseguibile da un terminale.
|
||||
Se non si avvia rendilo prima un eseguibile con chmod +x
|
||||
|
||||
|
||||
***questo codice molto probabilmente NON è sicuro, utilizzarlo a propria indiscrezione***
|
||||
@@ -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,6 @@
|
||||
#include <string.h>
|
||||
|
||||
extern char productPricesFileLocation[64]; // Dichiarazione extern
|
||||
extern char countFileLocation[64];
|
||||
|
||||
#endif
|
||||
+19
-2
@@ -1,10 +1,12 @@
|
||||
#ifndef LOGIC_H
|
||||
#define LOGIC_H
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ncurses.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
struct product {
|
||||
float cost;
|
||||
@@ -12,8 +14,23 @@ struct product {
|
||||
char name;
|
||||
};
|
||||
|
||||
int getCount();
|
||||
/* File */
|
||||
int existsFile(const char *fname);
|
||||
void createFile(const char *fname);
|
||||
int createFile(const char *fname);
|
||||
int renameDataFile(const char *oldName, const char *newName);
|
||||
|
||||
/* Prodotti */
|
||||
json_object *getProducts(const char *fname);
|
||||
void listProducts(const char *fname);
|
||||
int addProduct(const char *fname, const char *name, double price);
|
||||
double getPrice(const char *fname, const char *name);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Entrate */
|
||||
double getTotalRevenue(const char *productsFile, const char *soldFile);
|
||||
|
||||
#endif
|
||||
@@ -1,20 +1,23 @@
|
||||
#ifndef WINDOWS_H
|
||||
#define WINDOWS_H
|
||||
#ifndef WINDOWSBACK_H
|
||||
#define WINDOWSBACK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
int runMainMenu();
|
||||
void runOptionsMenu();
|
||||
WINDOW *create_newwin(int height, int width, int starty, int startx);
|
||||
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 warning(char *string);
|
||||
void statusf(const char *fmt, ...);
|
||||
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
|
||||
@@ -0,0 +1,18 @@
|
||||
#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 runAboutWindow();
|
||||
void runViewSalesWindow();
|
||||
void runViewProductsWindow();
|
||||
void runAddProductWindow();
|
||||
void example_window();
|
||||
|
||||
#endif
|
||||
@@ -1,17 +1,21 @@
|
||||
CC = gcc
|
||||
CFLAGS = -I./include -Wall -Wextra -MMD -MP
|
||||
LDLIBS = -lncurses
|
||||
CC ?= gcc
|
||||
PKG_CONFIG ?= pkg-config
|
||||
|
||||
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
|
||||
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))
|
||||
EXEC = productManager
|
||||
EXECTEST = test
|
||||
|
||||
DEPS = $(OBJECTS:.o=.d)
|
||||
|
||||
all: $(EXEC)
|
||||
|
||||
# 1. Per testare in locale su NixOS
|
||||
$(EXEC): $(OBJECTS)
|
||||
$(CC) $^ -o $@ $(LDLIBS)
|
||||
|
||||
@@ -21,10 +25,15 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
test:
|
||||
$(CC) $(CFLAGS) src/logic.c src/testLogic.c -o $(EXECTEST)
|
||||
# 2. Per creare il pacchetto portabile da inviare
|
||||
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/
|
||||
@echo "=== Pacchetto portabile creato con successo in ./dist ==="
|
||||
|
||||
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.mkShell {
|
||||
buildInputs = [
|
||||
nativeBuildInputs = [
|
||||
pkgs.gcc
|
||||
pkgs.gnumake
|
||||
pkgs.pkgconf
|
||||
];
|
||||
buildInputs = [
|
||||
pkgs.ncurses
|
||||
pkgs.json_c
|
||||
];
|
||||
}
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
#include "../include/globals.h"
|
||||
|
||||
char productPricesFileLocation[64]="./productPricesSettings.fpm";
|
||||
char countFileLocation[64]="./count.fpm";
|
||||
char productPricesFileLocation[64]="./pm-item-price-default.json";
|
||||
char countFileLocation[64]="./pm-item-sold-count-default.json";
|
||||
|
||||
+269
-30
@@ -2,61 +2,300 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ncurses.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "../include/logic.h"
|
||||
#include "../include/globals.h"
|
||||
|
||||
/*
|
||||
* 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
|
||||
* Formato dei file (JSON):
|
||||
*
|
||||
* File prodotti: [ {"name": "Maglietta S", "price": 19.90}, ... ]
|
||||
* 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
|
||||
*/
|
||||
|
||||
char *products;
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Utilità sui file */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
int existsFile(const char *fname) {
|
||||
FILE *file;
|
||||
if ((file = fopen(fname, "r")))
|
||||
{
|
||||
FILE *file = fopen(fname, "r");
|
||||
if (file) {
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void createFile(const char *fname) {
|
||||
FILE *file;
|
||||
file = fopen(fname, "w");
|
||||
fclose(file);
|
||||
/* Crea un file JSON contenente un array vuoto. Ritorna 0 se ok, -1 se errore. */
|
||||
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;
|
||||
}
|
||||
|
||||
char* getProducts(const char *fname) {
|
||||
/* FILE *file;
|
||||
|
||||
if((file=fopen(fname, "r"))==NULL) {
|
||||
printf("Errore nell'apertura del file");
|
||||
exit(1);
|
||||
/* Cambia il nome di un file di dati. Ritorna 0 se ok, -1 se errore. */
|
||||
int renameDataFile(const char *oldName, const char *newName) {
|
||||
return (rename(oldName, newName) == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
// TODO QUESTO CODICE NON E' MIO, RICONTROLLARE FUNZIONAMENTO fread()
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Funzioni interne */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
while(!feof(file)){
|
||||
n = fread(buffer, sizeof(char), sizeof(buffer), file);
|
||||
for (i=0; i<n; i++)
|
||||
if (buffer[i] == c)
|
||||
count++;
|
||||
}
|
||||
fclose(file);
|
||||
/*
|
||||
* Carica l'array JSON dal file (creandolo se non esiste).
|
||||
* Il chiamante deve liberare il risultato con json_object_put().
|
||||
* Ritorna NULL in caso di errore o se il contenuto non è un array.
|
||||
*/
|
||||
static json_object *loadArray(const char *fname) {
|
||||
if (!existsFile(fname) && createFile(fname) != 0)
|
||||
return NULL;
|
||||
|
||||
/* Un file vuoto (e.g. creato a mano o dalla vecchia createFile)
|
||||
* non e' JSON valido: lo trattiamo come un array vuoto. */
|
||||
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;
|
||||
}
|
||||
|
||||
int getCount(){
|
||||
/* Cerca un elemento per nome. Ritorna l'oggetto (non va liberato) o NULL. */
|
||||
static json_object *findByName(json_object *arr, const char *name) {
|
||||
size_t n = json_object_array_length(arr);
|
||||
|
||||
return 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
json_object *item = json_object_array_get_idx(arr, i);
|
||||
json_object *jname;
|
||||
|
||||
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||
strcmp(json_object_get_string(jname), name) == 0)
|
||||
return item;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 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 loadArray(fname);
|
||||
}
|
||||
|
||||
/* Stampa a schermo (ncurses) la lista dei prodotti con il relativo prezzo. */
|
||||
void listProducts(const char *fname) {
|
||||
json_object *arr = loadArray(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;
|
||||
|
||||
if (json_object_object_get_ex(item, "name", &jname) &&
|
||||
json_object_object_get_ex(item, "price", &jprice))
|
||||
printw("%zu) %-30s %8.2f\n", i + 1,
|
||||
json_object_get_string(jname),
|
||||
json_object_get_double(jprice));
|
||||
}
|
||||
|
||||
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) {
|
||||
json_object *arr = loadArray(fname);
|
||||
if (!arr)
|
||||
return -1;
|
||||
|
||||
if (findByName(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));
|
||||
json_object_array_add(arr, item);
|
||||
|
||||
int res = json_object_to_file_ext(fname, arr, JSON_C_TO_STRING_PRETTY);
|
||||
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 = loadArray(fname);
|
||||
if (!arr)
|
||||
return -1.0;
|
||||
|
||||
double price = -1.0;
|
||||
json_object *item = findByName(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;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 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 = loadArray(fname);
|
||||
if (!arr)
|
||||
return -1;
|
||||
|
||||
int count = 0;
|
||||
json_object *item = findByName(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 = loadArray(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 = loadArray(fname);
|
||||
if (!arr)
|
||||
return -1;
|
||||
|
||||
json_object *item = findByName(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 = json_object_to_file_ext(fname, arr, JSON_C_TO_STRING_PRETTY);
|
||||
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 = loadArray(productsFile);
|
||||
if (!products)
|
||||
return -1.0;
|
||||
|
||||
json_object *sales = loadArray(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 = findByName(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;
|
||||
}
|
||||
+10
-4
@@ -19,8 +19,9 @@
|
||||
#include <string.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <logic.h>
|
||||
#include "../include/windowsDeclaration.h"
|
||||
#include "../include/windowsBack.h"
|
||||
#include "../include/logic.h"
|
||||
|
||||
void printLogoBG();
|
||||
|
||||
@@ -49,20 +50,25 @@ int main(){
|
||||
|
||||
int running = 0;
|
||||
while(running != 6){
|
||||
printLogoBG();
|
||||
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?");
|
||||
runSellWindow();
|
||||
break;
|
||||
case 2:
|
||||
runViewSalesWindow();
|
||||
break;
|
||||
case 3:
|
||||
runViewProductsWindow();
|
||||
break;
|
||||
case 4:
|
||||
runAddProductWindow();
|
||||
break;
|
||||
case 5:
|
||||
runOptionsMenu();
|
||||
runAboutWindow();
|
||||
// runOptionsMenu();
|
||||
break;
|
||||
case 6:
|
||||
break;
|
||||
|
||||
+73
-7
@@ -6,12 +6,78 @@
|
||||
#include "../include/logic.h"
|
||||
#include "../include/globals.h"
|
||||
|
||||
int main() {
|
||||
/* Working
|
||||
printf("%d", existsFile("qwestoeuntest"));
|
||||
createFile("qwestoeuntest");
|
||||
printf("%d", existsFile("qwestoeuntest"));
|
||||
*/
|
||||
#define PRODUCTS_FILE "test_products.fpm"
|
||||
#define SOLD_FILE "test_sold.fpm"
|
||||
#define RENAMED_FILE "test_renamed.fpm"
|
||||
|
||||
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,204 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
#include "../include/windowsDeclaration.h"
|
||||
#include "../include/windowsBack.h"
|
||||
#include "../include/logic.h"
|
||||
#include "../include/globals.h"
|
||||
#define MAX_LEN 64
|
||||
#define LINE_LEN 96
|
||||
|
||||
/*
|
||||
*
|
||||
* HELPER
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
static void freeLines(char **lines, int n) {
|
||||
for (int i = 0; i < n; i++)
|
||||
free(lines[i]);
|
||||
free(lines);
|
||||
}
|
||||
|
||||
// Legge un campo stringa da un elemento JSON ("?" se manca)
|
||||
static const char *jsonStr(json_object *item, const char *key) {
|
||||
json_object *v = NULL;
|
||||
const char *s = NULL;
|
||||
if (json_object_object_get_ex(item, key, &v) && v)
|
||||
s = json_object_get_string(v);
|
||||
return s ? s : "?";
|
||||
}
|
||||
|
||||
static double jsonDouble(json_object *item, const char *key) {
|
||||
json_object *v = NULL;
|
||||
return (json_object_object_get_ex(item, key, &v) && v) ? json_object_get_double(v) : 0.0;
|
||||
}
|
||||
|
||||
static int jsonInt(json_object *item, const char *key) {
|
||||
json_object *v = NULL;
|
||||
return (json_object_object_get_ex(item, key, &v) && v) ? json_object_get_int(v) : 0;
|
||||
}
|
||||
|
||||
// Costruisce le righe "nome prezzo" dall'array dei prodotti.
|
||||
// *n contiene il numero di righe create. Ritorna NULL se malloc fallisce.
|
||||
static char **buildProductLines(json_object *products, int *n) {
|
||||
int count = (int)json_object_array_length(products);
|
||||
char **lines = malloc((count > 0 ? count : 1) * sizeof *lines);
|
||||
if (!lines) {
|
||||
*n = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int made = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
json_object *item = json_object_array_get_idx(products, i);
|
||||
lines[i] = malloc(LINE_LEN);
|
||||
if (!lines[i])
|
||||
break;
|
||||
snprintf(lines[i], LINE_LEN, "%-26.26s %8.2f",
|
||||
jsonStr(item, "name"), jsonDouble(item, "price"));
|
||||
made++;
|
||||
}
|
||||
*n = made;
|
||||
return lines;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finestra a elenco scorrevole.
|
||||
* - selectable = 1: ritorna l'indice scelto con INVIO/SPAZIO, -1 se annullato (q)
|
||||
* - selectable = 0: sola lettura, ritorna sempre -1
|
||||
* "footer" (puo' essere NULL) e' una riga di testo fissa sopra i comandi.
|
||||
*/
|
||||
static int listWindow(const char *title, char **lines, int n,
|
||||
const char *footer, int selectable) {
|
||||
int height = LINES - 4;
|
||||
int width = COLS / 2;
|
||||
int visible = height - 6;
|
||||
int textw = width - 4;
|
||||
if (visible < 1) visible = 1;
|
||||
if (textw < 0) textw = 0;
|
||||
|
||||
WINDOW *win = create_newwin(height, width, 0, COLS / 2);
|
||||
keypad(win, TRUE);
|
||||
mvwprintw(win, 1, 2, "%.*s", textw, title);
|
||||
|
||||
int sel = 0, top = 0, result = -1, done = 0;
|
||||
|
||||
while (!done) {
|
||||
// Mantiene la selezione dentro l'area visibile
|
||||
if (sel < top) top = sel;
|
||||
if (sel >= top + visible) top = sel - visible + 1;
|
||||
|
||||
for (int i = 0; i < visible; i++) {
|
||||
mvwhline(win, 3 + i, 1, ' ', width - 2);
|
||||
int idx = top + i;
|
||||
if (idx >= n) continue;
|
||||
if (selectable && idx == sel) wattron(win, A_REVERSE);
|
||||
mvwprintw(win, 3 + i, 2, "%.*s", textw, lines[idx]);
|
||||
if (selectable && idx == sel) wattroff(win, A_REVERSE);
|
||||
}
|
||||
if (n == 0)
|
||||
mvwprintw(win, 3, 2, "Nessun elemento.");
|
||||
|
||||
mvwhline(win, height - 3, 1, ' ', width - 2);
|
||||
if (footer)
|
||||
mvwprintw(win, height - 3, 2, "%.*s", textw, footer);
|
||||
|
||||
mvwhline(win, height - 2, 1, ' ', width - 2);
|
||||
mvwprintw(win, height - 2, 2, "%.*s", textw,
|
||||
selectable ? "SU/GIU scorri INVIO scegli q annulla"
|
||||
: "SU/GIU scorri q chiudi");
|
||||
wrefresh(win);
|
||||
|
||||
switch (wgetch(win)) {
|
||||
case KEY_UP: case 'k': if (sel > 0) sel--; break;
|
||||
case KEY_DOWN: case 'j': if (sel < n - 1) sel++; break;
|
||||
case KEY_PPAGE:
|
||||
sel -= visible;
|
||||
if (sel < 0) sel = 0;
|
||||
break;
|
||||
case KEY_NPAGE:
|
||||
sel += visible;
|
||||
if (sel > n - 1) sel = n - 1;
|
||||
if (sel < 0) sel = 0;
|
||||
break;
|
||||
case 10: case 32:
|
||||
if (selectable && n > 0) result = sel;
|
||||
done = 1;
|
||||
break;
|
||||
case 'q':
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
werase(win);
|
||||
destroy_win(win);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Finestre
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
void example_window() {
|
||||
WINDOW *win;
|
||||
int height = LINES / 2 -2;
|
||||
int width = COLS / 2;
|
||||
int starty = LINES / 2;
|
||||
int startx = COLS / 2;
|
||||
win = create_newwin(height, width, starty, startx);
|
||||
mvwprintw(win, 1, 2, "Esempio Interfaccia");
|
||||
|
||||
char *opts[] = {"Opzione 1", "Opzione 2", "Opzione 3"};
|
||||
mvwprintw(win, 3, 2, "Scegli dal menu:");
|
||||
int menu_res = uiSelectMenu(win, 4, 4, opts, 3);
|
||||
|
||||
char txt_buf[64] = "Testo base";
|
||||
mvwprintw(win, 9, 2, "Inserisci testo:");
|
||||
uiTextInput(win, 9, 19, txt_buf, 30);
|
||||
|
||||
mvwprintw(win, 11, 2, "Attiva opzione:");
|
||||
int switch_res = uiToggleSwitch(win, 11, 18, 0);
|
||||
|
||||
mvwprintw(win, 13, 2, "Scelto: M=%d, T=%s, S=%d", menu_res, txt_buf, switch_res);
|
||||
wrefresh(win);
|
||||
wgetch(win);
|
||||
werase(win);
|
||||
destroy_win(win);
|
||||
}
|
||||
|
||||
int runMainMenu(){
|
||||
char *mainMenu[] = {
|
||||
"Segna vendita",
|
||||
"Vedi vendite",
|
||||
"Vedi prodotti",
|
||||
"Aggiungi prodotti",
|
||||
"About",
|
||||
"Exit"
|
||||
};
|
||||
int n_mainMenu = sizeof(mainMenu) / sizeof(char *);
|
||||
|
||||
WINDOW *mainWin;
|
||||
int height = LINES / 2;
|
||||
int width = COLS / 2;
|
||||
int starty = 0;
|
||||
int startx = 0;
|
||||
mainWin = create_newwin(height, width, starty, startx);
|
||||
|
||||
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();
|
||||
|
||||
int choice = uiSelectMenu(mainWin, height / 2 - 3, width / 2 - 9, mainMenu, n_mainMenu);
|
||||
statusf("Avvio opzione '%s'", mainMenu[choice - 1]);
|
||||
|
||||
clrtoeol();
|
||||
refresh();
|
||||
|
||||
destroy_win(mainWin);
|
||||
return choice;
|
||||
}
|
||||
|
||||
void runAboutWindow(){
|
||||
WINDOW *aboutWin;
|
||||
aboutWin = create_newwin(LINES/2-2, COLS/2, LINES/2, 0);
|
||||
|
||||
wwrap_print(aboutWin, 1, 1, "Questo e' un software fatto per Roma2LUG, il codice verra' pubblicato sul gitea del lug in futuro. La parte logica e' fatta con Claude Code, non avevo voglia di scrivere quei pezzi e volevo effettivamente rendere utile il codice che avevo scritto per gestire ncurses. Oh well, spero vi possa essere comunque utile. Buon lavoro / Divertimento!");
|
||||
statusf("Uovo di pasqua ()");
|
||||
wrefresh(aboutWin);
|
||||
getch();
|
||||
werase(aboutWin);
|
||||
destroy_win(aboutWin);
|
||||
}
|
||||
|
||||
void runAddProductWindow() {
|
||||
WINDOW *addWin = create_newwin(LINES / 2, COLS / 2, 0, COLS / 2);
|
||||
|
||||
char name[33] = "";
|
||||
char priceStr[13] = "";
|
||||
char *opts[] = {"Salva", "Annulla"};
|
||||
|
||||
mvwprintw(addWin, 1, 2, "Aggiungi un nuovo prodotto");
|
||||
|
||||
mvwprintw(addWin, 3, 2, "Nome:");
|
||||
uiTextInput(addWin, 4, 2, name, 32);
|
||||
|
||||
mvwprintw(addWin, 6, 2, "Prezzo (es. 19.90):");
|
||||
uiTextInput(addWin, 7, 2, priceStr, 12);
|
||||
|
||||
int choice = uiSelectMenu(addWin, 9, 4, opts, 2);
|
||||
|
||||
if (choice == 1) {
|
||||
for (char *p = priceStr; *p; p++)
|
||||
if (*p == ',') *p = '.';
|
||||
|
||||
char *end;
|
||||
double price = strtod(priceStr, &end);
|
||||
|
||||
if (name[0] == '\0') {
|
||||
warningf("Il nome del prodotto non puo' essere vuoto.");
|
||||
} else if (priceStr[0] == '\0' || *end != '\0' || price < 0) {
|
||||
warningf("Il prezzo '%s' non e' valido.", priceStr);
|
||||
} else {
|
||||
int res = addProduct(productPricesFileLocation, name, price);
|
||||
if (res == 0)
|
||||
statusf("Prodotto '%s' aggiunto a %.2f", name, price);
|
||||
else if (res == 1)
|
||||
warningf("Il prodotto '%s' esiste gia'.", name);
|
||||
else
|
||||
warningf("Errore nella scrittura del file '%s'.", productPricesFileLocation);
|
||||
}
|
||||
} else {
|
||||
statusf("Operazione annullata");
|
||||
}
|
||||
|
||||
werase(addWin);
|
||||
destroy_win(addWin);
|
||||
}
|
||||
|
||||
void runOptionsMenu(){
|
||||
WINDOW *optionsWin;
|
||||
optionsWin = create_newwin(LINES/2, COLS/2, 0, COLS/2);
|
||||
|
||||
char input[MAX_LEN + 1];
|
||||
strcpy(input, productPricesFileLocation);
|
||||
|
||||
mvwprintw(optionsWin, 1, 2, "Posizione file prodotti:");
|
||||
uiTextInput(optionsWin, 2, 2, input, MAX_LEN);
|
||||
|
||||
if (existsFile(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);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Segna vendita */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
void runSellWindow() {
|
||||
json_object *products = getProducts(productPricesFileLocation);
|
||||
if (!products) {
|
||||
warningf("Impossibile leggere il file '%s'.", productPricesFileLocation);
|
||||
return;
|
||||
}
|
||||
|
||||
if (json_object_array_length(products) == 0) {
|
||||
warningf("Non ci sono prodotti. Aggiungine uno da 'Gestisci prodotti'.");
|
||||
json_object_put(products);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Scelta del prodotto
|
||||
int n;
|
||||
char **lines = buildProductLines(products, &n);
|
||||
if (!lines) {
|
||||
warningf("Memoria insufficiente.");
|
||||
json_object_put(products);
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = listWindow("Segna vendita - scegli il prodotto", lines, n, NULL, 1);
|
||||
freeLines(lines, n);
|
||||
|
||||
if (idx < 0) {
|
||||
statusf("Operazione annullata");
|
||||
json_object_put(products);
|
||||
return;
|
||||
}
|
||||
|
||||
json_object *item = json_object_array_get_idx(products, idx);
|
||||
const char *name = jsonStr(item, "name");
|
||||
double price = jsonDouble(item, "price");
|
||||
|
||||
// 2. Quantita' venduta
|
||||
WINDOW *win = create_newwin(9, COLS / 2, 0, COLS / 2);
|
||||
keypad(win, TRUE);
|
||||
|
||||
char qtyStr[8] = "1";
|
||||
char *opts[] = {"Salva", "Annulla"};
|
||||
|
||||
mvwprintw(win, 1, 2, "Segna vendita");
|
||||
mvwprintw(win, 3, 2, "Prodotto: %.30s", name);
|
||||
mvwprintw(win, 4, 2, "Quantita' venduta:");
|
||||
uiTextInput(win, 5, 2, qtyStr, 6);
|
||||
|
||||
int choice = uiSelectMenu(win, 6, 4, opts, 2);
|
||||
|
||||
if (choice == 1) {
|
||||
char *end;
|
||||
long qty = strtol(qtyStr, &end, 10);
|
||||
|
||||
if (qtyStr[0] == '\0' || *end != '\0' || qty <= 0 || qty > 1000000) {
|
||||
warningf("La quantita' '%s' non e' valida.", qtyStr);
|
||||
} else if (addSold(countFileLocation, name, (int)qty) == 0) {
|
||||
statusf("Registrata vendita: %ld x '%s' = %.2f", qty, name, qty * price);
|
||||
} else {
|
||||
warningf("Errore nella scrittura del file '%s'.", countFileLocation);
|
||||
}
|
||||
} else {
|
||||
statusf("Operazione annullata");
|
||||
}
|
||||
|
||||
werase(win);
|
||||
destroy_win(win);
|
||||
json_object_put(products);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Vedi vendite */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
void runViewSalesWindow() {
|
||||
json_object *sales = getProducts(countFileLocation);
|
||||
if (!sales) {
|
||||
warningf("Impossibile leggere il file '%s'.", countFileLocation);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = (int)json_object_array_length(sales);
|
||||
char **lines = malloc((count > 0 ? count : 1) * sizeof *lines);
|
||||
if (!lines) {
|
||||
warningf("Memoria insufficiente.");
|
||||
json_object_put(sales);
|
||||
return;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
json_object *item = json_object_array_get_idx(sales, i);
|
||||
const char *name = jsonStr(item, "name");
|
||||
int sold = jsonInt(item, "sold");
|
||||
double price = getPrice(productPricesFileLocation, name);
|
||||
|
||||
lines[i] = malloc(LINE_LEN);
|
||||
if (!lines[i])
|
||||
break;
|
||||
|
||||
if (price >= 0)
|
||||
snprintf(lines[i], LINE_LEN, "%-18.18s %4d %9.2f", name, sold, sold * price);
|
||||
else
|
||||
snprintf(lines[i], LINE_LEN, "%-18.18s %4d %9s", name, sold, "n/d");
|
||||
n++;
|
||||
}
|
||||
|
||||
char footer[64];
|
||||
double total = getTotalRevenue(productPricesFileLocation, countFileLocation);
|
||||
if (total >= 0)
|
||||
snprintf(footer, sizeof footer, "Entrate totali: %.2f", total);
|
||||
else
|
||||
snprintf(footer, sizeof footer, "Entrate totali: n/d");
|
||||
|
||||
listWindow("Vendite (prodotto / copie / incasso)", lines, n, footer, 0);
|
||||
|
||||
freeLines(lines, n);
|
||||
json_object_put(sales);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Vedi prodotti */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
void runViewProductsWindow() {
|
||||
json_object *products = getProducts(productPricesFileLocation);
|
||||
if (!products) {
|
||||
warningf("Impossibile leggere il file '%s'.", productPricesFileLocation);
|
||||
return;
|
||||
}
|
||||
|
||||
int n;
|
||||
char **lines = buildProductLines(products, &n);
|
||||
if (!lines) {
|
||||
warningf("Memoria insufficiente.");
|
||||
json_object_put(products);
|
||||
return;
|
||||
}
|
||||
|
||||
char footer[64];
|
||||
snprintf(footer, sizeof footer, "Prodotti in listino: %d", n);
|
||||
|
||||
listWindow("Prodotti (nome / prezzo)", lines, n, footer, 0);
|
||||
|
||||
freeLines(lines, n);
|
||||
json_object_put(products);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user