327 lines
11 KiB
C
327 lines
11 KiB
C
#include "window.h"
|
|
#include "ui.h"
|
|
#include "edit.h"
|
|
#include "draw.h"
|
|
#include "geometry.h"
|
|
#include "level.h"
|
|
#include <gtk/gtk.h>
|
|
#include <string.h>
|
|
|
|
|
|
static GtkWidget *drawingArea = NULL, *sidebar = NULL;
|
|
static GtkAdjustment *hAdjustment = NULL, *vAdjustment = NULL;
|
|
|
|
static GtkWidget *entryName, *labelArea = NULL, *labelPerimeter = NULL;
|
|
|
|
|
|
static gboolean deleteEvent(GtkWidget *widget, GdkEvent *event, gpointer data) {
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean scrollEvent(GtkWidget *widget, GdkEventScroll *event, gpointer user_data) {
|
|
const gdouble x = event->x/widget->allocation.width, y = event->y/widget->allocation.height;
|
|
|
|
switch(event->direction) {
|
|
case GDK_SCROLL_UP:
|
|
zoomIn(1.1, x, y);
|
|
break;
|
|
case GDK_SCROLL_DOWN:
|
|
zoomOut(1.1, x, y);
|
|
}
|
|
}
|
|
|
|
static gboolean buttonEvent(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
|
|
VERTEX v = {event->x, event->y};
|
|
int i;
|
|
|
|
switch(event->type) {
|
|
case GDK_BUTTON_PRESS:
|
|
switch(event->button) {
|
|
case 1:
|
|
viewToImage(&v);
|
|
|
|
setActiveRoom(NULL);
|
|
|
|
for(i = 0; i < getLevel()->nRooms; i++) {
|
|
if(vertexInPolygon(&v, &getLevel()->rooms[i].polygon)) {
|
|
setActiveRoom(&getLevel()->rooms[i]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
updateSidebar();
|
|
|
|
redraw();
|
|
gtk_widget_queue_draw(drawingArea);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean crossingNotifyEvent(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) {
|
|
int i;
|
|
VERTEX v;
|
|
|
|
|
|
setHoveredRoom(NULL);
|
|
|
|
if(event->type == GDK_ENTER_NOTIFY) {
|
|
v.x = event->x;
|
|
v.y = event->y;
|
|
|
|
viewToImage(&v);
|
|
|
|
|
|
for(i = 0; i < getLevel()->nRooms; i++) {
|
|
if(vertexInPolygon(&v, &getLevel()->rooms[i].polygon)) {
|
|
setHoveredRoom(&getLevel()->rooms[i]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
gtk_widget_queue_draw(drawingArea);
|
|
}
|
|
|
|
gboolean motionNotifyEvent(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) {
|
|
int i;
|
|
static VERTEX v, v_last = {0, 0};
|
|
LINE line;
|
|
gboolean changed = FALSE;
|
|
|
|
v.x = event->x;
|
|
v.y = event->y;
|
|
|
|
viewToImage(&v);
|
|
|
|
line.v1 = v_last;
|
|
line.v2 = v;
|
|
|
|
for(i = 0; i < getLevel()->nRooms; i++) {
|
|
if(linePolygonIntersection(&line, &getLevel()->rooms[i].polygon)) {
|
|
if(vertexInPolygon(&v, &getLevel()->rooms[i].polygon)) {
|
|
setHoveredRoom(&getLevel()->rooms[i]);
|
|
changed = TRUE;
|
|
|
|
break;
|
|
}
|
|
else {
|
|
setHoveredRoom(NULL);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(changed)
|
|
gtk_widget_queue_draw(drawingArea);
|
|
|
|
v_last = v;
|
|
}
|
|
|
|
static void destroy(GtkWidget *widget, gpointer data) {
|
|
gtk_main_quit();
|
|
}
|
|
|
|
static void refreshScrolling() {
|
|
if(getImageWidth() < drawingArea->allocation.width)
|
|
setXTranslate((getImageWidth()-drawingArea->allocation.width)/2);
|
|
else
|
|
setXTranslate(gtk_adjustment_get_value(hAdjustment));
|
|
|
|
if(getImageHeight() < drawingArea->allocation.height)
|
|
setYTranslate((getImageHeight()-drawingArea->allocation.height)/2);
|
|
else
|
|
setYTranslate(gtk_adjustment_get_value(vAdjustment));
|
|
|
|
gtk_widget_queue_draw(drawingArea);
|
|
}
|
|
|
|
static void updateScrollbars(double x, double y) {
|
|
const gdouble imageWidth = getImageWidth(), imageHeight = getImageHeight();
|
|
const gdouble width = drawingArea->allocation.width, height = drawingArea->allocation.height;
|
|
gdouble upper, pageSize, value;
|
|
|
|
gdk_window_freeze_updates(drawingArea->window);
|
|
|
|
g_object_get(G_OBJECT(hAdjustment), "upper", &upper, "page_size", &pageSize, NULL);
|
|
g_object_set(G_OBJECT(hAdjustment), "upper", imageWidth, "page_size", width, NULL);
|
|
gtk_adjustment_changed(hAdjustment);
|
|
|
|
if((pageSize > upper && width < imageWidth) || upper == 0)
|
|
value = (imageWidth-width)/2;
|
|
else
|
|
value = (gtk_adjustment_get_value(hAdjustment)+pageSize*x)/upper*imageWidth-width*x;
|
|
gtk_adjustment_set_value(hAdjustment, MAX(MIN(value, imageWidth-width), 0));
|
|
|
|
g_object_get(G_OBJECT(vAdjustment), "upper", &upper, "page_size", &pageSize, NULL);
|
|
g_object_set(G_OBJECT(vAdjustment), "upper", imageHeight, "page_size", height, NULL);
|
|
gtk_adjustment_changed(vAdjustment);
|
|
|
|
if((pageSize > upper && height < imageHeight) || upper == 0)
|
|
value = (imageHeight-height)/2;
|
|
else
|
|
value = (gtk_adjustment_get_value(vAdjustment)+pageSize*y)/upper*imageHeight-height*y;
|
|
gtk_adjustment_set_value(vAdjustment, MAX(MIN(value, imageHeight-height), 0));
|
|
|
|
gdk_window_thaw_updates(drawingArea->window);
|
|
|
|
refreshScrolling();
|
|
}
|
|
|
|
static void updateScrollbarsCentered() {
|
|
updateScrollbars(0.5, 0.5);
|
|
}
|
|
|
|
static void sidebarNameChanged(GtkEditable *editable, gpointer user_data) {
|
|
if(getActiveRoom() == NULL) return;
|
|
|
|
free(getActiveRoom()->name);
|
|
getActiveRoom()->name = strdup(gtk_entry_get_text(GTK_ENTRY(entryName)));
|
|
}
|
|
|
|
static GtkWidget* createSidebar() {
|
|
GtkWidget *widget, *labelRoomInfo, *labelName, *tableRoomData, *labelAreaLabel, *labelPerimeterLabel;
|
|
|
|
widget = gtk_vbox_new(FALSE, 0);
|
|
|
|
labelRoomInfo = gtk_label_new(NULL);
|
|
gtk_label_set_markup(GTK_LABEL(labelRoomInfo), "<b>Room info:</b>");
|
|
gtk_misc_set_alignment(GTK_MISC(labelRoomInfo), 0.0, 0.5);
|
|
gtk_box_pack_start(GTK_BOX(widget), labelRoomInfo, FALSE, FALSE, 5);
|
|
|
|
gtk_box_pack_start(GTK_BOX(widget), gtk_hseparator_new(), FALSE, FALSE, 5);
|
|
|
|
labelName = gtk_label_new("Name:");
|
|
gtk_misc_set_alignment(GTK_MISC(labelName), 0.0, 0.5);
|
|
gtk_box_pack_start(GTK_BOX(widget), labelName, FALSE, FALSE, 0);
|
|
|
|
entryName = gtk_entry_new();
|
|
gtk_widget_set_sensitive(entryName, FALSE);
|
|
g_signal_connect(G_OBJECT(entryName), "changed", G_CALLBACK(sidebarNameChanged), NULL);
|
|
gtk_box_pack_start(GTK_BOX(widget), entryName, FALSE, FALSE, 0);
|
|
|
|
tableRoomData = gtk_table_new(2, 2, FALSE);
|
|
gtk_table_set_row_spacings(GTK_TABLE(tableRoomData), 5);
|
|
gtk_table_set_col_spacings(GTK_TABLE(tableRoomData), 10);
|
|
gtk_box_pack_start(GTK_BOX(widget), tableRoomData, FALSE, FALSE, 5);
|
|
|
|
labelAreaLabel = gtk_label_new("Room area:");
|
|
gtk_misc_set_alignment(GTK_MISC(labelAreaLabel), 0.0, 0.5);
|
|
gtk_table_attach_defaults(GTK_TABLE(tableRoomData), labelAreaLabel, 0, 1, 0, 1);
|
|
|
|
labelArea = gtk_label_new(NULL);
|
|
gtk_misc_set_alignment(GTK_MISC(labelArea), 1.0, 0.5);
|
|
gtk_table_attach_defaults(GTK_TABLE(tableRoomData), labelArea, 1, 2, 0, 1);
|
|
|
|
labelPerimeterLabel = gtk_label_new("Room perimeter:");
|
|
gtk_misc_set_alignment(GTK_MISC(labelPerimeterLabel), 0.0, 0.5);
|
|
gtk_table_attach_defaults(GTK_TABLE(tableRoomData), labelPerimeterLabel, 0, 1, 1, 2);
|
|
|
|
labelPerimeter = gtk_label_new(NULL);
|
|
gtk_misc_set_alignment(GTK_MISC(labelPerimeter), 1.0, 0.5);
|
|
gtk_table_attach_defaults(GTK_TABLE(tableRoomData), labelPerimeter, 1, 2, 1, 2);
|
|
|
|
return widget;
|
|
}
|
|
|
|
GtkWidget* createMainWindow() {
|
|
GtkWidget *window, *vbox, *hPaned, *table, *vScroll, *hScroll;
|
|
GdkColor color = {0, 0, 0, 0};
|
|
|
|
|
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
|
|
g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(deleteEvent), NULL);
|
|
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
|
|
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
|
gtk_container_add(GTK_CONTAINER(window), vbox);
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), getMenu(), FALSE, FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), getToolbar(), FALSE, FALSE, 0);
|
|
|
|
gtk_window_add_accel_group(GTK_WINDOW(window), getAccels());
|
|
|
|
hPaned = gtk_hpaned_new();
|
|
gtk_box_pack_end(GTK_BOX(vbox), hPaned, TRUE, TRUE, 0);
|
|
|
|
table = gtk_table_new(2, 2, FALSE);
|
|
gtk_paned_pack1(GTK_PANED(hPaned), table, TRUE, TRUE);
|
|
|
|
drawingArea = gtk_drawing_area_new();
|
|
gtk_widget_modify_bg(drawingArea, GTK_WIDGET_STATE(drawingArea), &color);
|
|
g_signal_connect(G_OBJECT(drawingArea), "configure-event", G_CALLBACK(updateScrollbarsCentered), NULL);
|
|
g_signal_connect(G_OBJECT(drawingArea), "expose-event", G_CALLBACK(drawTopView), NULL);
|
|
g_signal_connect(G_OBJECT(drawingArea), "button-press-event", G_CALLBACK(buttonEvent), NULL);
|
|
g_signal_connect(G_OBJECT(drawingArea), "button-release-event", G_CALLBACK(buttonEvent), NULL);
|
|
g_signal_connect(G_OBJECT(drawingArea), "enter-notify-event", G_CALLBACK(crossingNotifyEvent), NULL);
|
|
g_signal_connect(G_OBJECT(drawingArea), "leave-notify-event", G_CALLBACK(crossingNotifyEvent), NULL);
|
|
g_signal_connect(G_OBJECT(drawingArea), "motion-notify-event", G_CALLBACK(motionNotifyEvent), NULL);
|
|
g_signal_connect(G_OBJECT(drawingArea), "scroll-event", G_CALLBACK(scrollEvent), NULL);
|
|
gtk_widget_add_events(drawingArea, GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
|
|
gtk_widget_set_double_buffered(drawingArea, FALSE);
|
|
gtk_table_attach(GTK_TABLE(table), drawingArea, 0, 1, 0, 1, GTK_FILL|GTK_EXPAND|GTK_SHRINK, GTK_FILL|GTK_EXPAND|GTK_SHRINK, 0, 0);
|
|
|
|
hAdjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 10, 100, 100));
|
|
hScroll = gtk_hscrollbar_new(hAdjustment);
|
|
g_signal_connect(G_OBJECT(hScroll), "value-changed", G_CALLBACK(refreshScrolling), NULL);
|
|
gtk_table_attach(GTK_TABLE(table), hScroll, 0, 1, 1, 2, GTK_FILL|GTK_EXPAND|GTK_SHRINK, 0, 0, 0);
|
|
|
|
vAdjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 10, 100, 100));
|
|
vScroll = gtk_vscrollbar_new(vAdjustment);
|
|
g_signal_connect(G_OBJECT(vScroll), "value-changed", G_CALLBACK(refreshScrolling), NULL);
|
|
gtk_table_attach(GTK_TABLE(table), vScroll, 1, 2, 0, 1, 0, GTK_FILL|GTK_EXPAND|GTK_SHRINK, 0, 0);
|
|
|
|
sidebar = createSidebar();
|
|
gtk_paned_pack2(GTK_PANED(hPaned), sidebar, FALSE, TRUE);
|
|
|
|
gtk_widget_show_all(vbox);
|
|
|
|
return window;
|
|
}
|
|
|
|
void updateSidebar() {
|
|
gchar *string;
|
|
|
|
|
|
if(getActiveRoom()) {
|
|
gtk_entry_set_text(GTK_ENTRY(entryName), getActiveRoom()->name);
|
|
gtk_widget_set_sensitive(entryName, TRUE);
|
|
|
|
string = g_strdup_printf("%.2f", polygonArea(&getActiveRoom()->polygon));
|
|
gtk_label_set_text(GTK_LABEL(labelArea), string);
|
|
g_free(string);
|
|
|
|
string = g_strdup_printf("%.2f", polygonPerimeter(&getActiveRoom()->polygon));
|
|
gtk_label_set_text(GTK_LABEL(labelPerimeter), string);
|
|
g_free(string);
|
|
}
|
|
else {
|
|
gtk_entry_set_text(GTK_ENTRY(entryName), "");
|
|
gtk_widget_set_sensitive(entryName, FALSE);
|
|
|
|
gtk_label_set_text(GTK_LABEL(labelArea), NULL);
|
|
gtk_label_set_text(GTK_LABEL(labelPerimeter), NULL);
|
|
}
|
|
}
|
|
|
|
void zoomIn(double factor, double x, double y) {
|
|
setScale(getScale()*factor);
|
|
updateScrollbars(x, y);
|
|
}
|
|
|
|
void zoomOut(double factor, double x, double y) {
|
|
setScale(getScale()/factor);
|
|
updateScrollbars(x, y);
|
|
}
|
|
|
|
void zoomInCentered(double factor) {
|
|
zoomIn(factor, 0.5, 0.5);
|
|
}
|
|
|
|
void zoomOutCentered(double factor) {
|
|
zoomOut(factor, 0.5, 0.5);
|
|
}
|