#include "Drawer.h" #include "Window.h" #include #include void Drawer::realize(GtkWidget *widget, Drawer *drawer) { GdkGLContext *context = gtk_widget_get_gl_context(widget); GdkGLDrawable *drawable = gtk_widget_get_gl_drawable(widget); if(!gdk_gl_drawable_gl_begin(drawable, context)) return; glClearColor(0, 0, 0, 1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gdk_gl_drawable_gl_end(drawable); } gboolean Drawer::eventHandler(GtkWidget *widget, GdkEvent *event, Drawer *drawer) { switch(event->type) { case GDK_CONFIGURE: drawer->updateViewport(); drawer->updateScrollbars(); return TRUE; case GDK_EXPOSE: drawer->render(); return TRUE; case GDK_MOTION_NOTIFY: drawer->updateHoveredPoint(event->motion.x, event->motion.y); drawer->window->getActiveTool()->getEventHandler()->motion(); return TRUE; case GDK_ENTER_NOTIFY: drawer->updateHoveredPoint(event->crossing.x, event->crossing.y); drawer->window->getActiveTool()->getEventHandler()->motion(); return TRUE; case GDK_LEAVE_NOTIFY: drawer->window->getEditManager().setHoveredVertex(NULL); drawer->window->getActiveTool()->getEventHandler()->motion(); return TRUE; case GDK_BUTTON_PRESS: drawer->window->getActiveTool()->getEventHandler()->buttonPress(event->button.button); return TRUE; case GDK_BUTTON_RELEASE: drawer->window->getActiveTool()->getEventHandler()->buttonRelease(event->button.button); return TRUE; case GDK_SCROLL: switch(event->scroll.direction) { case GDK_SCROLL_UP: drawer->zoom(1, event->scroll.x/widget->allocation.width, event->scroll.y/widget->allocation.height); break; case GDK_SCROLL_DOWN: drawer->zoom(-1, event->scroll.x/widget->allocation.width, event->scroll.y/widget->allocation.height); break; default: return FALSE; } return TRUE; default: return FALSE; } } void Drawer::valueChanged(GtkAdjustment *adjustment, Drawer *drawer) { drawer->updateScrolling(); } void Drawer::updateViewport() { GdkGLContext *context = gtk_widget_get_gl_context(drawingArea); GdkGLDrawable *drawable = gtk_widget_get_gl_drawable(drawingArea); if(!gdk_gl_drawable_gl_begin(drawable, context)) return; glViewport(0, 0, getWidth(), getHeight()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(getWidth() != 0 && getHeight() != 0) glScalef(2.0f/getWidth(), -2.0f/getHeight(), 1); gdk_gl_drawable_gl_end(drawable); } void Drawer::updateScrolling() { if((getImageWidth())*scale < getWidth()) xCenter = 0; else xCenter = gtk_adjustment_get_value(hAdjustment); if((getImageHeight())*scale < getHeight()) yCenter = 0; else yCenter = gtk_adjustment_get_value(vAdjustment); gtk_widget_queue_draw(drawingArea); } void Drawer::updateScrollbars(float x, float y) { const gdouble imageWidth = getImageWidth(), imageHeight = getImageHeight(); const gdouble minX = -imageWidth/2, maxX = imageWidth/2; const gdouble minY = -imageHeight/2, maxY = imageHeight/2; const gdouble width = getWidth()/scale, height = getHeight()/scale; gdouble lower, upper, pageSize, value; gdk_window_freeze_updates(drawingArea->window); g_object_get(G_OBJECT(hAdjustment), "lower", &lower, "upper", &upper, "page_size", &pageSize, NULL); g_object_set(G_OBJECT(hAdjustment), "lower", minX + width/2, "upper", maxX + width/2, "page_size", width, NULL); gtk_adjustment_changed(hAdjustment); if(pageSize > (upper-lower) && width < imageWidth) value = 0; else value = gtk_adjustment_get_value(hAdjustment) + (x-0.5)*pageSize*(pageSize/width-1); gtk_adjustment_set_value(hAdjustment, MAX(MIN(value, maxX - width/2), minX + width/2)); g_object_get(G_OBJECT(vAdjustment), "lower", &lower, "upper", &upper, "page_size", &pageSize, NULL); g_object_set(G_OBJECT(vAdjustment), "lower", minY + height/2, "upper", maxY + height/2, "page_size", height, NULL); gtk_adjustment_changed(vAdjustment); if(pageSize > (upper-lower) && height < imageHeight) value = 0; else value = gtk_adjustment_get_value(vAdjustment) + (y-0.5)*pageSize*(pageSize/height-1); gtk_adjustment_set_value(vAdjustment, MAX(MIN(value, maxY - height/2), minY + height/2)); gdk_window_thaw_updates(drawingArea->window); updateScrolling(); } void Drawer::updateHoveredPoint(float x, float y) { Vertex v(x, y); viewToImage(&v); window->getEditManager().setHoveredVertex(&v); } void Drawer::render() { GdkGLContext *context = gtk_widget_get_gl_context(drawingArea); GdkGLDrawable *drawable = gtk_widget_get_gl_drawable(drawingArea); Rectangle rect(0, 0, drawingArea->allocation.width, drawingArea->allocation.height); viewToImage(&rect.getVertex1()); viewToImage(&rect.getVertex2()); if(!gdk_gl_drawable_gl_begin(drawable, context)) return; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glScalef(scale, scale, 1); glTranslatef(-xCenter, -yCenter, 0); renderer.render(window->getLevel(), rect, scale); if(window->getActiveTool()->getRenderer()) window->getActiveTool()->getRenderer()->render(window->getLevel(), rect, scale); glMatrixMode(GL_MODELVIEW); glPopMatrix(); gdk_gl_drawable_swap_buffers(drawable); gdk_gl_drawable_gl_end(drawable); } Drawer::Drawer(Window *window, GdkGLConfig *glconfig) : renderer(&window->getEditManager()) { this->window = window; zoomExp = 0; scale = 100; xCenter = 0; yCenter = 0; drawer = gtk_table_new(2, 2, FALSE); g_object_ref_sink(G_OBJECT(drawer)); drawingArea = gtk_drawing_area_new(); gtk_widget_set_gl_capability(drawingArea, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE); g_signal_connect(G_OBJECT(drawingArea), "realize", G_CALLBACK(realize), this); g_signal_connect(G_OBJECT(drawingArea), "configure-event", G_CALLBACK(eventHandler), this); g_signal_connect(G_OBJECT(drawingArea), "expose-event", G_CALLBACK(eventHandler), this); g_signal_connect(G_OBJECT(drawingArea), "button-press-event", G_CALLBACK(eventHandler), this); g_signal_connect(G_OBJECT(drawingArea), "button-release-event", G_CALLBACK(eventHandler), this); g_signal_connect(G_OBJECT(drawingArea), "enter-notify-event", G_CALLBACK(eventHandler), this); g_signal_connect(G_OBJECT(drawingArea), "leave-notify-event", G_CALLBACK(eventHandler), this); g_signal_connect(G_OBJECT(drawingArea), "motion-notify-event", G_CALLBACK(eventHandler), this); g_signal_connect(G_OBJECT(drawingArea), "scroll-event", G_CALLBACK(eventHandler), this); gtk_widget_add_events(drawingArea, GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_RELEASE_MASK); gtk_table_attach(GTK_TABLE(drawer), 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)); GtkWidget *hScroll = gtk_hscrollbar_new(hAdjustment); g_signal_connect(G_OBJECT(hScroll), "value-changed", G_CALLBACK(valueChanged), this); gtk_table_attach(GTK_TABLE(drawer), 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)); GtkWidget *vScroll = gtk_vscrollbar_new(vAdjustment); g_signal_connect(G_OBJECT(vScroll), "value-changed", G_CALLBACK(valueChanged), this); gtk_table_attach(GTK_TABLE(drawer), vScroll, 1, 2, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)(GTK_FILL|GTK_EXPAND|GTK_SHRINK), 0, 0); gtk_widget_show_all(drawer); } Drawer::~Drawer() { g_object_unref(G_OBJECT(drawer)); } void Drawer::zoom(int zoom, float x, float y) { zoomExp = MAX(MIN(zoomExp + zoom, 50), -100); scale = 100*powf(1.1f, zoomExp); updateScrollbars(x, y); } void Drawer::imageToView(Vertex *v) const { v->setX((v->getX()-xCenter)*scale + getWidth()/2); v->setY((v->getY()-yCenter)*scale + getHeight()/2); } void Drawer::viewToImage(Vertex *v) const { v->setX((v->getX()-getWidth()/2)/scale+xCenter); v->setY((v->getY()-getHeight()/2)/scale+yCenter); } float Drawer::getImageWidth() const { float max = 0; for(Level::iterator object = window->getLevel().begin(); object != window->getLevel().end(); object++) { if((*object)->isOfType("Room")) { Room *room = (Room*)&**object; for(Room::iterator v = room->begin(); v != room->end(); v++) { max = fmaxf(max, fabsf(v->getX())); } } } return (2*max+1); } float Drawer::getImageHeight() const { float max = 0; for(Level::iterator object = window->getLevel().begin(); object != window->getLevel().end(); object++) { if((*object)->isOfType("Room")) { Room *room = (Room*)&**object; for(Room::iterator v = room->begin(); v != room->end(); v++) { max = fmaxf(max, fabsf(v->getY())); } } } return (2*max+1); }