🢀 ircpaint :: 25e77ee


commit 25e77ee660f6a6a114e48a1abed542ee7220ea41
Author: acidvegas <acid.vegas@acid.vegas>
Date:   Mon Jun 24 02:34:06 2019 -0400

    Initial commit

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5523887
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+###### Requirements
+* QtCore4.dll
+* QtGui4.dll
+* Microsoft Visual C++ 2010 Redistributable
+
+###### Mirrors
+- [acid.vegas](https://acid.vegas/ircpaint) *(main)*
+- [SuperNETs](https://git.supernets.org/ircart/ircpaint)
+- [GitHub](https://github.com/ircart/ircpaint)
+- [GitLab](https://gitlab.com/ircart/ircpaint)
+
+###### Credits
+* Pedrobear/[wwared](https://github.com/wwared)
diff --git a/ircpaint/BFill_Command.cpp b/ircpaint/BFill_Command.cpp
new file mode 100644
index 0000000..4590f4f
--- /dev/null
+++ b/ircpaint/BFill_Command.cpp
@@ -0,0 +1,40 @@
+#include <QtGui>
+
+#include "BFill_Command.h"
+#include "MainWidget.h"
+
+void BFill_Command::undo() {
+    foreach (cell c, changed) {
+        img->setPixel(c.first, c.second);
+        widget->update(widget->pixelRect(c.first));
+    }
+}
+
+void BFill_Command::redo() {
+    changed.clear();
+    pixels.clear();
+    pixels << QPoint(x, y);
+    for (int i = 0; i < pixels.size(); ++i) {
+        if (img->pixel(pixels[i]) != from)
+            continue;
+        int fy = pixels[i].y();
+        for (int fx = pixels[i].x(); fx < img->width() && img->pixel(fx, fy) == from; ++fx) {
+            changed << std::make_pair(QPoint(fx, fy), img->pixel(fx, fy));
+            img->setPixel(fx, fy, to);
+            widget->update(widget->pixelRect(fx, fy));
+            if (fy < img->height()-1 && img->pixel(fx, fy+1) == from)
+                pixels << QPoint(fx, fy+1);
+            if (fy > 0 && img->pixel(fx, fy-1) == from)
+                pixels << QPoint(fx, fy-1);
+        }
+        for (int fx = pixels[i].x()-1; fx >= 0 && img->pixel(fx, fy) == from; --fx) {
+            changed << std::make_pair(QPoint(fx, fy), img->pixel(fx, fy));
+            img->setPixel(fx, fy, to);
+            widget->update(widget->pixelRect(fx, fy));
+            if (fy < img->height()-1 && img->pixel(fx, fy+1) == from)
+                pixels << QPoint(fx, fy+1);
+            if (fy > 0 && img->pixel(fx, fy-1) == from)
+                pixels << QPoint(fx, fy-1);
+        }
+    }
+}
diff --git a/ircpaint/BFill_Command.h b/ircpaint/BFill_Command.h
new file mode 100644
index 0000000..3cbad5d
--- /dev/null
+++ b/ircpaint/BFill_Command.h
@@ -0,0 +1,29 @@
+#ifndef BFILL_COMMAND_H
+#define BFILL_COMMAND_H
+
+#include <utility>
+
+#include <QUndoCommand>
+#include <QColor>
+#include <QList>
+#include <QPoint>
+#include <QRgb>
+
+class QImage;
+class MainWidget;
+
+class BFill_Command : public QUndoCommand {
+    int x, y;
+    QRgb from, to;
+    typedef std::pair<QPoint, QRgb> cell;
+    QList<cell> changed;
+    QList<QPoint> pixels;
+    QImage* img;
+    MainWidget* widget;
+public:
+    BFill_Command(MainWidget* w, QImage* i, QRgb f, QRgb t, int xx, int yy) : x(xx), y(yy), from(f), to(t), img(i), widget(w) { }
+    void undo();
+    void redo();
+};
+
+#endif // BFILL_COMMAND_H
diff --git a/ircpaint/BLine_Command.cpp b/ircpaint/BLine_Command.cpp
new file mode 100644
index 0000000..fbf4602
--- /dev/null
+++ b/ircpaint/BLine_Command.cpp
@@ -0,0 +1,30 @@
+#include <QtGui>
+
+#include "BLine_Command.h"
+#include "MainWidget.h"
+
+void BLine_Command::undo() {
+    QPainter p(img);
+    p.drawImage(qMin(xstart, xend), qMin(ystart, yend), changed);
+    p.end();
+    if (other) {
+        p.begin(other);
+        p.drawImage(qMin(xstart, xend), qMin(ystart, yend), *otherChanged);
+        p.end();
+    }
+    widget->update(widget->pixelRect(xstart,ystart).united(widget->pixelRect(xend, yend)));
+}
+
+void BLine_Command::redo() {
+    QPainter p(img);
+    p.setPen(col);
+    p.drawLine(xstart,ystart,xend,yend);
+    p.end();
+    if (other) {
+        p.begin(other);
+        p.setPen(col);
+        p.drawLine(xstart,ystart,xend,yend);
+        p.end();
+    }
+    widget->update(widget->pixelRect(xstart,ystart).united(widget->pixelRect(xend, yend)).adjusted(-3, -3, 3, 3));
+}
diff --git a/ircpaint/BLine_Command.h b/ircpaint/BLine_Command.h
new file mode 100644
index 0000000..cb9ebde
--- /dev/null
+++ b/ircpaint/BLine_Command.h
@@ -0,0 +1,48 @@
+#ifndef BLINE_COMMAND_H
+#define BLINE_COMMAND_H
+
+#include <utility>
+#include <cstdlib>
+
+#include <QUndoCommand>
+#include <QColor>
+#include <QImage>
+
+class MainWidget;
+
+/* Note to self:
+ * Due to your own stupidity, this class
+ * keeps a huge rectangle of the changed
+ * area in memory.
+ * This creates a problem when color
+ * changing is added to the preferences.
+ * Either fix that or just clear the undo
+ * stack if the user changes preferences
+ */
+
+class BLine_Command : public QUndoCommand {
+    int xstart, ystart, xend, yend;
+    QColor col;
+    QImage* img;
+    QImage* other;
+    MainWidget* widget;
+    QImage changed;
+    QImage* otherChanged; // only used if other is not NULL
+public:
+    BLine_Command(MainWidget* w, QImage* i, QImage* o, QColor c, int xs, int ys, int xe, int ye) : xstart(xs), ystart(ys), xend(xe), yend(ye), col(c), img(i), other(o), widget(w),
+                                                                                        changed(i->copy(qMin(xs, xe), qMin(ys, ye), std::abs(xe-xs)+1, std::abs(ye-ys)+1))
+    {
+        if (o) {
+            otherChanged = new QImage(o->copy(qMin(xs, xe), qMin(ys, ye), std::abs(xe-xs)+1, std::abs(ye-ys)+1));
+        } else {
+            otherChanged = NULL;
+        }
+    }
+    ~BLine_Command() {
+        delete otherChanged;
+    }
+    void undo();
+    void redo();
+};
+
+#endif // BLINE_COMMAND_H
diff --git a/ircpaint/BPen_Command.cpp b/ircpaint/BPen_Command.cpp
new file mode 100644
index 0000000..93a1038
--- /dev/null
+++ b/ircpaint/BPen_Command.cpp
@@ -0,0 +1,57 @@
+#include <QtGui>
+
+#include "BPen_Command.h"
+
+#include "MainWidget.h"
+
+BPen_Command::BPen_Command(MainWidget* w, QImage* i, QImage* o, int id, int x, int y, QRgb c) : curid(id), widget(w), img(i), other(o) {
+    cells << std::make_pair(QPoint(x, y), c);
+}
+
+void BPen_Command::undo() {
+    foreach (cell c, changed) {
+        img->setPixel(c.first, c.second);
+        widget->update(widget->pixelRect(c.first));
+    }
+    if (other) {
+        foreach (cell c, otherChanged) {
+            other->setPixel(c.first, c.second);
+            widget->update(widget->pixelRect(c.first));
+        }
+    }
+}
+
+void BPen_Command::redo() {
+    changed.clear();
+    otherChanged.clear();
+    foreach (cell c, cells) {
+        changed << std::make_pair(c.first, img->pixel(c.first));
+        img->setPixel(c.first, c.second);
+        if (other) {
+            otherChanged << std::make_pair(c.first, other->pixel(c.first));
+            other->setPixel(c.first, c.second);
+        }
+        widget->update(widget->pixelRect(c.first));
+    }
+}
+
+bool BPen_Command::mergeWith(const QUndoCommand* other) {
+    BPen_Command* cmd = dynamic_cast<BPen_Command*>(const_cast<QUndoCommand*>(other));
+    if (cells.size() > 500 || cmd->id() != id() || !cmd)
+        return false;
+    foreach (cell c, cmd->cells) {
+        foreach (cell h, cells) {
+            if (c.first == h.first) {
+                if (c.second == h.second)
+                    return true;
+                return false;
+            }
+        }
+        cells << c;
+    }
+    foreach (cell c, cmd->changed)
+        changed << c;
+    foreach (cell c, cmd->otherChanged)
+        otherChanged << c;
+    return true;
+}
diff --git a/ircpaint/BPen_Command.h b/ircpaint/BPen_Command.h
new file mode 100644
index 0000000..d22797c
--- /dev/null
+++ b/ircpaint/BPen_Command.h
@@ -0,0 +1,29 @@
+#ifndef BPEN_COMMAND_H
+#define BPEN_COMMAND_H
+
+#include <QUndoCommand>
+#include <QPoint>
+#include <QRgb>
+
+class MainWidget;
+class QImage;
+
+class BPen_Command : public QUndoCommand {
+    typedef std::pair<QPoint, QRgb> cell;
+    int curid;
+    MainWidget* widget;
+    QImage* img;
+    QImage* other;
+public:
+    QList<cell> cells;
+    QList<cell> changed;
+    QList<cell> otherChanged; // only used if other is not NULL
+
+    BPen_Command(MainWidget* w, QImage* i, QImage* o, int id, int x, int y, QRgb c);
+    void undo();
+    void redo();
+    bool mergeWith(const QUndoCommand* other);
+    int id() const { return curid; }
+};
+
+#endif // BPEN_COMMAND_H
diff --git a/ircpaint/BRect_Command.cpp b/ircpaint/BRect_Command.cpp
new file mode 100644
index 0000000..7e46e48
--- /dev/null
+++ b/ircpaint/BRect_Command.cpp
@@ -0,0 +1,56 @@
+#include <cstdlib>
+
+#include <QtGui>
+
+#include "BRect_Command.h"
+#include "MainWidget.h"
+
+void BRect_Command::undo() {
+    foreach (cell c, changed) {
+        img->setPixel(c.first, c.second);
+        widget->update(widget->pixelRect(c.first));
+    }
+    if (other) {
+        foreach (cell c, otherChanged) {
+            other->setPixel(c.first, c.second);
+            widget->update(widget->pixelRect(c.first));
+        }
+    }
+}
+
+void BRect_Command::redo() {
+    changed.clear();
+    otherChanged.clear();
+    int xl, xr, yb, yt;
+    if (xstart < xend) {
+        xl = xstart; xr = xend;
+    } else {
+        xl = xend; xr = xstart;
+    }
+    if (ystart < yend) {
+        yt = ystart; yb = yend;
+    } else {
+        yt = yend; yb = ystart;
+    }
+    for (int i = xl; i <= xr; ++i) {
+        changed << std::make_pair(QPoint(i, yt), img->pixel(i, yt))
+                << std::make_pair(QPoint(i, yb), img->pixel(i, yb));
+        img->setPixel(i, yt, col); img->setPixel(i, yb, col);
+        if (other) {
+            otherChanged << std::make_pair(QPoint(i, yt), other->pixel(i, yt))
+                         << std::make_pair(QPoint(i, yb), other->pixel(i, yb));
+            other->setPixel(i, yt, col); other->setPixel(i, yb, col);
+        }
+    }
+    for (int i = yt+1; i < yb; ++i) {
+        changed << std::make_pair(QPoint(xl, i), img->pixel(xl, i))
+                << std::make_pair(QPoint(xr, i), img->pixel(xr, i));
+        img->setPixel(xl, i, col); img->setPixel(xr, i, col);
+        if (other) {
+            otherChanged << std::make_pair(QPoint(xl, i), other->pixel(xl, i))
+                         << std::make_pair(QPoint(xr, i), other->pixel(xr, i));
+            other->setPixel(xl, i, col); other->setPixel(xr, i, col);
+        }
+    }
+    widget->update(widget->pixelRect(xstart, ystart).united(widget->pixelRect(xend, yend)).adjusted(-3, -3, 3, 3));
+}
diff --git a/ircpaint/BRect_Command.h b/ircpaint/BRect_Command.h
new file mode 100644
index 0000000..46fcd69
--- /dev/null
+++ b/ircpaint/BRect_Command.h
@@ -0,0 +1,29 @@
+#ifndef BRECT_COMMAND_H
+#define BRECT_COMMAND_H
+
+#include <utility>
+
+#include <QUndoCommand>
+#include <QRgb>
+#include <QPoint>
+#include <QList>
+
+class QImage;
+class MainWidget;
+
+class BRect_Command : public QUndoCommand {
+    int xstart, ystart, xend, yend;
+    QRgb col;
+    QImage* img;
+    QImage* other;
+    MainWidget* widget;
+    typedef std::pair<QPoint, QRgb> cell;
+    QList<cell> changed;
+    QList<cell> otherChanged; // only used if other is not NULL
+public:
+    BRect_Command(MainWidget* w, QImage* i, QImage* o, QRgb c, int xs, int ys, int xe, int ye) : xstart(xs), ystart(ys), xend(xe), yend(ye), col(c), img(i), other(o), widget(w) { }
+    void undo();
+    void redo();
+};
+
+#endif // BRECT_COMMAND_H
diff --git a/ircpaint/Brush.cpp b/ircpaint/Brush.cpp
new file mode 100644
index 0000000..87fa284
--- /dev/null
+++ b/ircpaint/Brush.cpp
@@ -0,0 +1,17 @@
+#include "Brush.h"
+
+void Brush::onWidgetPaint(QPaintEvent *, QPainter&) {
+    return;
+}
+
+bool Brush::onMouseClick(QMouseEvent *, int , int, bool) {
+    return false;
+}
+
+bool Brush::onMouseRelease(QMouseEvent *, int , int, bool) {
+    return false;
+}
+
+bool Brush::onMouseMove(QMouseEvent *, int , int, bool) {
+    return false;
+}
diff --git a/ircpaint/Brush.h b/ircpaint/Brush.h
new file mode 100644
index 0000000..a3afcba
--- /dev/null
+++ b/ircpaint/Brush.h
@@ -0,0 +1,21 @@
+#ifndef BRUSH_H
+#define BRUSH_H
+
+class MainWidget;
+class QMouseEvent;
+class QPaintEvent;
+class QPainter;
+
+class Brush {
+protected:
+    MainWidget* widget;
+public:
+    Brush(MainWidget* w) : widget(w) {} // the x and y coordinates below are ascii coordinates, [0,xasc) and [0,yasc)
+    virtual ~Brush() {}
+    virtual bool onMouseClick(QMouseEvent* event, int x, int y, bool insideWidget); // insideWidget is true if the event occurred in the paintable area
+    virtual bool onMouseRelease(QMouseEvent* event, int x, int y, bool insideWidget); // brush paint events are always called, but are not required to be implemented
+    virtual bool onMouseMove(QMouseEvent* event, int x, int y, bool insideWidget); // IMPORTANT: when insideWidget is true, the x and y values are INVALID and NOT SUPPOSED TO BE USED
+    virtual void onWidgetPaint(QPaintEvent* event, QPainter& painter); // if the mouse functions return true, the widget emits a somethingChanged() signal
+};
+
+#endif // BRUSH_H
diff --git a/ircpaint/BrushList.cpp b/ircpaint/BrushList.cpp
new file mode 100644
index 0000000..919cfd9
--- /dev/null
+++ b/ircpaint/BrushList.cpp
@@ -0,0 +1,74 @@
+#include "BrushList.h"
+
+#include <QGridLayout>
+#include <QActionGroup>
+#include <QToolButton>
+
+BrushList::BrushList(QWidget *parent) : QWidget(parent) {
+    setAttribute(Qt::WA_StaticContents);
+
+    bCursorAct = new QAction(tr("Cursor"), this);
+    bCursorAct->setIcon(QIcon(":/icons/cursor.png"));
+    bCursorAct->setCheckable(true);
+    bCursorAct->setData(BrushT_Cursor);
+    connect(bCursorAct, SIGNAL(triggered()), this, SLOT(buttonClicked()));
+    bCursorBut = new QToolButton(this);
+    bCursorBut->setDefaultAction(bCursorAct);
+
+    bPenAct = new QAction(tr("Pencil"), this);
+    bPenAct->setIcon(QIcon(":/icons/pencil.png"));
+    bPenAct->setCheckable(true);
+    bPenAct->setData(BrushT_Pen);
+    connect(bPenAct, SIGNAL(triggered()), this, SLOT(buttonClicked()));
+    bPenBut = new QToolButton(this);
+    bPenBut->setDefaultAction(bPenAct);
+
+    bFillAct = new QAction(tr("Fill"), this);
+    bFillAct->setIcon(QIcon(":/icons/paintcan.png"));
+    bFillAct->setCheckable(true);
+    bFillAct->setData(BrushT_Fill);
+    connect(bFillAct, SIGNAL(triggered()), this, SLOT(buttonClicked()));
+    bFillBut = new QToolButton(this);
+    bFillBut->setDefaultAction(bFillAct);
+
+    bLineAct = new QAction(tr("Line"), this);
+    bLineAct->setIcon(QIcon(":/icons/line.png"));
+    bLineAct->setCheckable(true);
+    bLineAct->setData(BrushT_Line);
+    connect(bLineAct, SIGNAL(triggered()), this, SLOT(buttonClicked()));
+    bLineBut = new QToolButton(this);
+    bLineBut->setDefaultAction(bLineAct);
+
+    bRectAct = new QAction(tr("Rectangle"), this);
+    bRectAct->setIcon(QIcon(":/icons/rect.png"));
+    bRectAct->setCheckable(true);
+    bRectAct->setData(BrushT_Rect);
+    connect(bRectAct, SIGNAL(triggered()), this, SLOT(buttonClicked()));
+    bRectBut = new QToolButton(this);
+    bRectBut->setDefaultAction(bRectAct);
+
+    group = new QActionGroup(this);
+    group->setExclusive(true);
+    group->addAction(bCursorAct);
+    group->addAction(bPenAct);
+    group->addAction(bFillAct);
+    group->addAction(bLineAct);
+    group->addAction(bRectAct);
+
+    layout = new QGridLayout(this);
+    layout->addWidget(bCursorBut, 0, 0);
+    layout->addWidget(bPenBut, 0, 1);
+    layout->addWidget(bFillBut, 1, 0);
+    layout->addWidget(bLineBut, 1, 1);
+    layout->addWidget(bRectBut, 2, 0);
+    layout->setRowStretch(3, 1);
+
+    bCursorAct->trigger();
+}
+
+void BrushList::buttonClicked() {
+    QAction* a = qobject_cast<QAction*>(sender());
+    if (a) {
+        emit brushSelected((BrushType)a->data().toInt());
+    }
+}
diff --git a/ircpaint/BrushList.h b/ircpaint/BrushList.h
new file mode 100644
index 0000000..5065773
--- /dev/null
+++ b/ircpaint/BrushList.h
@@ -0,0 +1,37 @@
+#ifndef BRUSHLIST_H
+#define BRUSHLIST_H
+
+#include "BrushType.h"
+#include <QWidget>
+class QActionGroup;
+class QToolButton;
+class QGridLayout;
+
+class BrushList : public QWidget {
+    Q_OBJECT
+public:
+    explicit BrushList(QWidget *parent = 0);
+
+public slots:
+    void buttonClicked();
+
+signals:
+    void brushSelected(BrushType);
+
+private:
+    QActionGroup* group;
+
+    QToolButton* bCursorBut;
+    QAction* bCursorAct;
+    QToolButton* bPenBut;
+    QAction* bPenAct;
+    QToolButton* bFillBut;
+    QAction* bFillAct;
+    QToolButton* bLineBut;
+    QAction* bLineAct;
+    QToolButton* bRectBut;
+    QAction* bRectAct;
+    QGridLayout* layout;
+};
+
+#endif // BRUSHLIST_H
diff --git a/ircpaint/BrushType.h b/ircpaint/BrushType.h
new file mode 100644
index 0000000..231bdfd
--- /dev/null
+++ b/ircpaint/BrushType.h
@@ -0,0 +1,8 @@
+#ifndef BRUSHTYPE_H
+#define BRUSHTYPE_H
+
+enum BrushType { BrushT_Cursor, BrushT_Pen,
+                 BrushT_Fill, BrushT_Line,
+                 BrushT_Rect };
+
+#endif // BRUSHTYPE_H
diff --git a/ircpaint/Brush_Fill.cpp b/ircpaint/Brush_Fill.cpp
new file mode 100644
index 0000000..533701b
--- /dev/null
+++ b/ircpaint/Brush_Fill.cpp
@@ -0,0 +1,31 @@
+#include <QtGui>
+
+#include "MainWidget.h"
+
+#include "Brush_Fill.h"
+#include "BFill_Command.h"
+
+bool Brush_Fill::onMouseRelease(QMouseEvent* event, int x, int y, bool insideWidget) {
+    if (insideWidget) {
+        QRgb from, to;
+        QImage* img;
+        if (event->button() == Qt::LeftButton) {
+            img = &widget->background;
+            from = img->pixel(x, y);
+            to = widget->getBGColor().rgb();
+            if (from == to)
+                return false;
+        } else if (event->button() == Qt::RightButton) {
+            img = &widget->foreground;
+            from = img->pixel(x, y);
+            to = widget->getFGColor().rgb();
+            if (from == to)
+                return false;
+        } else {
+            return false;
+        }
+        undo->push(new BFill_Command(widget, img, from, to, x, y));
+        return true;
+    }
+    return false;
+}
diff --git a/ircpaint/Brush_Fill.h b/ircpaint/Brush_Fill.h
new file mode 100644
index 0000000..09ca2b7
--- /dev/null
+++ b/ircpaint/Brush_Fill.h
@@ -0,0 +1,15 @@
+#ifndef BRUSH_FILL_H
+#define BRUSH_FILL_H
+
+#include "Brush.h"
+
+class QUndoStack;
+
+class Brush_Fill : public Brush {
+    QUndoStack* undo;
+public:
+    Brush_Fill(MainWidget* w, QUndoStack* u) : Brush(w), undo(u) {}
+    bool onMouseRelease(QMouseEvent* event, int x, int y, bool insideWidget);
+};
+
+#endif // BRUSH_FILL_H
diff --git a/ircpaint/Brush_Line.cpp b/ircpaint/Brush_Line.cpp
new file mode 100644
index 0000000..10197f6
--- /dev/null
+++ b/ircpaint/Brush_Line.cpp
@@ -0,0 +1,59 @@
+#include <QtGui>
+
+#include "MainWidget.h"
+
+#include "Brush_Line.h"
+#include "BLine_Command.h"
+
+bool Brush_Line::onMouseClick(QMouseEvent *event, int x, int y, bool insideWidget) {
+    if (insideWidget) {
+        start = event->pos();
+        end = event->pos();
+        xstart = x;
+        ystart = y;
+        drawPreview = true;
+        if (event->button() == Qt::LeftButton) {
+            drawOnBg = true;
+            col = widget->getBGColor();
+        } else {
+            drawOnBg = false;
+            col = widget->getFGColor();
+        }
+        widget->update(QRect(start,end).adjusted(-3,-3,3,3));
+    }
+    return false;
+}
+
+bool Brush_Line::onMouseMove(QMouseEvent *event, int , int , bool insideWidget) {
+    QPoint oldend = end;
+    if (insideWidget) {
+        end = event->pos();
+    }
+    widget->update(QRect(start,end).united(QRect(start,oldend)).adjusted(-3,-3,3,3));
+    return false;
+}
+
+bool Brush_Line::onMouseRelease(QMouseEvent *event, int x, int y, bool insideWidget) {
+    drawPreview = false;
+    if (insideWidget) {
+        end = event->pos();
+        if (drawOnBg) {
+            undo->push(new BLine_Command(widget, &widget->background, widget->alternate ? &widget->foreground : NULL, col, xstart, ystart, x, y));
+        } else {
+            undo->push(new BLine_Command(widget, &widget->foreground, widget->alternate ? &widget->background : NULL, col, xstart, ystart, x, y));
+        }
+        return true;
+    } else {
+        widget->update(QRect(start,end).adjusted(-3,-3,3,3));
+        return false;
+    }
+}
+
+void Brush_Line::onWidgetPaint(QPaintEvent *, QPainter &painter) {
+    if (drawPreview) {
+        QPen p(col);
+        p.setWidth(3);
+        painter.setPen(p);
+        painter.drawLine(start, end);
+    }
+}
diff --git a/ircpaint/Brush_Line.h b/ircpaint/Brush_Line.h
new file mode 100644
index 0000000..33a4a1a
--- /dev/null
+++ b/ircpaint/Brush_Line.h
@@ -0,0 +1,25 @@
+#ifndef BRUSH_LINE_H
+#define BRUSH_LINE_H
+
+#include "Brush.h"
+
+#include <QColor>
+#include <QPoint>
+
+class QUndoStack;
+
+class Brush_Line : public Brush {
+    QPoint start, end;
+    int xstart, ystart;
+    bool drawPreview, drawOnBg;
+    QColor col;
+    QUndoStack* undo;
+public:
+    Brush_Line(MainWidget* w, QUndoStack* u) : Brush(w), undo(u), drawPreview(false), drawOnBg(false), xstart(0), ystart(0) {}
+    bool onMouseClick(QMouseEvent *event, int x, int y, bool insideWidget);
+    bool onMouseMove(QMouseEvent *event, int x, int y, bool insideWidget);
+    bool onMouseRelease(QMouseEvent *event, int x, int y, bool insideWidget);
+    void onWidgetPaint(QPaintEvent *event, QPainter &painter);
+};
+
+#endif // BRUSH_LINE_H
diff --git a/ircpaint/Brush_Pen.cpp b/ircpaint/Brush_Pen.cpp
new file mode 100644
index 0000000..5394da0
--- /dev/null
+++ b/ircpaint/Brush_Pen.cpp
@@ -0,0 +1,35 @@
+#include <QtGui>
+#include "MainWidget.h"
+
+#include "Brush_Pen.h"
+
+#include "BPen_Command.h"
+
+bool Brush_Pen::onMouseClick(QMouseEvent *event, int x, int y, bool insideWidget) {
+    if (insideWidget) {
+        if (event->button() == Qt::LeftButton) {
+            undo->push(new BPen_Command(widget, &widget->background, widget->alternate ? &widget->foreground : NULL, id, x, y, widget->getBGColor().rgb()));
+        } else if (event->button() == Qt::RightButton) {
+            undo->push(new BPen_Command(widget, &widget->foreground, widget->alternate ? &widget->background : NULL, id, x, y, widget->getFGColor().rgb()));
+        }
+        return true;
+    }
+    return false;
+}
+
+bool Brush_Pen::onMouseMove(QMouseEvent *event, int x, int y, bool insideWidget) {
+    if (insideWidget) {
+        if (event->buttons() & Qt::LeftButton) {
+            undo->push(new BPen_Command(widget, &widget->background, widget->alternate ? &widget->foreground : NULL, id, x, y, widget->getBGColor().rgb()));
+        } else if (event->buttons() & Qt::RightButton) {
+            undo->push(new BPen_Command(widget, &widget->foreground, widget->alternate ? &widget->background : NULL, id, x, y, widget->getFGColor().rgb()));
+        }
+        return true;
+    }
+    return false;
+}
+
+bool Brush_Pen::onMouseRelease(QMouseEvent *, int, int, bool) {
+    id = id == 55 ? 555 : 55;
+    return false;
+}
diff --git a/ircpaint/Brush_Pen.h b/ircpaint/Brush_Pen.h
new file mode 100644
index 0000000..f5fcd90
--- /dev/null
+++ b/ircpaint/Brush_Pen.h
@@ -0,0 +1,19 @@
+#ifndef BRUSH_PEN_H
+#define BRUSH_PEN_H
+
+#include "Brush.h"
+
+class QUndoStack;
+class BPen_Command;
+
+class Brush_Pen : public Brush {
+    QUndoStack* undo;
+    int id;
+public:
+    Brush_Pen(MainWidget* w, QUndoStack* u) : Brush(w), undo(u), id(55) {}
+    bool onMouseClick(QMouseEvent *event, int x, int y, bool insideWidget);
+    bool onMouseMove(QMouseEvent *event, int x, int y, bool insideWidget);
+    bool onMouseRelease(QMouseEvent *, int, int, bool);
+};
+
+#endif // BRUSH_PEN_H
diff --git a/ircpaint/Brush_Rect.cpp b/ircpaint/Brush_Rect.cpp
new file mode 100644
index 0000000..02a839a
--- /dev/null
+++ b/ircpaint/Brush_Rect.cpp
@@ -0,0 +1,58 @@
+#include <QtGui>
+#include "MainWidget.h"
+
+#include "Brush_Rect.h"
+#include "BRect_Command.h"
+
+bool Brush_Rect::onMouseClick(QMouseEvent* event, int x, int y, bool insideWidget) {
+    if (insideWidget) {
+        start = event->pos();
+        end = event->pos();
+        xstart = x;
+        ystart = y;
+        drawPreview = true;
+        if (event->button() == Qt::LeftButton) {
+            drawOnBg = true;
+            col = widget->getBGColor().rgb();
+        } else {
+            drawOnBg = false;
+            col = widget->getFGColor().rgb();
+        }
+        widget->update(QRect(start,end).adjusted(-3,-3,3,3));
+    }
+    return false;
+}
+
+bool Brush_Rect::onMouseMove(QMouseEvent* event, int , int , bool insideWidget) {
+    QPoint oldend = end;
+    if (insideWidget) {
+        end = event->pos();
+    }
+    widget->update(QRect(start,end).united(QRect(start,oldend)).adjusted(-3,-3,3,3));
+    return false;
+}
+
+bool Brush_Rect::onMouseRelease(QMouseEvent* event, int x, int y, bool insideWidget) {
+    drawPreview = false;
+    if (insideWidget) {
+        end = event->pos();
+        if (drawOnBg) {
+            undo->push(new BRect_Command(widget, &widget->background, widget->alternate ? &widget->foreground : NULL, col, xstart, ystart, x, y));
+        } else {
+            undo->push(new BRect_Command(widget, &widget->foreground, widget->alternate ? &widget->background : NULL, col, xstart, ystart, x, y));
+        }
+        return true;
+    } else {
+        widget->update(QRect(start,end).adjusted(-3,-3,3,3));
+        return false;
+    }
+}
+
+void Brush_Rect::onWidgetPaint(QPaintEvent* , QPainter &painter) {
+    if (drawPreview) {
+        QPen p(col);
+        p.setWidth(3);
+        painter.setPen(p);
+        painter.drawRect(QRect(start, end));
+    }
+}
diff --git a/ircpaint/Brush_Rect.h b/ircpaint/Brush_Rect.h
new file mode 100644
index 0000000..2ab931c
--- /dev/null
+++ b/ircpaint/Brush_Rect.h
@@ -0,0 +1,25 @@
+#ifndef BRUSH_RECT_H
+#define BRUSH_RECT_H
+
+#include "Brush.h"
+
+#include <QRgb>
+#include <QPoint>
+
+class QUndoStack;
+
+class Brush_Rect : public Brush {
+    QPoint start, end;
+    int xstart, ystart;
+    bool drawPreview, drawOnBg;
+    QRgb col;
+    QUndoStack* undo;
+public:
+    Brush_Rect(MainWidget* w, QUndoStack* u) : Brush(w), undo(u), drawPreview(false), drawOnBg(false), xstart(0), ystart(0) {}
+    bool onMouseClick(QMouseEvent* event, int x, int y, bool insideWidget);
+    bool onMouseMove(QMouseEvent* event, int x, int y, bool insideWidget);
+    bool onMouseRelease(QMouseEvent* event, int x, int y, bool insideWidget);
+    void onWidgetPaint(QPaintEvent* event, QPainter &painter);
+};
+
+#endif // BRUSH_RECT_H
diff --git a/ircpaint/ColorPicker.cpp b/ircpaint/ColorPicker.cpp
new file mode 100644
index 0000000..e4c62a7
--- /dev/null
+++ b/ircpaint/ColorPicker.cpp
@@ -0,0 +1,40 @@
+#include <QtGui>
+
+#include "ColorPicker.h"
+#include <QtDebug>
+
+ColorPicker::ColorPicker(QWidget *parent, QMap<int, QRgb>* c) : QWidget(parent), colors(c) {
+    setAttribute(Qt::WA_StaticContents);
+    setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+}
+
+QSize ColorPicker::sizeHint() const {
+    return QSize(53,209);
+}
+
+void ColorPicker::mouseReleaseEvent(QMouseEvent* event) {
+    int i = event->pos().x() / 25;
+    int j = event->pos().y() / 25;
+    if (i > 1 || j > 7) {
+        QWidget::mouseReleaseEvent(event);
+        return;
+    }
+    if (event->button() == Qt::LeftButton) {
+        emit bgColorPicked(i ? (j*2)+1 : j*2);
+    } else if (event->button() == Qt::RightButton) {
+        emit fgColorPicked(i ? (j*2)+1 : j*2);
+    }
+}
+
+void ColorPicker::paintEvent(QPaintEvent*) {
+    QPainter painter(this);
+    painter.setPen(palette().dark().color());
+    for (int i = 0; i < 3; ++i)
+        painter.drawLine(25*i, 0, 25*i, 200);
+    for (int i = 0; i < 9; ++i)
+        painter.drawLine(0, 25*i, 50, 25*i);
+    for (int i = 0; i < 8; ++i) // first column (0,2,4,6,8,10,12,14)
+        painter.fillRect(1, (25*i)+1, 24, 24, (*colors)[i*2]);
+    for (int i = 0; i < 8; ++i) // second column (1,3,5,7,9,11,13,15)
+        painter.fillRect(26, (25*i)+1, 24, 24, (*colors)[(i*2)+1]);
+}
diff --git a/ircpaint/ColorPicker.h b/ircpaint/ColorPicker.h
new file mode 100644
index 0000000..8001da9
--- /dev/null
+++ b/ircpaint/ColorPicker.h
@@ -0,0 +1,26 @@
+#ifndef COLORPICKER_H
+#define COLORPICKER_H
+
+#include <QWidget>
+#include <QMap>
+#include <QRgb>
+
+class ColorPicker : public QWidget {
+    Q_OBJECT
+public:
+    explicit ColorPicker(QWidget *parent, QMap<int, QRgb>* c);
+    QSize sizeHint() const;
+
+protected:
+    void paintEvent(QPaintEvent*);
+    void mouseReleaseEvent(QMouseEvent* event);
+
+signals:
+    void bgColorPicked(int);
+    void fgColorPicked(int);
+
+private:
+    QMap<int, QRgb>* colors;
+};
+
+#endif // COLORPICKER_H
diff --git a/ircpaint/ColorSwatch.cpp b/ircpaint/ColorSwatch.cpp
new file mode 100644
index 0000000..e7287c9
--- /dev/null
+++ b/ircpaint/ColorSwatch.cpp
@@ -0,0 +1,35 @@
+#include <QtGui>
+
+#include "ColorSwatch.h"
+
+ColorSwatch::ColorSwatch(QWidget *parent, QMap<int, QRgb>* c) : QWidget(parent), colors(c), bg(&(*c)[1]), fg(&(*c)[0]) {
+    setAttribute(Qt::WA_StaticContents);
+    setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+}
+
+QSize ColorSwatch::sizeHint() const {
+    return QSize(46, 46);
+}
+
+void ColorSwatch::bgColorChanged(int i) {
+    while (i > 15)
+        i -= 15;
+    bg = &(*colors)[i];
+    update();
+}
+
+void ColorSwatch::fgColorChanged(int i) {
+    while (i > 15)