From 953a6ff48ac6c46a0d81bedb0fbdafe5d637666e Mon Sep 17 00:00:00 2001 From: neoraider Date: Mon, 24 Dec 2007 00:04:02 +0000 Subject: zoomedit: Use tools for event handling and rendering --- Drawer.cpp | 9 ++++---- Drawer.h | 4 ++-- EditManager.cpp | 68 ++++++++++-------------------------------------------- EditManager.h | 34 ++++++++------------------- EventHandler.h | 11 +++++++++ Renderer.cpp | 31 ++----------------------- Renderer.h | 6 ++++- SidebarManager.cpp | 17 ++++---------- SidebarManager.h | 11 +++++---- SidebarToolbox.h | 4 ++-- SidebarView.cpp | 31 +++++++++++++------------ SidebarView.h | 6 +++-- Tool.h | 11 +++++++-- ToolAddPolygon.cpp | 52 ++++++++++++++++++++++++++++++++++++++--- ToolAddPolygon.h | 26 +++++++++++++++++++-- ToolSelector.cpp | 14 ++++++++++- ToolSelector.h | 18 +++++++++++++-- Window.h | 5 ++++ 18 files changed, 196 insertions(+), 162 deletions(-) create mode 100644 EventHandler.h diff --git a/Drawer.cpp b/Drawer.cpp index 147103a..abe8e20 100644 --- a/Drawer.cpp +++ b/Drawer.cpp @@ -49,7 +49,7 @@ gboolean Drawer::eventHandler(GtkWidget *widget, GdkEvent *event, Drawer *drawer return TRUE; case GDK_BUTTON_PRESS: - drawer->window->getEditManager().buttonPress(event->button.button); + drawer->window->getActiveTool()->getEventHandler()->buttonPress(event->button.button); return TRUE; case GDK_SCROLL: @@ -168,6 +168,9 @@ void Drawer::render() { renderer.render(window->getLevel(), rect, scale); + if(window->getActiveTool()->getRenderer()) + window->getActiveTool()->getRenderer()->render(window->getLevel(), rect, scale); + glMatrixMode(GL_MODELVIEW); glPopMatrix(); @@ -175,9 +178,7 @@ void Drawer::render() { gdk_gl_drawable_gl_end(drawable); } -Drawer::Drawer(Window *window, GdkGLConfig *glconfig) - : renderer(&window->getEditManager()) -{ +Drawer::Drawer(Window *window, GdkGLConfig *glconfig) : renderer(&window->getEditManager()) { this->window = window; zoomExp = 0; scale = 100; diff --git a/Drawer.h b/Drawer.h index b471f1f..75039b2 100644 --- a/Drawer.h +++ b/Drawer.h @@ -16,10 +16,10 @@ class Drawer { GtkAdjustment *hAdjustment, *vAdjustment; int zoomExp; - Renderer renderer; - Window *window; + Renderer renderer; + float scale; float xCenter, yCenter; diff --git a/EditManager.cpp b/EditManager.cpp index bae061d..2d743f5 100644 --- a/EditManager.cpp +++ b/EditManager.cpp @@ -2,7 +2,7 @@ #include "Window.h" -bool EditManager::lineOk(const Line& l) const { +bool EditManager::lineOk(const Room &newRoom, const Line &l) const { Line l2; for(size_t i = 0; i+2 < newRoom.size(); i++) { @@ -30,10 +30,8 @@ bool EditManager::lineOk(const Line& l) const { EditManager::EditManager(Window *window) { this->window = window; - selectedObject = NULL; - mode = VIEW; - hoveredObject = NULL; + selectedObject = NULL; hasHoveredVertex = false; } @@ -42,40 +40,18 @@ void EditManager::redraw() { window->redraw(); } -void EditManager::addRoom() { - if(mode == VIEW) { - newRoom = Room(); - mode = ADD; +bool EditManager::addRoom(const Room &newRoom) { + if(newRoom.size() > 2 && polygonOk(newRoom)) { + Room *room = new Room(newRoom); + room->setName(idManager.generate("room")); - selectedObject = &newRoom; - - window->update(); - } -} - -void EditManager::finishRoom() { - if(mode == ADD) { - mode = VIEW; - - if(newRoom.size() > 2 && polygonOk(newRoom)) { - newRoom.setName(idManager.generate("room")); - window->getLevel().push_back(SharedPtr(new Room(newRoom))); - selectedObject = &*window->getLevel().back(); - } - else { - selectedObject = NULL; - } + window->getLevel().push_back(SharedPtr(room)); + selectedObject = &*window->getLevel().back(); - window->update(); + return true; } -} - -void EditManager::addVertex(const Vertex &v) { - if(mode != ADD) - return; - newRoom.push_back(v); - window->update(); + return false; } Vertex* EditManager::getHoveredVertex() { @@ -107,27 +83,7 @@ void EditManager::setHoveredVertex(Vertex *v) { window->update(); } -void EditManager::buttonPress(unsigned int button) { - switch(button) { - case 1: - if(!hasHoveredVertex) - break; - - switch(mode) { - case VIEW: - selectedObject = hoveredObject; - break; - - case ADD: - if(vertexOk(hoveredVertex)) - addVertex(hoveredVertex); - } - - window->update(); - } -} - -bool EditManager::vertexOk(const Vertex& v) const { +bool EditManager::vertexOk(const Room &newRoom, const Vertex &v) const { Line l; for(Level::iterator room = window->getLevel().begin(); room != window->getLevel().end(); room++) { @@ -140,7 +96,7 @@ bool EditManager::vertexOk(const Vertex& v) const { l.setVertex1(newRoom.back()); l.setVertex2(v); - return lineOk(l); + return lineOk(newRoom, l); } bool EditManager::polygonOk(const Polygon& polygon) const { diff --git a/EditManager.h b/EditManager.h index 8c0d86b..4413aeb 100644 --- a/EditManager.h +++ b/EditManager.h @@ -10,57 +10,43 @@ class Window; class EditManager { - public: - enum Mode { - VIEW, ADD - }; - private: - Mode mode; - IdManager idManager; Window *window; - Room newRoom; - LevelObject *selectedObject; - LevelObject *hoveredObject; + LevelObject *selectedObject; Vertex hoveredVertex; bool hasHoveredVertex; - bool lineOk(const Line& l) const; + bool lineOk(const Room &newRoom, const Line &l) const; public: EditManager(Window *window); void redraw(); - void addRoom(); - void finishRoom(); - - void addVertex(const Vertex &v); + LevelObject* getHoveredObject() { + return hoveredObject; + } - Mode getMode() const { - return mode; + void setSelectedObject(LevelObject *object) { + selectedObject = object; } LevelObject* getSelectedObject() { return selectedObject; } - LevelObject* getHoveredObject() { - return hoveredObject; - } + bool addRoom(const Room &room); Vertex* getHoveredVertex(); void setHoveredVertex(Vertex *v); - void buttonPress(unsigned int button); - - bool vertexOk(const Vertex& v) const; - bool polygonOk(const Polygon& polygon) const; + bool vertexOk(const Room &newRoom, const Vertex &v) const; + bool polygonOk(const Polygon &polygon) const; }; #endif /*EDITMANAGER_H_*/ diff --git a/EventHandler.h b/EventHandler.h new file mode 100644 index 0000000..7708341 --- /dev/null +++ b/EventHandler.h @@ -0,0 +1,11 @@ +#ifndef EVENTHANDLER_H_ +#define EVENTHANDLER_H_ + +class EventHandler { + public: + virtual ~EventHandler() {} + + virtual bool buttonPress(unsigned int button) = 0; +}; + +#endif /*EVENTHANDLER_H_*/ diff --git a/Renderer.cpp b/Renderer.cpp index 1c3255b..be00732 100644 --- a/Renderer.cpp +++ b/Renderer.cpp @@ -126,7 +126,7 @@ void Renderer::renderRoom(const Room &room, bool selected, bool hovered, float s glColor4f(1.0f, 1.0f, 1.0f, 0.9f); glLineWidth(2.0f); } - else if(hovered && editManager->getMode() == EditManager::VIEW) { + else if(hovered) { glColor4f(0.0f, 0.7f, 1.0f, 0.7f); glLineWidth(2.0f); } @@ -143,7 +143,7 @@ void Renderer::renderPlayerStart(const PlayerStart &start, bool selected, bool h glColor4f(1.0f, 1.0f, 1.0f, 0.9f); glLineWidth(2.0f); } - else if(hovered && editManager->getMode() == EditManager::VIEW) { + else if(hovered) { glColor4f(0.0f, 0.7f, 0.7f, 0.7f); glLineWidth(2.0f); } @@ -170,31 +170,4 @@ void Renderer::render(const Level &level, const Rectangle &rect, float scale) { renderObject(**object, (&**object == editManager->getSelectedObject()), (&**object == editManager->getHoveredObject()), scale); } - - if(editManager->getMode() == EditManager::ADD) { - Room *activeRoom = (Room*)editManager->getSelectedObject(); - - if(editManager->polygonOk(*activeRoom)) - glColor4f(0.0f, 0.7f, 1.0f, 0.2f); - else - glColor4f(1.0f, 0.3f, 0.3f, 0.2f); - - fillPolygon(*activeRoom); - - glLineWidth(2.0f); - glColor4f(0.0f, 0.7f, 1.0f, 0.7f); - drawPolygon(*activeRoom, false); - - if(!activeRoom->empty() && editManager->getHoveredVertex()) { - if(!editManager->vertexOk(*editManager->getHoveredVertex())) - glColor4f(1.0f, 0.3f, 0.3f, 0.7f); - - glBegin(GL_LINES); - - glVertex2f(activeRoom->back().getX(), activeRoom->back().getY()); - glVertex2f(editManager->getHoveredVertex()->getX(), editManager->getHoveredVertex()->getY()); - - glEnd(); - } - } } diff --git a/Renderer.h b/Renderer.h index 4c29b0f..1637904 100644 --- a/Renderer.h +++ b/Renderer.h @@ -14,6 +14,8 @@ class Renderer { EditManager *editManager; void drawGrid(const Rectangle &rect, float scale); + + protected: void fillPolygon(const Polygon &polygon); void drawPolygon(const Polygon &polygon, bool close = true); void drawCircle(const Vertex &m, float r, int n); @@ -29,7 +31,9 @@ class Renderer { this->editManager = editManager; } - void render(const Level &level, const Rectangle &rect, float scale); + virtual ~Renderer() {} + + virtual void render(const Level &level, const Rectangle &rect, float scale); }; #endif /*RENDERER_H_*/ diff --git a/SidebarManager.cpp b/SidebarManager.cpp index 0eb1f7b..393d748 100644 --- a/SidebarManager.cpp +++ b/SidebarManager.cpp @@ -3,10 +3,9 @@ SidebarManager::SidebarManager(Window *window) - : sidebarToolbox(window), sidebarView(&window->getEditManager()), - sidebarAdd(&window->getEditManager()), toolAddPolygon(&window->getEditManager()) + : sidebarToolbox(window), toolAddPolygon(&window->getEditManager()) { - this->editor = &window->getEditManager(); + this->window = window; activeSidebar = NULL; sidebar = gtk_vbox_new(FALSE, 0); @@ -16,7 +15,7 @@ SidebarManager::SidebarManager(Window *window) gtk_box_pack_start(GTK_BOX(sidebar), gtk_hseparator_new(), FALSE, FALSE, 5); - toolLabel = gtk_label_new(sidebarToolbox.getActiveTool()->getName()); + toolLabel = gtk_label_new(sidebarToolbox.getActiveTool().getName()); gtk_box_pack_start(GTK_BOX(sidebar), toolLabel, FALSE, FALSE, 0); @@ -40,13 +39,7 @@ SidebarManager::~SidebarManager() { void SidebarManager::update() { Sidebar *newSidebar = activeSidebar; - switch(editor->getMode()) { - case EditManager::VIEW: - newSidebar = &sidebarView; - break; - case EditManager::ADD: - newSidebar = &sidebarAdd; - } + newSidebar = window->getActiveTool()->getSidebar(); if(activeSidebar != newSidebar) { if(activeSidebar) @@ -57,7 +50,7 @@ void SidebarManager::update() { activeSidebar = newSidebar; } - gtk_label_set_text(GTK_LABEL(toolLabel), sidebarToolbox.getActiveTool()->getName()); + gtk_label_set_text(GTK_LABEL(toolLabel), sidebarToolbox.getActiveTool().getName()); sidebarToolbox.update(); activeSidebar->update(); diff --git a/SidebarManager.h b/SidebarManager.h index 90fc0cc..4401c9b 100644 --- a/SidebarManager.h +++ b/SidebarManager.h @@ -20,12 +20,9 @@ class SidebarManager { SidebarToolbox sidebarToolbox; - SidebarView sidebarView; - SidebarAdd sidebarAdd; - Sidebar *activeSidebar; - EditManager *editor; + Window *window; ToolAddPolygon toolAddPolygon; @@ -37,10 +34,14 @@ class SidebarManager { SidebarManager(Window *window); virtual ~SidebarManager(); - GtkWidget *getWidget() { + GtkWidget* getWidget() { return sidebar; } + Tool* getActiveTool() { + return &sidebarToolbox.getActiveTool(); + } + void update(); }; diff --git a/SidebarToolbox.h b/SidebarToolbox.h index 8e8a1f2..961df24 100644 --- a/SidebarToolbox.h +++ b/SidebarToolbox.h @@ -47,8 +47,8 @@ class SidebarToolbox : Sidebar { return widget; } - Tool *getActiveTool() { - return activeTool; + Tool& getActiveTool() { + return *activeTool; } void addTool(Tool *tool); diff --git a/SidebarView.cpp b/SidebarView.cpp index 99eae25..1246126 100644 --- a/SidebarView.cpp +++ b/SidebarView.cpp @@ -1,29 +1,30 @@ #include "SidebarView.h" #include "PlayerStart.h" +#include "ToolSelector.h" void SidebarView::spinButtonChanged(GtkWidget *spinbutton, SidebarView *view) { - if(!view->editor->getSelectedObject()) + if(!view->editManager->getSelectedObject()) return; - if(view->editor->getSelectedObject()->isOfType("Room")) { + if(view->editManager->getSelectedObject()->isOfType("Room")) { if(spinbutton == view->spinButtonHeight) - ((Room*)view->editor->getSelectedObject())->setHeight(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); + ((Room*)view->editManager->getSelectedObject())->setHeight(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); } - else if(view->editor->getSelectedObject()->isOfType("PlayerStart")) { + else if(view->editManager->getSelectedObject()->isOfType("PlayerStart")) { if(spinbutton == view->spinButtonX) - ((PlayerStart*)view->editor->getSelectedObject())->setX(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); + ((PlayerStart*)view->editManager->getSelectedObject())->setX(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); else if(spinbutton == view->spinButtonY) - ((PlayerStart*)view->editor->getSelectedObject())->setY(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); + ((PlayerStart*)view->editManager->getSelectedObject())->setY(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); else if(spinbutton == view->spinButtonZ) - ((PlayerStart*)view->editor->getSelectedObject())->setZ(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); + ((PlayerStart*)view->editManager->getSelectedObject())->setZ(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbutton))); } - view->editor->redraw(); + view->editManager->redraw(); } -SidebarView::SidebarView(EditManager *editor) { - this->editor = editor; +SidebarView::SidebarView(EditManager *editManager) { + this->editManager = editManager; sidebar = gtk_vbox_new(FALSE, 0); g_object_ref_sink(G_OBJECT(sidebar)); @@ -154,9 +155,9 @@ void SidebarView::update() { gtk_widget_hide(tableRoomData); gtk_widget_hide(tablePlayerStart); - if(editor->getSelectedObject()) { - if(editor->getSelectedObject()->isOfType("Room")) { - Room *room = (Room*)editor->getSelectedObject(); + if(editManager->getSelectedObject()) { + if(editManager->getSelectedObject()->isOfType("Room")) { + Room *room = (Room*)editManager->getSelectedObject(); gtk_label_set_markup(GTK_LABEL(labelType), "Room info:"); @@ -177,8 +178,8 @@ void SidebarView::update() { gtk_widget_show(tableRoomData); } - else if(editor->getSelectedObject()->isOfType("PlayerStart")) { - PlayerStart *playerStart = (PlayerStart*)editor->getSelectedObject(); + else if(editManager->getSelectedObject()->isOfType("PlayerStart")) { + PlayerStart *playerStart = (PlayerStart*)editManager->getSelectedObject(); gtk_label_set_markup(GTK_LABEL(labelType), "Player start:"); diff --git a/SidebarView.h b/SidebarView.h index 01cdcb9..e9c4c49 100644 --- a/SidebarView.h +++ b/SidebarView.h @@ -5,6 +5,8 @@ #include "EditManager.h" +class ToolSelector; + class SidebarView : public Sidebar { private: GtkWidget *sidebar; @@ -12,7 +14,7 @@ class SidebarView : public Sidebar { GtkWidget *tableRoomData, *labelArea, *labelPerimeter, *spinButtonHeight; GtkWidget *tablePlayerStart, *spinButtonX, *spinButtonY, *spinButtonZ; - EditManager *editor; + EditManager *editManager; // prevent shallow copy SidebarView(const SidebarView &w); @@ -21,7 +23,7 @@ class SidebarView : public Sidebar { static void spinButtonChanged(GtkWidget *spinbutton, SidebarView *view); public: - SidebarView(EditManager *editor); + SidebarView(EditManager *editManager); virtual ~SidebarView(); GtkWidget* getWidget(); diff --git a/Tool.h b/Tool.h index b1e3731..8cabfdf 100644 --- a/Tool.h +++ b/Tool.h @@ -2,6 +2,9 @@ #define TOOL_H_ #include "Object.h" +#include "EventHandler.h" +#include "Sidebar.h" +#include "Renderer.h" #include @@ -12,12 +15,16 @@ class Tool : public Object { virtual void activate() {}; virtual void deactivate() {}; - virtual const char *getName() const { + virtual const char* getName() const { return getType(); } - virtual GtkWidget *getImage() = 0; + virtual GtkWidget* getImage() = 0; virtual bool isSensitive() = 0; + + virtual EventHandler* getEventHandler() = 0; + virtual Sidebar* getSidebar() = 0; + virtual Renderer* getRenderer() {return NULL;} }; #endif /*TOOL_H_*/ diff --git a/ToolAddPolygon.cpp b/ToolAddPolygon.cpp index 37613bf..184d107 100644 --- a/ToolAddPolygon.cpp +++ b/ToolAddPolygon.cpp @@ -1,7 +1,8 @@ #include "ToolAddPolygon.h" +#include -ToolAddPolygon::ToolAddPolygon(EditManager *editManager) { +ToolAddPolygon::ToolAddPolygon(EditManager *editManager) : Renderer(editManager), sidebar(editManager) { this->editManager = editManager; image = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON); @@ -13,9 +14,54 @@ ToolAddPolygon::~ToolAddPolygon() { } void ToolAddPolygon::activate() { - editManager->addRoom(); + newRoom = Room(); + + editManager->setSelectedObject(NULL); } void ToolAddPolygon::deactivate() { - editManager->finishRoom(); + editManager->addRoom(newRoom); +} + +void ToolAddPolygon::render(const Level &level, const Rectangle &rect, float scale) { + if(editManager->polygonOk(newRoom)) + glColor4f(0.0f, 0.7f, 1.0f, 0.2f); + else + glColor4f(1.0f, 0.3f, 0.3f, 0.2f); + + fillPolygon(newRoom); + + glLineWidth(2.0f); + glColor4f(0.0f, 0.7f, 1.0f, 0.7f); + drawPolygon(newRoom, false); + + if(!newRoom.empty() && editManager->getHoveredVertex()) { + if(!editManager->vertexOk(newRoom, *editManager->getHoveredVertex())) + glColor4f(1.0f, 0.3f, 0.3f, 0.7f); + + glBegin(GL_LINES); + + glVertex2f(newRoom.back().getX(), newRoom.back().getY()); + glVertex2f(editManager->getHoveredVertex()->getX(), editManager->getHoveredVertex()->getY()); + + glEnd(); + } +} + +bool ToolAddPolygon::buttonPress(unsigned int button) { + if(button != 1) + return false; + + if(!editManager->getHoveredVertex()) + return false; + + if(!editManager->vertexOk(newRoom, *editManager->getHoveredVertex())) + return false; + + newRoom.push_back(*editManager->getHoveredVertex()); + + editManager->redraw(); + sidebar.update(); + + return true; } diff --git a/ToolAddPolygon.h b/ToolAddPolygon.h index b3c7856..69a0e07 100644 --- a/ToolAddPolygon.h +++ b/ToolAddPolygon.h @@ -3,13 +3,19 @@ #include "Tool.h" #include "EditManager.h" +#include "SidebarAdd.h" -class ToolAddPolygon : public Tool { + +class ToolAddPolygon : public Tool, public EventHandler, public Renderer { private: GtkWidget *image; EditManager *editManager; + SidebarAdd sidebar; + + Room newRoom; + // prevent shallow copy ToolAddPolygon(const ToolAddPolygon &t); const ToolAddPolygon& operator=(const ToolAddPolygon &t); @@ -29,13 +35,29 @@ class ToolAddPolygon : public Tool { return "Add polygonal room"; } - bool isSensitive() { + virtual bool isSensitive() { return TRUE; } virtual GtkWidget *getImage() { return image; } + + virtual EventHandler* getEventHandler() { + return this; + } + + virtual Renderer *getRenderer() { + return this; + } + + virtual void render(const Level &level, const Rectangle &rect, float scale); + + virtual bool buttonPress(unsigned int button); + + virtual Sidebar* getSidebar() { + return &sidebar; + } }; #endif /*TOOLADDPOLYGON_H_*/ diff --git a/ToolSelector.cpp b/ToolSelector.cpp index 2b61dee..46ee6d2 100644 --- a/ToolSelector.cpp +++ b/ToolSelector.cpp @@ -1,7 +1,7 @@ #include "ToolSelector.h" -ToolSelector::ToolSelector(EditManager *editManager) { +ToolSelector::ToolSelector(EditManager *editManager) : sidebar(editManager) { this->editManager = editManager; image = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON); @@ -11,3 +11,15 @@ ToolSelector::ToolSelector(EditManager *editManager) { ToolSelector::~ToolSelector() { g_object_unref(G_OBJECT(image)); } + +bool ToolSelector::buttonPress(unsigned int button) { + if(button != 1) + return false; + + editManager->setSelectedObject(editManager->getHoveredObject()); + + editManager->redraw(); + sidebar.update(); + + return true; +} diff --git a/ToolSelector.h b/ToolSelector.h index b88c861..5ba2bcd 100644 --- a/ToolSelector.h +++ b/ToolSelector.h @@ -3,13 +3,17 @@ #include "Tool.h" #include "EditManager.h" +#include "SidebarView.h" -class ToolSelector : public Tool { + +class ToolSelector : public Tool, public EventHandler { private: GtkWidget *image; EditManager *editManager; + SidebarView sidebar; + // prevent shallow copy ToolSelector(const ToolSelector &t); const ToolSelector& operator=(const ToolSelector &t); @@ -26,13 +30,23 @@ class ToolSelector : public Tool { return "Select"; } - bool isSensitive() { + virtual bool isSensitive() { return TRUE; } virtual GtkWidget *getImage() { return image; } + + virtual EventHandler *getEventHandler() { + return this; + } + + virtual bool buttonPress(unsigned int button); + + virtual Sidebar* getSidebar() { + return &sidebar; + } }; #endif /*TOOLSELECTOR_H_*/ diff --git a/Window.h b/Window.h index 2e75f13..c8fa469 100644 --- a/Window.h +++ b/Window.h @@ -36,6 +36,7 @@ class Window { const Window& operator=(const Window &w); static gboolean deleteEvent(GtkWidget *widget, GdkEvent *event, Window *window); + public: Window(GdkGLConfig *glconfig, WindowManager *manager); virtual ~Window(); @@ -48,6 +49,10 @@ class Window { drawer.update(); } + Tool* getActiveTool() { + return sidebar.getActiveTool(); + } + EditManager& getEditManager() { return editor; } -- cgit v1.2.3