230 lines
8.7 KiB
C
230 lines
8.7 KiB
C
#include <gtk/gtk.h>
|
|
#include <cairo.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#define MAX_STUDENTS 1024
|
|
#define MAX_NAME 256
|
|
|
|
typedef struct {
|
|
char name[MAX_NAME];
|
|
int votes;
|
|
} Student;
|
|
|
|
static GtkWidget *text_input;
|
|
static GtkWidget *text_output;
|
|
static GtkWidget *drawing_area;
|
|
|
|
static Student chart_students[MAX_STUDENTS];
|
|
static int chart_count = 0;
|
|
|
|
static const double bar_colors[][3] = {
|
|
{0.96, 0.76, 0.05},
|
|
{0.75, 0.75, 0.75},
|
|
{0.80, 0.50, 0.20},
|
|
{0.26, 0.53, 0.96},
|
|
{0.20, 0.78, 0.35},
|
|
{0.90, 0.30, 0.30},
|
|
{0.60, 0.20, 0.80},
|
|
{0.10, 0.70, 0.80},
|
|
};
|
|
#define NUM_COLORS 8
|
|
|
|
static int find_student(Student *students, int count, const char *name) {
|
|
for (int i = 0; i < count; i++)
|
|
if (strcmp(students[i].name, name) == 0) return i;
|
|
return -1;
|
|
}
|
|
|
|
static int cmp(const void *a, const void *b) {
|
|
const Student *sa = (const Student *)a;
|
|
const Student *sb = (const Student *)b;
|
|
if (sb->votes != sa->votes) return sb->votes - sa->votes;
|
|
return strcmp(sa->name, sb->name);
|
|
}
|
|
|
|
static void set_output(const char *text) {
|
|
GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_output));
|
|
gtk_text_buffer_set_text(buf, text, -1);
|
|
}
|
|
|
|
static gboolean on_draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
|
(void)data;
|
|
int width = gtk_widget_get_allocated_width(widget);
|
|
int height = gtk_widget_get_allocated_height(widget);
|
|
|
|
cairo_set_source_rgb(cr, 0.15, 0.15, 0.15);
|
|
cairo_paint(cr);
|
|
|
|
if (chart_count == 0) {
|
|
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
|
|
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_NORMAL);
|
|
cairo_set_font_size(cr, 13);
|
|
cairo_move_to(cr, 20, height / 2.0);
|
|
cairo_show_text(cr, "Zadajte hlasy a kliknite Spočítať");
|
|
return FALSE;
|
|
}
|
|
|
|
int margin_left = 20, margin_right = 20, margin_top = 24, margin_bottom = 70;
|
|
int max_votes = chart_students[0].votes;
|
|
int n = chart_count > 20 ? 20 : chart_count;
|
|
|
|
double bar_area_w = width - margin_left - margin_right;
|
|
double bar_area_h = height - margin_top - margin_bottom;
|
|
double slot = bar_area_w / n;
|
|
double bar_w = slot - 6;
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
double bar_h = (bar_area_h * chart_students[i].votes) / (double)max_votes;
|
|
double x = margin_left + i * slot + 3;
|
|
double y = margin_top + bar_area_h - bar_h;
|
|
|
|
int ci = i < 3 ? i : (3 + (i % (NUM_COLORS - 3)));
|
|
cairo_set_source_rgb(cr, bar_colors[ci][0], bar_colors[ci][1], bar_colors[ci][2]);
|
|
cairo_rectangle(cr, x, y, bar_w, bar_h);
|
|
cairo_fill(cr);
|
|
|
|
// Vote count on top
|
|
cairo_set_source_rgb(cr, 1, 1, 1);
|
|
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
|
|
cairo_set_font_size(cr, 11);
|
|
char vs[16];
|
|
snprintf(vs, sizeof(vs), "%d", chart_students[i].votes);
|
|
cairo_text_extents_t ext;
|
|
cairo_text_extents(cr, vs, &ext);
|
|
cairo_move_to(cr, x + (bar_w - ext.width) / 2.0, y - 5);
|
|
cairo_show_text(cr, vs);
|
|
|
|
// Name rotated below bar
|
|
cairo_save(cr);
|
|
cairo_translate(cr, x + bar_w / 2.0, height - margin_bottom + 10);
|
|
cairo_rotate(cr, -G_PI / 4.0);
|
|
cairo_set_source_rgb(cr, 0.9, 0.9, 0.9);
|
|
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
|
cairo_set_font_size(cr, 10);
|
|
char short_name[24];
|
|
if (strlen(chart_students[i].name) > 20) {
|
|
strncpy(short_name, chart_students[i].name, 19);
|
|
short_name[19] = '\0';
|
|
strcat(short_name, "~");
|
|
} else {
|
|
strncpy(short_name, chart_students[i].name, sizeof(short_name) - 1);
|
|
short_name[sizeof(short_name)-1] = '\0';
|
|
}
|
|
cairo_move_to(cr, 0, 0);
|
|
cairo_show_text(cr, short_name);
|
|
cairo_restore(cr);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void calculate(GtkWidget *widget, gpointer data) {
|
|
(void)widget; (void)data;
|
|
|
|
GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_input));
|
|
GtkTextIter start, end;
|
|
gtk_text_buffer_get_start_iter(buf, &start);
|
|
gtk_text_buffer_get_end_iter(buf, &end);
|
|
char *text = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
|
|
|
|
Student students[MAX_STUDENTS];
|
|
int count = 0, loaded = 0;
|
|
|
|
char *line = strtok(text, "\n");
|
|
while (line != NULL) {
|
|
if (strlen(line) == 0 || line[0] == '\r') { line = strtok(NULL, "\n"); continue; }
|
|
int votes; char name[MAX_NAME];
|
|
if (sscanf(line, "%d %255[^\n\r]", &votes, name) != 2 || votes <= 0) break;
|
|
loaded++;
|
|
int idx = find_student(students, count, name);
|
|
if (idx >= 0) { students[idx].votes += votes; }
|
|
else if (count < MAX_STUDENTS) { strncpy(students[count].name, name, MAX_NAME-1); students[count].votes = votes; count++; }
|
|
line = strtok(NULL, "\n");
|
|
}
|
|
g_free(text);
|
|
|
|
if (loaded == 0) { set_output("Nepodarilo nacitat nic"); chart_count = 0; gtk_widget_queue_draw(drawing_area); return; }
|
|
|
|
qsort(students, count, sizeof(Student), cmp);
|
|
chart_count = count;
|
|
memcpy(chart_students, students, count * sizeof(Student));
|
|
gtk_widget_queue_draw(drawing_area);
|
|
|
|
char output[MAX_STUDENTS * (MAX_NAME + 16)];
|
|
int pos = 0;
|
|
pos += snprintf(output + pos, sizeof(output) - pos, "Vysledky:\n");
|
|
for (int i = 0; i < count; i++)
|
|
pos += snprintf(output + pos, sizeof(output) - pos, "%d %s\n", students[i].votes, students[i].name);
|
|
set_output(output);
|
|
}
|
|
|
|
static void clear_all(GtkWidget *widget, gpointer data) {
|
|
(void)widget; (void)data;
|
|
gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_input)), "", -1);
|
|
set_output("");
|
|
chart_count = 0;
|
|
gtk_widget_queue_draw(drawing_area);
|
|
}
|
|
|
|
static void activate(GtkApplication *app, gpointer user_data) {
|
|
(void)user_data;
|
|
GtkWidget *window = gtk_application_window_new(app);
|
|
gtk_window_set_title(GTK_WINDOW(window), "Anketa Študent roka");
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 900, 650);
|
|
gtk_container_set_border_width(GTK_CONTAINER(window), 12);
|
|
|
|
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
|
|
gtk_container_add(GTK_CONTAINER(window), hbox);
|
|
|
|
// Left panel
|
|
GtkWidget *left = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
|
|
gtk_box_pack_start(GTK_BOX(hbox), left, FALSE, FALSE, 0);
|
|
gtk_widget_set_size_request(left, 280, -1);
|
|
|
|
gtk_box_pack_start(GTK_BOX(left), gtk_label_new("Hlasy (počet meno):"), FALSE, FALSE, 0);
|
|
GtkWidget *scroll_in = gtk_scrolled_window_new(NULL, NULL);
|
|
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll_in), 220);
|
|
gtk_box_pack_start(GTK_BOX(left), scroll_in, TRUE, TRUE, 0);
|
|
text_input = gtk_text_view_new();
|
|
gtk_text_view_set_monospace(GTK_TEXT_VIEW(text_input), TRUE);
|
|
gtk_container_add(GTK_CONTAINER(scroll_in), text_input);
|
|
|
|
GtkWidget *btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
|
|
gtk_box_pack_start(GTK_BOX(left), btn_box, FALSE, FALSE, 0);
|
|
GtkWidget *btn_calc = gtk_button_new_with_label("Spočítať hlasy");
|
|
g_signal_connect(btn_calc, "clicked", G_CALLBACK(calculate), NULL);
|
|
gtk_box_pack_start(GTK_BOX(btn_box), btn_calc, TRUE, TRUE, 0);
|
|
GtkWidget *btn_clear = gtk_button_new_with_label("Vymazať");
|
|
g_signal_connect(btn_clear, "clicked", G_CALLBACK(clear_all), NULL);
|
|
gtk_box_pack_start(GTK_BOX(btn_box), btn_clear, FALSE, FALSE, 0);
|
|
|
|
gtk_box_pack_start(GTK_BOX(left), gtk_label_new("Výsledky:"), FALSE, FALSE, 0);
|
|
GtkWidget *scroll_out = gtk_scrolled_window_new(NULL, NULL);
|
|
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll_out), 180);
|
|
gtk_box_pack_start(GTK_BOX(left), scroll_out, TRUE, TRUE, 0);
|
|
text_output = gtk_text_view_new();
|
|
gtk_text_view_set_monospace(GTK_TEXT_VIEW(text_output), TRUE);
|
|
gtk_text_view_set_editable(GTK_TEXT_VIEW(text_output), FALSE);
|
|
gtk_container_add(GTK_CONTAINER(scroll_out), text_output);
|
|
|
|
// Right panel: chart
|
|
GtkWidget *right = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
|
|
gtk_box_pack_start(GTK_BOX(hbox), right, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(right), gtk_label_new("Graf hlasov:"), FALSE, FALSE, 0);
|
|
drawing_area = gtk_drawing_area_new();
|
|
gtk_box_pack_start(GTK_BOX(right), drawing_area, TRUE, TRUE, 0);
|
|
g_signal_connect(drawing_area, "draw", G_CALLBACK(on_draw), NULL);
|
|
|
|
gtk_widget_show_all(window);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
GtkApplication *app = gtk_application_new("sk.tuke.pvjc.anketa", G_APPLICATION_DEFAULT_FLAGS);
|
|
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
|
int status = g_application_run(G_APPLICATION(app), argc, argv);
|
|
g_object_unref(app);
|
|
return status;
|
|
}
|