diff options
Diffstat (limited to 'window.cpp')
-rw-r--r-- | window.cpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/window.cpp b/window.cpp new file mode 100644 index 0000000..39c7ebc --- /dev/null +++ b/window.cpp @@ -0,0 +1,367 @@ +#include "window.h" +#include "ui.h" +#include "edit.h" +#include "draw.h" +#include "geometry.h" +#include "level.h" +#include <gtk/gtk.h> +#include <stdlib.h> +#include <string.h> + + +static GtkWidget *drawingArea = NULL, *sidebarView = NULL, *sidebarAdd = NULL; +static GtkAdjustment *hAdjustment = NULL, *vAdjustment = NULL; + +static GtkWidget *entryName, *labelArea = NULL, *labelPerimeter = NULL, *buttonAdd = NULL, *buttonAddDo = 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}; + + switch(event->type) { + case GDK_BUTTON_PRESS: + switch(event->button) { + case 1: + switch(getEditMode()) { + case EDIT_MODE_VIEW: + case EDIT_MODE_SELECTED: + setActiveRoom(getHoveredRoom()); + + updateSidebar(); + + redraw(); + + break; + case EDIT_MODE_ADD: + viewToImage(&v); + + if(isVertexOk(&v)) { + addVertex(&getActiveRoom()->polygon, &v); + updateSidebar(); + } + } + + gtk_widget_queue_draw(drawingArea); + } + } + + return FALSE; +} + +gboolean crossingNotifyEvent(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) { + VERTEX v; + + + switch(event->type) { + case GDK_ENTER_NOTIFY: + v.x = event->x; + v.y = event->y; + + viewToImage(&v); + + setHoveredVertex(&v); + + break; + case GDK_LEAVE_NOTIFY: + setHoveredVertex(NULL); + } + + + gtk_widget_queue_draw(drawingArea); +} + +gboolean motionNotifyEvent(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) { + VERTEX v; + ROOM *last = getHoveredRoom(); + + v.x = event->x; + v.y = event->y; + + viewToImage(&v); + + setHoveredVertex(&v); + + if(getHoveredRoom() != last || getEditMode() == EDIT_MODE_ADD) + gtk_widget_queue_draw(drawingArea); +} + +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 void sidebarButtonClicked(GtkButton *button, gpointer user_data) { + if(button == GTK_BUTTON(buttonAdd)) { + startAddMode(); + updateSidebar(); + + gtk_widget_queue_draw(drawingArea); + } + else if(button == GTK_BUTTON(buttonAddDo)) { + if(getActiveRoom() &&getActiveRoom()->polygon.nVertices > 2 && isPolygonOk(&getActiveRoom()->polygon)) { + addRoom(getLevel(), getActiveRoom()); + setActiveRoom(&getLevel()->rooms[getLevel()->nRooms-1]); + } + + endAddMode(); + updateSidebar(); + + gtk_widget_queue_draw(drawingArea); + } +} + +static GtkWidget* createSidebar() { + GtkWidget *sidebar, *labelRoomInfo, *labelName; + GtkWidget *tableRoomData, *labelAreaLabel, *labelPerimeterLabel; + + sidebar = gtk_hbox_new(FALSE, 0); + + // View sidebar + sidebarView = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(sidebar), sidebarView, FALSE, FALSE, 5); + + 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(sidebarView), labelRoomInfo, FALSE, FALSE, 5); + + gtk_box_pack_start(GTK_BOX(sidebarView), 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(sidebarView), 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(sidebarView), 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(sidebarView), 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); + + buttonAdd = gtk_button_new_with_label("Add room"); + g_signal_connect(G_OBJECT(buttonAdd), "clicked", G_CALLBACK(sidebarButtonClicked), NULL); + gtk_box_pack_end(GTK_BOX(sidebarView), buttonAdd, FALSE, FALSE, 0); + + // Add sidebar + sidebarAdd = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(sidebar), sidebarAdd, FALSE, FALSE, 5); + + labelRoomInfo = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(labelRoomInfo), "<b>Add room</b>"); + gtk_misc_set_alignment(GTK_MISC(labelRoomInfo), 0.0, 0.5); + gtk_box_pack_start(GTK_BOX(sidebarAdd), labelRoomInfo, FALSE, FALSE, 5); + + buttonAddDo = gtk_button_new_with_label("Add room"); + g_signal_connect(G_OBJECT(buttonAddDo), "clicked", G_CALLBACK(sidebarButtonClicked), NULL); + gtk_box_pack_end(GTK_BOX(sidebarAdd), buttonAddDo, FALSE, FALSE, 0); + + return sidebar; +} + +GtkWidget* createMainWindow() { + GtkWidget *window, *hPaned, *vbox, *table, *vScroll, *hScroll, *sidebar; + 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, (GtkStateType)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_table_attach(GTK_TABLE(table), drawingArea, 0, 1, 0, 1, (GtkAttachOptions)(GTK_FILL|GTK_EXPAND|GTK_SHRINK), (GtkAttachOptions)(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, (GtkAttachOptions)(GTK_FILL|GTK_EXPAND|GTK_SHRINK), (GtkAttachOptions)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, (GtkAttachOptions)0, (GtkAttachOptions)(GTK_FILL|GTK_EXPAND|GTK_SHRINK), 0, 0); + + sidebar = createSidebar(); + gtk_paned_pack2(GTK_PANED(hPaned), sidebar, FALSE, TRUE); + + gtk_widget_show_all(vbox); + gtk_widget_hide(sidebarAdd); + + return window; +} + +void updateSidebar() { + gchar *string; + + switch(getEditMode()) { + case EDIT_MODE_VIEW: + case EDIT_MODE_SELECTED: + gtk_widget_hide(sidebarAdd); + gtk_widget_show(sidebarView); + + 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); + } + + break; + case EDIT_MODE_ADD: + gtk_widget_hide(sidebarView); + gtk_widget_show(sidebarAdd); + + if(getActiveRoom()) { + if(getActiveRoom()->polygon.nVertices > 2 && isPolygonOk(&getActiveRoom()->polygon)) + gtk_widget_set_sensitive(buttonAddDo, TRUE); + else + gtk_widget_set_sensitive(buttonAddDo, FALSE); + } + } +} + +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); +} |