// XDrawChem
// Copyright (C) 2004-2005  Bryan Herger <bherger@users.sourceforge.net>
// Copyright (C) 2020  Yaman Qalieh <ybq987@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <QAction>
#include <QColorDialog>
#include <QDrag>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QMouseEvent>
#include <QPainter>
#include <QPrinter>
#include <cmath>

#include "bracket.h"
#include "chemdata.h"
#include "defs.h"
#include "drawable.h"
#include "molecule.h"
#include "molinfodialog.h"
#include "render2d.h"
#include "text.h"
#include "textshapedialog.h"
#include "xdc_event.h"

// move point to grid if needed
QPoint Render2D::GridLock(QPoint qp1) {
    if (mode < 200)
        return qp1;
    if (mode > 299)
        return qp1;

    int d1 = qRound((double)preferences.getGridSpacing() * zoomFactor);

    if (preferences.getDrawGrid() == GRID_SQUARE) {
        double gs = (double)d1, gx = (double)qp1.x(), gy = (double)qp1.y();
        int rx, ry;

        rx = qRound(gx / gs);
        ry = qRound(gy / gs);
        qp1.setX(rx * d1);
        qp1.setY(ry * d1);

        update();
        directdraw = true;
        //      painter->begin(this);
        drawBox(QPoint(qp1.x() - 2, qp1.y() - 2), QPoint(qp1.x() + 2, qp1.y() + 2),
                QColor(255, 0, 0));
        //      painter->end();
        directdraw = false;

        return qp1;
    }

    if (preferences.getDrawGrid() == GRID_HEX) {
        bool hexoffset = true;
        double hexhoriz = (double)d1 / 2.0;
        double hexgap = 2.0 * hexhoriz + (double)d1;
        double hexelevation = hexhoriz * 1.73205;
        double hex_initx, hex_rightx, dx, dy;
        double maxdist = 9999.0, max_x = 0.0, max_y = 0.0;

        for (double hex_y = 0.0; hex_y < renderHeight; hex_y += hexelevation) {
            if (hexoffset)
                hex_initx = hexhoriz;
            else
                hex_initx = hexgap;
            for (double hex_x = hex_initx; hex_x < renderWidth; hex_x += hexgap) {
                hex_rightx = hex_x + (double)d1;
                dx = hex_x - qp1.x();
                dy = hex_y - qp1.y();
                if (sqrt(dx * dx + dy * dy) < maxdist) {
                    max_x = hex_x;
                    max_y = hex_y;
                    maxdist = sqrt(dx * dx + dy * dy);
                }
                hex_x = hex_rightx;
                dx = hex_x - qp1.x();
                dy = hex_y - qp1.y();
                if (sqrt(dx * dx + dy * dy) < maxdist) {
                    max_x = hex_x;
                    max_y = hex_y;
                    maxdist = sqrt(dx * dx + dy * dy);
                }
            }
            hexoffset = !hexoffset;
        }
        qp1.setX((int)max_x);
        qp1.setY((int)max_y);
        return qp1;
    }

    return qp1;
}

// right-click popup choices from mousePressEvent popup (rtclickpopup)
void Render2D::molInfo() {
    qDebug() << "Popup-info";
    DPoint *tmp_pt2 = new DPoint;

    tmp_pt2->set(targetmol->BoundingBoxAll().center());
    c->Tool(tmp_pt2, MODE_TOOL_MOLECULE_INFO);
}

void Render2D::molSelect() {
    DeselectAll();
    targetmol->SelectAll();
    selectionBox = c->selectionBox();
    mode = MODE_SELECT_MULTIPLE_SELECTED;
    update();
}

void Render2D::molCut() {
    DeselectAll();
    targetmol->SelectAll();
    Cut();
    qDebug() << "popup-Cut";
}

void Render2D::molCopy() {
    DeselectAll();
    targetmol->SelectAll();
    Copy();
    DeselectAll();
    qDebug() << "popup-Copy";
}

void Render2D::bondEdit() {
    if (highlightobject->Type() == TYPE_TEXT) {
        doubleClickFlag = true;
        prev_mode = mode;
        mode = MODE_TEXT;
        DrawText_mousePressEvent(0, QPoint(0, 0));
        return;
    }
    highlightobject->Edit();
    highlightobject->Highlight(false);
    highlightobject = 0;
    if (mode == MODE_SELECT)
        setCursor(Qt::ArrowCursor);
    else
        setCursor(Qt::CrossCursor);
    mouse3down = false;
    update();
}

void Render2D::bondInfo() {
    QString objinf;

    objinf = tr("No information.");

    if (highlightobject->Type() == TYPE_BOND) {
        objinf = "ID: ";
        objinf.append(((Bond *)highlightobject)->CName());
        objinf.append("\nReactions: ");
        objinf.append(((Bond *)highlightobject)->getReactions());
        // make it easier to add to retro.txt
        qDebug() << "ID:" << ((Bond *)highlightobject)->CName();
    }

    QMessageBox::information(0, tr("Object information"), objinf);
}

void Render2D::textShape() {
    localtext = (Text *)highlightobject;
    TextShapeDialog *tsd = new TextShapeDialog(this);

    tsd->set_stype(localtext->getShape());
    tsd->setBorderCheck(localtext->getBorder());
    tsd->setFillCheck(localtext->getFill());
    tsd->setBorderColor(localtext->getBorderColor());
    tsd->setFillColor(localtext->getFillColor());
    tsd->setWidth(localtext->getShapeWidth());
    tsd->setHeight(localtext->getShapeHeight());

    if (tsd->exec()) {
        qDebug() << "textShape OK";
    }

    localtext->setShape(tsd->get_stype());
    localtext->setBorder(tsd->getBorderCheck());
    localtext->setFill(tsd->getFillCheck());
    localtext->setBorderColor(tsd->getBorderColor());
    localtext->setFillColor(tsd->getFillColor());
    localtext->setShapeWidth(tsd->getWidth());
    localtext->setShapeHeight(tsd->getHeight());

    delete tsd;

    localtext = 0;
}

void Render2D::bracketFill() {
    Bracket *lbracket = (Bracket *)highlightobject;
    QColor b1color = lbracket->getFillColor();

    b1color = QColorDialog::getColor(b1color);

    if (b1color.isValid() == true) {
        lbracket->setFill(true);
        lbracket->setFillColor(b1color);
    }
}

void Render2D::bracketFillOff() {
    Bracket *lbracket = (Bracket *)highlightobject;

    lbracket->setFill(false);
}

void Render2D::mousePressEvent(QMouseEvent *e1) {
    qDebug() << "mousePressEvent begin; mode: " << mode;
    if (localstring == "XDC_DELETE_ME") {
        delete localtexteditor;

        localtexteditor = 0;
        localstring = " ";
    }

    QPoint curqpt = zoomCorrectReverse(e1->pos());

    if (preferences.getSnapGrid() != GRID_NONE) {
        curqpt = GridLock(curqpt);
    }

    bool allowedit = false;

    moved = false;

    DPoint tmp_pt(curqpt.x(), curqpt.y());
    DPoint *tmp_pt2 = new DPoint;

    tmp_pt2->x = curqpt.x();
    tmp_pt2->y = curqpt.y();

    if (mode > 100)
        c->StartUndo(mode,
                     0); // BMS: StartUndo does nothing with the point - don't copy!
    // qDebug() << "Click" ;
    // update button state
    if (e1->button() == Qt::LeftButton)
        mouse1down = true;
    if (e1->button() == Qt::RightButton) {
        mouse3down = true;
    }
    // qDebug() << "start at " << curqpt.x() << "," << curqpt.y() ;

    // can the current mode use right button popup menu?
    bool rbm = false;

    if (mode == MODE_SELECT || mode == MODE_SELECT_MOVE_OBJECT || mode == MODE_DRAWBRACKET)
        rbm = true;

    if (rbm && mouse3down) {
        // if (rtclickpopup != 0) delete rtclickpopup;
        rtclickpopup = new QMenu(this);
        bool menu_ok = false;

        if ((mode == MODE_DRAWBRACKET) && (highlightobject == 0)) {
            // put nearest bracket into highlightobject
            DPoint *e = new DPoint;

            e->x = curqpt.x();
            e->y = curqpt.y();
            double distobj;

            // get Drawable of nearest object
            Drawable *no = c->FindNearestObject(e, distobj);

            if ((distobj < 5.0) && (no->Type() == TYPE_BRACKET))
                highlightobject = no;
        }

        if (highlightobject != 0) {
            QString rtmt;

            rtmt = tr("Object");
            if (highlightobject->Type() == TYPE_ARROW)
                rtmt = tr("Arrow");
            else if (highlightobject->Type() == TYPE_BOND)
                rtmt = tr("Bond");
            else if (highlightobject->Type() == TYPE_BRACKET)
                rtmt = tr("Bracket");
            else if (highlightobject->Type() == TYPE_CURVEARROW)
                rtmt = tr("Curved Arrow");
            else if (highlightobject->Type() == TYPE_SYMBOL)
                rtmt = tr("Symbol");
            else if (highlightobject->Type() == TYPE_TEXT)
                rtmt = tr("Text");
            QAction *title = new QAction("test", this);
            title->setEnabled(false);
            QFont f = title->font();
            //            rtclickpopup->setFont( QFont("Helvetica", 12,
            //            QFont::Bold) );
            rtclickpopup->addAction(title);
            rtclickpopup->addSeparator();
            rtclickpopup->addAction(tr("Edit"), this, SLOT(bondEdit()));
            if (highlightobject->Type() == TYPE_BOND) {
                rtclickpopup->addAction(tr("Info"), this, SLOT(bondInfo()));
            } else if (highlightobject->Type() == TYPE_BRACKET) {
                rtclickpopup->addAction(tr("Fill color..."), this, SLOT(bracketFill()));
                rtclickpopup->addAction(tr("No fill"), this, SLOT(bracketFillOff()));
            } else if (highlightobject->Type() == TYPE_TEXT) {
                rtclickpopup->addAction(tr("Shape..."), this, SLOT(textShape()));
            }
            menu_ok = true;
        }

        targetmol = c->insideMolecule(tmp_pt2);
        if (targetmol != 0) {
            rtclickpopup->setTitle(tr("Molecule"));
            rtclickpopup->addAction(tr("Info"), this, SLOT(molInfo()));
            rtclickpopup->addAction(tr("Select"), this, SLOT(molSelect()));
            rtclickpopup->addAction(tr("Cut"), this, SLOT(molCut()));
            rtclickpopup->addAction(tr("Copy"), this, SLOT(molCopy()));
            menu_ok = true;
        }
        mouse3down = false;
        if (menu_ok)
            rtclickpopup->popup(mapToGlobal(e1->pos()));
    }
    // MODE_TEXT with right button
    if ((mode == MODE_TEXT) && mouse3down) {
        rtclickpopup = new QMenu(this);
        // int id1;
        bool menu_ok = false;

        if (highlightobject != 0) {
            QString rtmt;

            rtmt = tr("Object");
            if (highlightobject->Type() == TYPE_TEXT)
                rtmt = tr("Text");
            rtclickpopup->addAction(rtmt);
            rtclickpopup->addAction(tr("Edit"), this, SLOT(bondEdit()));
            if (highlightobject->Type() == TYPE_TEXT)
                rtclickpopup->addAction(tr("Shape..."), this, SLOT(textShape()));
            menu_ok = true;
        }
        mouse3down = false;
        if (menu_ok) {
            rtclickpopup->popup(mapToGlobal(e1->pos()));
            return;
        }
    }
    // MODE_TEXT: call special function to handle text events
    if (mode == MODE_TEXT) {

        qDebug() << "mode_text 1";
        DrawText_mousePressEvent(e1, curqpt);
        qDebug() << "mode_text 2";
        return;
    }
    // MODE_SELECT_LASSO: start lasso
    if (mode == MODE_SELECT_LASSO) {
        c->DeselectAll();
        curr_lasso.clear();
        curr_lasso.append(curqpt);
        return;
    }
    // MODE_SELECT: start multiple selection
    if ((mode == MODE_SELECT) && (mouse1down)) {
        if ((highlightpoint == 0) && (highlightobject == 0)) {
            selectOrigin = e1->pos();
            mode = MODE_SELECT_MULTIPLE;
            return;
        } else {
            qDebug() << "Should move.";
            if (highlightpoint == 0) {
                mode = MODE_SELECT_MOVE_OBJECT;
            } else {
                if (highlightpoint->other != 0) {
                    mode = MODE_DRAWLINE_DRAWING;
                    savedBondOrder = highlightpoint->otherBond->Order();
                    c->Erase(highlightpoint->otherBond);
                    if (savedBondOrder == 2)
                        c->Erase(highlightpoint->otherBond);
                    if (savedBondOrder == 3)
                        c->Erase(highlightpoint->otherBond);
                    startpoint = highlightpoint->other;
                    endpoint = 0;
                    highlightpoint = 0;
                    prev_mode = MODE_SELECT;
                } else {
                    mode = MODE_SELECT_MOVE_POINT;
                }
            }
        }
        return;
    }
    // set allowedit = true for appropriate modes
    if (mode == MODE_SELECT)
        allowedit = true;
    // get Drawable of nearest object
    DPoint *e = new DPoint;

    e->x = curqpt.x();
    e->y = curqpt.y();
    double distobj;
    Drawable *no = c->FindNearestObject(e, distobj);

    if (distobj > 5.0)
        no = 0;
    if (no != 0) {
        if ((mode == MODE_DRAWLINE) && (no->Type() == TYPE_BOND))
            allowedit = true;
        if ((mode == MODE_DRAWLINE_DASH) && (no->Type() == TYPE_BOND))
            allowedit = true;
        if ((mode == MODE_DRAWLINE_UP) && (no->Type() == TYPE_BOND))
            allowedit = true;
        if ((mode == MODE_DRAWLINE_DOWN) && (no->Type() == TYPE_BOND))
            allowedit = true;
    }
    // allowedit and BUTTON3: edit item under mouse
    if ((allowedit) && (mouse3down)) {
        if (highlightobject == 0)
            highlightobject = no;
        if (highlightobject != 0) {
            if (highlightobject->Type() == TYPE_TEXT) {
                setCursor(Qt::IBeamCursor);
                mode = MODE_TEXT;
                text_exists = true;
                localtext = (Text *)highlightobject;
                emit textOn(localtext->getFont());

                update();
                return;
            }
            qDebug() << "Will edit";
            highlightobject->Edit();
            highlightobject->Highlight(false);
            highlightobject = 0;
            if (mode == MODE_SELECT)
                setCursor(Qt::ArrowCursor);
            else
                setCursor(Qt::CrossCursor);
            mouse3down = false;
            update();
        }
        return;
    }
    // MODE_SELECT_MULTIPLE_SELECTED: either move, or deselect all
    if ((mode == MODE_SELECT_MULTIPLE_SELECTED) && (mouse1down)) {
        // check for move
        if (selectionBox.contains(curqpt)) {
            c->StartUndo(0, 0);
            mode = MODE_SELECT_MOVE_MULTIPLE;
        } else {
            c->DeselectAll();
            mode = MODE_SELECT;
            update();
        }
    }
    // MODE_SELECT_MULTIPLE_SELECTED and Button3: Drag and Drop!
    if ((mode == MODE_SELECT_MULTIPLE_SELECTED) && (mouse3down)) {
        // check for move
        if (selectionBox.contains(curqpt)) {
            dragging = true;
        } else {
            c->DeselectAll();
            mode = MODE_SELECT;
            update();
        }
    }
    // MODE_SELECT_MULTIPLE_RESIZE: resize drawing
    if ((mode == MODE_SELECT_MULTIPLE_RESIZE) && (mouse1down)) {
        mode = MODE_SELECT_RESIZE_MULTIPLE;
    }
    // MODE_SELECT_MULTIPLE_ROTATE: rotate drawing
    if ((mode == MODE_SELECT_MULTIPLE_ROTATE) && (mouse1down)) {
        mode = MODE_SELECT_ROTATE_MULTIPLE;
        rotateOrigin.setX((selectionBox.right() + selectionBox.left() - 2) / 2);
        rotateOrigin.setY((selectionBox.bottom() + selectionBox.top()) / 2);
        prevangle = getAngle(rotateOrigin, curqpt);
    }
    // MODE_DRAWLINE (draw line tool selected), and left button.
    // This means we start drawing a line
    if ((mode == MODE_DRAWLINE) && (mouse1down)) {
        startpoint = highlightpoint; // both 0 if nothing selected
        highlightpoint = 0;          // stop drawing highlight box
        if (startpoint == 0) {
            startpoint = new DPoint;
            startpoint->set(curqpt);
            endpoint = 0;
        }
        mode = MODE_DRAWLINE_DRAWING;
    }
    // MODE_DRAWWAVYLINE (draw line tool selected), and left button.
    // This means we start drawing a line
    if ((mode == MODE_DRAWWAVYLINE) && (mouse1down)) {
        startpoint = highlightpoint; // both 0 if nothing selected
        highlightpoint = 0;          // stop drawing highlight box
        if (startpoint == 0) {
            startpoint = new DPoint;
            startpoint->set(curqpt);
            endpoint = 0;
        }
        mode = MODE_DRAWWAVYLINE_DRAWING;
    }
    // MODE_DRAWCHAIN (draw chain tool selected), and left button.
    // This means we start drawing an aliphatic chain
    if ((mode == MODE_DRAWCHAIN) && (mouse1down)) {
        startpoint = highlightpoint; // both 0 if nothing selected
        highlightpoint = 0;          // stop drawing highlight box
        if (startpoint == 0) {
            startpoint = new DPoint;
            startpoint->set(curqpt);
            endpoint = 0;
        }
        mode = MODE_DRAWCHAIN_DRAWING;
    }
    // MODE_DRAWLINE_DASH (draw dashed line tool selected), and left button.
    // This means we start drawing a dashed line
    if ((mode == MODE_DRAWLINE_DASH) && (mouse1down)) {
        startpoint = highlightpoint; // both 0 if nothing selected
        highlightpoint = 0;          // stop drawing highlight box
        if (startpoint == 0) {
            startpoint = new DPoint;
            startpoint->set(curqpt);
            endpoint = 0;
        }
        mode = MODE_DRAWLINE_DASH_DRAWING;
    }
    // MODE_DRAWLINE_UP (draw stereo-up line tool selected), and left button.
    // This means we start drawing a line
    if ((mode == MODE_DRAWLINE_UP) && (mouse1down)) {
        startpoint = highlightpoint; // both 0 if nothing selected
        highlightpoint = 0;          // stop drawing highlight box
        if (startpoint == 0) {
            startpoint = new DPoint;
            startpoint->set(curqpt);
            endpoint = 0;
        }
        mode = MODE_DRAWLINE_UP_DRAWING;
    }
    // MODE_DRAWLINE_DOWN (draw stereo-down line tool selected), and left
    // button. This means we start drawing a line
    if ((mode == MODE_DRAWLINE_DOWN) && (mouse1down)) {
        startpoint = highlightpoint; // both 0 if nothing selected
        highlightpoint = 0;          // stop drawing highlight box
        if (startpoint == 0) {
            startpoint = new DPoint;
            startpoint->set(curqpt);
            endpoint = 0;
        }
        mode = MODE_DRAWLINE_DOWN_DRAWING;
    }
    // MODE_DRAWARROW (draw arrow tool selected), and left button.
    // This means we start drawing an arrow
    if ((mode == MODE_DRAWARROW) && (mouse1down)) {
        startpoint = new DPoint;
        startpoint->set(curqpt);
        endpoint = 0;
        mode = MODE_DRAWARROW_DRAWING;
    }
    // MODE_DRAWCURVEARROW (draw curve arrow tool selected), and left button.
    // This means we start drawing an arrow
    if ((mode == MODE_DRAWCURVEARROW) && (mouse1down)) {
        startpoint = new DPoint;
        startpoint->set(curqpt);
        endpoint = 0;
        mode = MODE_DRAWCURVEARROW_DRAWING;
    }
    // MODE_DRAWBRACKET (draw bracket tool selected), and left button.
    // This means we start drawing an bracket
    if ((mode == MODE_DRAWBRACKET) && (mouse1down)) {
        startpoint = new DPoint;
        startpoint->set(curqpt);
        endpoint = 0;
        mode = MODE_DRAWBRACKET_DRAWING;
    }
    // MODE_ERASE, and left button.  Erase highlighted object.
    if ((mode == MODE_ERASE) && (mouse1down)) {
        if (highlightobject == 0)
            return;
        c->Erase(highlightobject);
        highlightobject = 0;
        update();
    }
}

void Render2D::mouseDoubleClickEvent(QMouseEvent *e1) {
    if (localstring == "XDC_DELETE_ME") {
        delete localtexteditor;

        localtexteditor = 0;
        localstring = " ";
    }

    QPoint curqpt = zoomCorrectReverse(e1->pos());

    if (preferences.getSnapGrid() != GRID_NONE) {
        curqpt = GridLock(curqpt);
    }

    // bool allowedit = false;

    moved = false;

    DPoint tmp_pt(curqpt.x(), curqpt.y());
    DPoint *tmp_pt2 = new DPoint;

    tmp_pt2->x = curqpt.x();
    tmp_pt2->y = curqpt.y();

    qDebug() << "mouseDoubleClickEvent received";

    if (highlightobject != 0) {
        if (highlightobject->Type() == TYPE_TEXT) {
            qDebug() << "Activate text object?";
            doubleClickFlag = true;
            prev_mode = mode;
            mode = MODE_TEXT;
            DrawText_mousePressEvent(e1, curqpt);
            return;
        }
    }
}

void Render2D::mouseReleaseEvent(QMouseEvent *e1) {
    qDebug() << "release MODE: " << mode;

    DPoint *tmp_pt;
    XDC_Event *evt;
    QPoint curqpt = zoomCorrectReverse(e1->pos());

    if (preferences.getSnapGrid() != GRID_NONE) {
        curqpt = GridLock(curqpt);
    }
    // qDebug() << "Unclick" ;
    // update button state
    if (e1->button() == Qt::LeftButton)
        mouse1down = false;
    if (e1->button() == Qt::RightButton)
        mouse3down = false;
    // qDebug() << "end at " << curqpt.x() << "," << curqpt.y() ;

    // Control-LeftClick invokes Molecule Information window
    if (e1->button() == Qt::LeftButton && e1->modifiers() == Qt::ControlModifier) {
        tmp_pt = new DPoint(curqpt);
        c->Tool(tmp_pt, MODE_TOOL_MOLECULE_INFO);
        update();
        return;
    }
    // MODE_TEXT: call special function to handle text events
    if (mode == MODE_TEXT) {
        DrawText_mouseReleaseEvent(e1, curqpt);
        return;
    }
    // MODE_SELECT_LASSO: finish lasso
    if (mode == MODE_SELECT_LASSO) {
        // TODO: validate loop
        // TODO: if loop is OK ("closed") then select points and draw box
        curr_lasso.append(curr_lasso.at(0));
        if (c->SelectWithinLoop(curr_lasso)) {
            qDebug() << "Objects found in loop";
            bool shiftdown = e1->button() == Qt::LeftButton && e1->modifiers() == Qt::ShiftModifier;

            // c->NewSelectRect(QRect(selectOrigin, selectPrev), shiftdown);
            selectionBox = c->selectionBox();
            if (selectionBox.left() < 999)
                mode = MODE_SELECT_MULTIPLE_SELECTED;
            else
                mode = MODE_SELECT_LASSO;
            update();
        }

        update();
        curr_lasso.clear();
        emit SignalSetStatusBar(tr("Select mode: left click on objects to move"));
        return;
    }
    // MODE_DRAWBEZIER
    if (mode == MODE_DRAWBEZIER) {
        update();
        directdraw = true;
        //        painter->begin(this);
        if (bezier_count < 3) {
            //            tmp_bezier.setPoint( bezier_count, curqpt.x(),
            //            curqpt.y() );
            drawBezier(tmp_bezier, currentColor, true);
            bezier_count++;
        } else {
            //            tmp_bezier.setPoint( bezier_count, curqpt.x(),
            //            curqpt.y() );
            // add new GraphicObject
            evt = new XDC_Event(EVT_ADD_GRAPHIC);
            evt->setPoints(tmp_bezier);
            evt->setParam1(bracket_type);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);

            //            drawBezier( tmp_bezier, currentColor, false,
            //            bracket_type );
            tmp_bezier.resize(0);
            tmp_bezier.resize(4);
            bezier_count = 0;
        }
        directdraw = false;
        //      painter->end();
        return;
    }
    // MODE_TOOL_*: ChemData handles this, mostly
    if (mode == MODE_TOOL_RETRO_BONDNAME) {
        qDebug() << "mode invoke";
        // retro_bondname handled by Molecule containing this bond
        if (highlightobject != 0) {
            qDebug() << "invoke";
            qDebug() << c->insideMolecule(highlightobject->Start())
                            ->RetroBondName((Bond *)highlightobject, true);
            highlightobject->Highlight(false);
            highlightobject = 0;
            update();
            setMode_Select();
        }
        return;
    }

    if ((mode > MODE_TOOL) && (mode < MODE_TOOL_RETRO_BONDNAME)) {
        tmp_pt = new DPoint(curqpt);
        c->Tool(tmp_pt, mode);
        // if (mode == MODE_TOOL_CLEANUPMOL) {
        setMode_Select();
        update();
        //}
        return;
    }
    // MODE_SELECT_MULTIPLE: finish multiple selection
    if ((mode == MODE_SELECT_MULTIPLE) && (!mouse1down)) {
        int tmp1;

        if (selectOrigin.x() > selectPrev.x()) {
            tmp1 = selectOrigin.x();
            selectOrigin.setX(selectPrev.x());
            selectPrev.setX(tmp1);
        }
        if (selectOrigin.y() > selectPrev.y()) {
            tmp1 = selectOrigin.y();
            selectOrigin.setY(selectPrev.y());
            selectPrev.setY(tmp1);
        }
        update();
        directdraw = true;
        //      painter->begin(this);
        drawBox(selectOrigin, selectPrev, QColor(255, 255, 255));
        bool shiftdown = false;

        if (e1->modifiers() == Qt::ShiftModifier)
            shiftdown = true;
        c->NewSelectRect(QRect(zoomCorrectReverse(selectOrigin), zoomCorrectReverse(selectPrev)),
                         shiftdown);
        selectionBox = c->selectionBox();
        if (selectionBox.left() < 999)
            mode = MODE_SELECT_MULTIPLE_SELECTED;
        else
            mode = MODE_SELECT;
        directdraw = false;
        //      painter->end();
        return;
    }
    // MODE_SELECT_MOVE_MULTIPLE: finish moving
    if ((mode == MODE_SELECT_MOVE_MULTIPLE) && (!mouse1down)) {
        mode = MODE_SELECT_MULTIPLE_SELECTED;
        c->FinishMove();
        update();
    }
    // MODE_SELECT_MOVE_OBJECT: finish moving
    if ((mode == MODE_SELECT_MOVE_OBJECT) && (!mouse1down)) {
        mode = MODE_SELECT;
        c->FinishMove();
        update();
    }
    // MODE_SELECT_MOVE_POINT: finish moving
    if ((mode == MODE_SELECT_MOVE_POINT) && (!mouse1down)) {
        mode = MODE_SELECT;
        c->FinishMove();
        update();
    }
    // MODE_SELECT_RESIZE_MULTIPLE: finish resizing
    if ((mode == MODE_SELECT_RESIZE_MULTIPLE) && (!mouse1down)) {
        mode = MODE_SELECT_MULTIPLE_SELECTED;
    }
    // MODE_SELECT_ROTATE_MULTIPLE: finish rotating
    if ((mode == MODE_SELECT_ROTATE_MULTIPLE) && (!mouse1down)) {
        mode = MODE_SELECT_MULTIPLE_SELECTED;
    }
    // MODE_DRAWLINE_DRAWING (drawing line), and left button.
    // Finish line
    if ((mode == MODE_DRAWLINE_DRAWING) && (!mouse1down)) {
        if (moved) {
            if (endpoint == 0) {
                endpoint = new DPoint;
                endpoint->set(curqpt);
            }
            // submit this line/bond to ChemData
            if (startpoint != endpoint) {
                evt = new XDC_Event(EVT_ADD_BOND);
                evt->setStart(startpoint);
                evt->setEnd(endpoint);
                evt->setParam1(thick);
                evt->setParam2(savedBondOrder);
                evt->setColor(currentColor);
                emit XDCEventSignal(evt);

                savedBondOrder = -1;
                // c->addBond(startpoint, endpoint, thick, 1, currentColor);
            }
        }
        startpoint = 0;
        endpoint = 0;
        if (prev_mode == MODE_SELECT) {
            mode = MODE_SELECT;
            setCursor(Qt::ArrowCursor);
            emit SignalSetStatusBar(tr("Select mode: left click on object to "
                                       "move, right click on object to edit"));

            prev_mode = 0;
        } else {
            mode = MODE_DRAWLINE;
        }
        update();
        return;
    }
    // MODE_DRAWCHAIN_DRAWING (drawing chain), and left button.
    // Finish chain
    if ((mode == MODE_DRAWCHAIN_DRAWING) && (!mouse1down)) {
        if (moved) {
            if (endpoint == 0) {
                endpoint = new DPoint;
                endpoint->set(curqpt);
            }
            // submit this set of lines/bonds to ChemData
            double ang, len;
            QString sang, slen;

            ang = Drawable::getAngle(startpoint, endpoint);
            double alt_ang1 = (ang - 30.0) * M_PI / 180.0;
            double alt_ang2 = (ang + 30.0) * M_PI / 180.0;
            double alt_swap;

            if (e1->modifiers() == Qt::ShiftModifier) {
                alt_swap = alt_ang1;
                alt_ang1 = alt_ang2;
                alt_ang2 = alt_swap;
            }
            len = startpoint->distanceTo(endpoint);
            double blen = preferences.getBond_fixedlength();
            int n1, nseg = qRound(len / blen);
            QPoint spoint, epoint, ipoint1, ipoint2;

            spoint = startpoint->toQPoint();
            ipoint1 = spoint;
            for (n1 = 0; n1 < nseg; n1++) {
                ipoint2.setX(qRound(ipoint1.x() + cos(alt_ang1) * blen));
                ipoint2.setY(qRound(ipoint1.y() + sin(alt_ang1) * blen));
                // drawLine( ipoint1, ipoint2, 1, currentColor );
                evt = new XDC_Event(EVT_ADD_BOND);
                if (n1 == 0)
                    evt->setStart(startpoint);
                else
                    evt->setStart(endpoint);
                endpoint = new DPoint(ipoint2);
                evt->setEnd(endpoint);
                evt->setParam1(thick);
                evt->setParam2(1);
                evt->setColor(currentColor);
                emit XDCEventSignal(evt);

                alt_swap = alt_ang1;
                alt_ang1 = alt_ang2;
                alt_ang2 = alt_swap;
                ipoint1 = ipoint2;
            }
        }
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWCHAIN;
        update();
        return;
    }
    // MODE_DRAWLINE_DASH_DRAWING (drawing line), and left button.
    // Finish line
    if ((mode == MODE_DRAWLINE_DASH_DRAWING) && (!mouse1down)) {
        if (moved) {
            if (endpoint == 0) {
                endpoint = new DPoint;
                endpoint->set(curqpt);
            }
            // submit this line/bond to ChemData
            evt = new XDC_Event(EVT_ADD_BOND_DASH);
            evt->setStart(startpoint);
            evt->setEnd(endpoint);
            evt->setParam1(thick);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);

            // c->addBond(startpoint, endpoint, thick, 99, currentColor);
        }
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWLINE_DASH;
        update();
        return;
    }
    // MODE_DRAWLINE_UP_DRAWING (drawing line), and left button.
    // Finish line
    if ((mode == MODE_DRAWLINE_UP_DRAWING) && (!mouse1down)) {
        if (moved) {
            if (endpoint == 0) {
                endpoint = new DPoint;
                endpoint->set(curqpt);
            }
            // submit this line/bond to ChemData
            evt = new XDC_Event(EVT_ADD_BOND_UP);
            evt->setStart(startpoint);
            evt->setEnd(endpoint);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);

            // c->addBond(startpoint, endpoint, 1, 5, currentColor);
        }
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWLINE_UP;
        update();
        return;
    }
    // MODE_DRAWLINE_DOWN_DRAWING (drawing line), and left button.
    // Finish line
    if ((mode == MODE_DRAWLINE_DOWN_DRAWING) && (!mouse1down)) {
        if (moved) {
            if (endpoint == 0) {
                endpoint = new DPoint;
                endpoint->set(curqpt);
            }
            // submit this line/bond to ChemData
            evt = new XDC_Event(EVT_ADD_BOND_DOWN);
            evt->setStart(startpoint);
            evt->setEnd(endpoint);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);

            // c->addBond(startpoint, endpoint, 1, 7, currentColor);
        }
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWLINE_DOWN;
        update();
        return;
    }
    // MODE_DRAWWAVYLINE_DRAWING (drawing line), and left button.
    // Finish line
    if ((mode == MODE_DRAWWAVYLINE_DRAWING) && (!mouse1down)) {
        if (moved) {
            if (endpoint == 0) {
                endpoint = new DPoint;
                endpoint->set(curqpt);
            }
            // submit this line/bond to ChemData
            evt = new XDC_Event(EVT_ADD_BOND_WAVY);
            evt->setStart(startpoint);
            evt->setEnd(endpoint);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);
        }
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWWAVYLINE;
        update();
        return;
    }
    // MODE_DRAWARROW_DRAWING (drawing arrow), and left button.
    // Finish arrow
    if ((mode == MODE_DRAWARROW_DRAWING) && (!mouse1down)) {
        if (moved) {
            if (endpoint == 0) {
                endpoint = new DPoint;
                endpoint->set(curqpt);
            }
            // submit this arrow to ChemData
            evt = new XDC_Event(EVT_ADD_ARROW);
            evt->setStart(startpoint);
            evt->setEnd(endpoint);
            evt->setColor(currentColor);
            evt->setParam1(bracket_type);
            evt->setParam2(thick);
            emit XDCEventSignal(evt);

            // c->addArrow(startpoint, endpoint, currentColor, bracket_type);
        }
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWARROW;
        update();
    }
    // MODE_DRAWCURVEARROW_DRAWING (drawing curved arrow), and left button.
    // Finish arrow
    if ((mode == MODE_DRAWCURVEARROW_DRAWING) && (!mouse1down)) {
        endpoint = new DPoint;
        endpoint->set(curqpt);
        // submit this arrow to ChemData
        evt = new XDC_Event(EVT_ADD_CURVEARROW);
        evt->setStart(startpoint);
        evt->setEnd(endpoint);
        evt->setColor(currentColor);
        evt->setText(symbolfile);
        emit XDCEventSignal(evt);

        // c->addCurveArrow(startpoint, endpoint, currentColor, symbolfile);
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWCURVEARROW;
        update();
    }
    // MODE_DRAWBRACKET_DRAWING (drawing bracket), and left button.
    // Finish bracket
    if ((mode == MODE_DRAWBRACKET_DRAWING) && (!mouse1down)) {
        if (endpoint == 0) {
            endpoint = new DPoint;
            endpoint->set(curqpt);
        }
        // submit this bracket to ChemData
        evt = new XDC_Event(EVT_ADD_BRACKET);
        evt->setStart(startpoint);
        evt->setEnd(endpoint);
        evt->setColor(currentColor);
        evt->setParam1(bracket_type);
        emit XDCEventSignal(evt);

        // c->addBracket(startpoint, endpoint, currentColor, bracket_type);
        startpoint = 0;
        endpoint = 0;
        mode = MODE_DRAWBRACKET;
        update();
    }
    // MODE_SYMBOL: create Symbol
    if ((mode == MODE_SYMBOL) && (!mouse1down)) {
        if (highlightpoint == 0) {
            endpoint = new DPoint(curqpt);
        } else {
            endpoint = highlightpoint;
        }
        if (symbolfile == "antibody") {
            c->DeselectAll();
            DPoint *ab0, *ab1, *ab2;
            double dab, dabscale;

            dabscale = preferences.getBond_fixedlength();
            dab = dabscale / 1.41421;
            ab0 = new DPoint(endpoint);
            ab0->y = ab0->y - dabscale;
            ab1 = new DPoint(ab0);
            ab2 = new DPoint(ab0);
            ab1->x = ab1->x - dab;
            ab1->y = ab1->y - dab;
            ab2->x = ab2->x + dab;
            ab2->y = ab2->y - dab;
            preferences.setBondCenter(true);
            evt = new XDC_Event(EVT_ADD_BOND);
            evt->setStart(endpoint);
            evt->setEnd(ab0);
            evt->setParam1(2);
            evt->setParam2(2);
            evt->setColor(currentColor);
            evt->setBool1(true);
            emit XDCEventSignal(evt);

            preferences.setBondCenter(false);
            evt = new XDC_Event(EVT_ADD_BOND);
            evt->setStart(ab0);
            evt->setEnd(ab1);
            evt->setParam1(1);
            evt->setParam2(2);
            evt->setColor(currentColor);
            evt->setBool1(true);
            emit XDCEventSignal(evt);

            evt = new XDC_Event(EVT_ADD_BOND);
            evt->setStart(ab0);
            evt->setEnd(ab2);
            evt->setParam1(1);
            evt->setParam2(2);
            evt->setBool1(true);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);

            mode = MODE_SELECT_MULTIPLE_SELECTED;
            selectionBox = c->selectionBox();
            update();
            endpoint = 0;
            return;
        }
        if (highlightpoint != 0) {
            c->addSymbol(highlightpoint, symbolfile); // add to existing point
        } else {
            c->addSymbol(endpoint, symbolfile); // add at current mouse position
        }
        endpoint = 0;
        update();
    }
    // MODE_DRAWNEWMAN_*: create Newman projection
    if ((mode == MODE_DRAWNEWMAN_ANTI) && (!mouse1down)) {
        endpoint = new DPoint;
        endpoint->set(curqpt);
        double blen = preferences.getBond_fixedlength();
        int n1;
        QPoint ipoint1 = endpoint->toQPoint();
        QPoint ipoint2;

        for (n1 = 30; n1 < 331; n1 = n1 + 60) {
            ipoint2.setX(qRound(ipoint1.x() + cos((double)n1 * M_PI / 180.0) * blen));
            ipoint2.setY(qRound(ipoint1.y() + sin((double)n1 * M_PI / 180.0) * blen));
            tmp_pt = new DPoint(ipoint2);
            evt = new XDC_Event(EVT_ADD_BOND);
            evt->setStart(endpoint);
            evt->setEnd(tmp_pt);
            evt->setParam1(thick);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);
        }
        c->addSymbol(endpoint, "newman");
        endpoint = 0;
        update();
    }
    // MODE_DRAWNEWMAN_*: create Newman projection
    if ((mode == MODE_DRAWNEWMAN_GAUCHE) && (!mouse1down)) {
        endpoint = new DPoint;
        endpoint->set(curqpt);
        double blen = preferences.getBond_fixedlength();
        int n1;
        QPoint ipoint1 = endpoint->toQPoint();
        QPoint ipoint2;

        for (n1 = 30; n1 < 331; n1 = n1 + 120) {
            ipoint2.setX(qRound(ipoint1.x() + cos((double)n1 * M_PI / 180.0) * blen));
            ipoint2.setY(qRound(ipoint1.y() + sin((double)n1 * M_PI / 180.0) * blen));
            tmp_pt = new DPoint(ipoint2);
            evt = new XDC_Event(EVT_ADD_BOND);
            evt->setStart(endpoint);
            evt->setEnd(tmp_pt);
            evt->setParam1(thick);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);
        }
        for (n1 = 50; n1 < 351; n1 = n1 + 120) {
            ipoint2.setX(qRound(ipoint1.x() + cos((double)n1 * M_PI / 180.0) * blen));
            ipoint2.setY(qRound(ipoint1.y() + sin((double)n1 * M_PI / 180.0) * blen));
            tmp_pt = new DPoint(ipoint2);
            evt = new XDC_Event(EVT_ADD_BOND);
            evt->setStart(endpoint);
            evt->setEnd(tmp_pt);
            evt->setParam1(thick);
            evt->setColor(currentColor);
            emit XDCEventSignal(evt);
        }
        c->addSymbol(endpoint, "newman");
        endpoint = 0;
        update();
    }
    // MODE_RING: place ring
    if ((mode == MODE_RING) && (!mouse1down)) {
        endpoint = new DPoint;
        endpoint->set(curqpt);
        c->setThickKludge(thick);
        // are we smart-placing this ring?
        if ((smartplace != 0) && (highlightpoint != 0)) {
            qDebug() << "smart placement! Mode " << smartplace;
            if (smartplace == 1)
                c->SmartPlace(symbolfile, highlightpoint);
            if (smartplace == 2)
                c->SmartPlaceToo(symbolfile, highlightpoint);
            if (smartplace == 3)
                c->SmartPlaceThree(symbolfile, highlightpoint);
            // Clear highlighted point
            highlightpoint = 0;
            endpoint = 0;
            mode = MODE_SELECT_MULTIPLE_SELECTED;
            emit SignalSetStatusBar(tr("Select mode: left click on object to move"));

            setCursor(Qt::SizeAllCursor);
            selectionBox = c->selectionBox();
            update();
            smartplace = 0;
            c->setThickKludge(-1);
            return;
        }
        // translate ring to click point
        c->load(symbolfile);
        c->setThickKludge(-1);
        mode = MODE_SELECT_MULTIPLE_SELECTED;
        emit SignalSetStatusBar(tr("Select mode: left click on object to move, "
                                   "right click on object to edit"));

        setCursor(Qt::SizeAllCursor);
        selectionBox = c->selectionBox();
        double dx = endpoint->x - selectionBox.center().x();
        double dy = endpoint->y - selectionBox.center().y();

        // qDebug() << "Move by " << dx << "," << dy ;
        c->Move(dx, dy);
        selectionBox.translate((int)dx, (int)dy);

        endpoint = 0;
        update();
        smartplace = 0;
        return;
    }
}

void Render2D::mouseMoveEvent(QMouseEvent *e1) {
    // qDebug() << "mouseMoveEvent begin; mode: " << mode;
    if (localstring == "XDC_DELETE_ME") {
        delete localtexteditor;

        localtexteditor = 0;
        localstring = " ";
    }

    if (mode == MODE_DRAWBRACKET)
        highlightobject = 0;

    if ((mode == MODE_RING) && mouse1down)
        return;

    if ((mode == MODE_DRAWBEZIER) && mouse1down)
        return;

    QPoint curqpt = zoomCorrectReverse(e1->pos());

    if (preferences.getSnapGrid() != GRID_NONE) {
        curqpt = GridLock(curqpt);
    }

    lastmouse = curqpt;

    moved = true;

    DPoint tmp_pt(curqpt.x(), curqpt.y());
    Molecule *tmp_mol;

    tmp_molecule = 0;

    // Tools
    if ((mode >= 600) && (mode < 698)) {
        tmp_molecule = c->insideMolecule(&tmp_pt);
        if (tmp_molecule != 0) {
            QRect mr = tmp_molecule->BoundingBoxAll();
            mr.setLeft(mr.left() - 2);
            mr.setRight(mr.right() + 2);
            mr.setTop(mr.top() - 2);
            mr.setBottom(mr.bottom() + 2);
            selectionBox = mr;
            update();
        } else {
            update();
        }
    }

    if (mode == MODE_TOOL_CALCMW) {
        // check if we're over a molecule
        tmp_mol = c->insideMolecule(&tmp_pt);
        if (tmp_mol != 0) {
            Text *tmp_txt = tmp_mol->CalcMW();
            emit SignalSetStatusBar(tmp_txt->getText() + tr(", click to paste into drawing"));
            delete tmp_txt;
        } else {
            emit SignalSetStatusBar(tr("Click on a molecule to calculate its molecular weight"));
        }
        return;
    }

    if (mode == MODE_TOOL_CALCEF) {
        // check if we're over a molecule
        tmp_mol = c->insideMolecule(&tmp_pt); // doesn't copy pointer
        if (tmp_mol != 0) {
            Text *tmp_txt = tmp_mol->CalcEmpiricalFormula();
            emit SignalSetStatusBar(tmp_txt->getText() + tr(", click to paste into drawing"));
            delete tmp_txt;
        } else {
            emit SignalSetStatusBar(tr("Click on a molecule to calculate its empirical formula"));
        }
        return;
    }
    // MODE_TOOL_*: return
    if ((mode >= 600) && (mode < 698))
        return;

    // Dragging
    if (dragging && mouse3down) {
        qDebug() << "Start drag";
        // make pixmap of selection
        QRect savebox = selectionBox;
        int prevmode = mode;

        mode = MODE_SELECT;
        c->DeselectAll();
        // update();
        QPixmap pm;
        pm = this->grab(savebox);

        c->NewSelectRect(savebox, false);
        selectionBox = c->selectionBox();
        mode = prevmode;
        QDrag *drag = new QDrag(this);
        QMimeData *dragObject = new QMimeData();
        dragObject->setImageData(pm.toImage());
        drag->setMimeData(dragObject);
        QSize maxSize = pm.size().boundedTo(QSize(100, 100));
        drag->setPixmap(pm.scaled(maxSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
        drag->exec();
        dragging = false;
        update();
        setCursor(Qt::ArrowCursor);
        mouse3down = false;
        return;
    }
    // MODE_TEXT: call special function to handle text events
    if (mode == MODE_TEXT) {
        DrawText_mouseMoveEvent(e1);
        if (prevpos == 0) {
            prevpos = new DPoint;
        }
        prevpos->x = curqpt.x();
        prevpos->y = curqpt.y();
        return;
    }
    // MODE_SELECT: call special function to handle select events
    if (mode < 199) {
        Select_mouseMoveEvent(e1);
        if (prevpos == 0) {
            prevpos = new DPoint;
        }
        prevpos->x = curqpt.x();
        prevpos->y = curqpt.y();
        return;
    }
    // bool update;
    double ang, len;
    QString sang, slen;
    DPoint *prevhighlight = highlightpoint;
    Drawable *prevhighlightobject = highlightobject;

    // Create DPoint of current pointer position
    DPoint *e = new DPoint;
    DPoint *nearestPoint = 0;

    e->x = curqpt.x();
    e->y = curqpt.y();
    double dist, distobj;

    // Get DPoint of nearest point
    nearestPoint = c->FindNearestPoint(e, dist);
    // get Drawable of nearest object
    Drawable *no = c->FindNearestObject(e, distobj);

    // no buttons down
    if ((mouse1down == false) && (mouse3down == false)) {
        if (mode == MODE_DRAWARROW)
            return;

        // if (no != 0) {
        // highlight object if object close enough
        if ((no != 0) && (mode == MODE_ERASE) && (distobj < 6.0)) {
            highlightobject = no;
            if (prevhighlightobject != 0)
                prevhighlightobject->Highlight(false);
            highlightobject->Highlight(true);
            if (prevhighlightobject != highlightobject)
                update();
        }
        // unhighlight object if no object close
        if ((mode == MODE_ERASE) && (no == 0 || distobj >= 6.0)) {
            // Clear highlighted object
            highlightobject = 0;
            if (prevhighlightobject != 0)
                prevhighlightobject->Highlight(false);
            if (prevhighlightobject != highlightobject)
                update();
            return;
        }

        if ((no != 0) && (mode == MODE_TOOL_RETRO_BONDNAME) && (distobj < 6.0)) {
            if (no->Type() == TYPE_BOND) {
                highlightobject = no;
                if (prevhighlightobject != 0) {
                    prevhighlightobject->Highlight(false);
                }
                highlightobject->Highlight(true);
                if (prevhighlightobject != highlightobject) {
                    update();
                }
            }
        }
        // unhighlight object if no object close
        if ((mode == MODE_TOOL_RETRO_BONDNAME) && (no == 0 || distobj >= 6.0)) {
            // Clear highlighted object
            highlightobject = 0;
            if (prevhighlightobject != 0)
                prevhighlightobject->Highlight(false);
            if (prevhighlightobject != highlightobject)
                update();
            return;
        }
        // highlight text object preferentially when MODE_SELECT...
        if ((no != 0) && (mode == MODE_SELECT) && (no->Type() == TYPE_TEXT)) {
            highlightpoint = 0;
            highlightobject = no;
            if ((prevhighlightobject != highlightobject) && (prevhighlightobject != 0))
                prevhighlightobject->Highlight(false);
            highlightobject->Highlight(true);
            if (prevhighlightobject != highlightobject)
                update();
            // return since no need to check points
            return;
        }
        // highlight object if object close enough and no point close
        if ((no != 0) && (mode == MODE_SELECT) && (distobj < 6.0) && (dist >= 8.0)) {
            highlightobject = no;
            if (prevhighlightobject != 0)
                prevhighlightobject->Highlight(false);
            highlightobject->Highlight(true);
            if (prevhighlightobject != highlightobject)
                update();
            // return since no need to check points
            return;
        }
        // unhighlight object if no object close
        if ((mode == MODE_SELECT) && (no == 0 || distobj >= 6.0)) {
            // Clear highlighted object
            highlightobject = 0;
            if (prevhighlightobject != 0)
                prevhighlightobject->Highlight(false);
            if (prevhighlightobject != highlightobject)
                update();
            // don't return; go on to check for points
        }
        // unhighlight object if point close
        if ((mode == MODE_SELECT) && (dist < 8.0)) {
            // Clear highlighted object
            highlightobject = 0;
            if (prevhighlightobject != 0)
                prevhighlightobject->Highlight(false);
            if (prevhighlightobject != highlightobject)
                update();
            // don't return; go on to check for points
        }

        if (nearestPoint != 0 &&
            (mode == MODE_SYMBOL ||
             mode == MODE_DRAWLINE ||
             mode == MODE_RING ||
             mode == MODE_DRAWLINE_DASH ||
             mode == MODE_DRAWLINE_UP ||
             mode == MODE_DRAWLINE_DOWN ||
             mode == MODE_DRAWCHAIN)) {
            // MODE_SELECT and MODE_TEXT handled in their own functions
            if (dist < 6.0) {
                highlightpoint = nearestPoint;
            } else {
                highlightpoint = NULL;
            }

            if (prevhighlight != highlightpoint)
                update();
            return;

        }

        return; // rest of function only if mouse button down
    }           // no button down

    // we're probably going to draw something...
    update();
    directdraw = true;

    // MODE_DRAWLINE_DRAWING: draw temporary line
    if ((mode == MODE_DRAWLINE_DRAWING) && mouse1down) {
        qDebug() << "mouseMoveEvent: drawing line";
        // if within range of existing point AND the end point is not the same
        // as the start point, snap to that point
        if ((dist < 6.0) && (startpoint != nearestPoint)) {
            highlightpoint = nearestPoint;
            endpoint = nearestPoint;
        } else {
            endpoint = new DPoint;
            endpoint->x = curqpt.x();
            endpoint->y = curqpt.y();
            highlightpoint = 0;                // stop highlighting
            if (preferences.getBond_fixed()) { // fix endpoint if fixed set
                CorrectEndpoint_bond();
                nearestPoint = c->FindNearestPoint(endpoint, dist);
                if (dist < 2.0) {
                    highlightpoint = nearestPoint;
                    endpoint = nearestPoint;
                }
            }
        }
        ang = Drawable::getAngle(startpoint, endpoint);
        len = startpoint->distanceTo(endpoint);
        sang.setNum(ang);
        slen.setNum(len);
        emit SignalSetStatusBar(tr("Draw Line - Length = ") + slen + tr(", Angle = ") + sang);

        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        //        drawLine( startpoint->toQPoint(), endpoint->toQPoint(), thick,
        //        currentColor );
        directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWLINE_DASH_DRAWING: draw temporary line
    if ((mode == MODE_DRAWLINE_DASH_DRAWING) && mouse1down) {
        qDebug() << "mouseMoveEvent: drawing dashed line";
        // if within range of existing point AND the end point is not the same
        // as the start point, snap to that point
        if ((dist < 6.0) && (startpoint != nearestPoint)) {
            highlightpoint = nearestPoint;
            endpoint = nearestPoint;
        } else {
            endpoint = new DPoint;
            endpoint->x = curqpt.x();
            endpoint->y = curqpt.y();
            highlightpoint = 0;                // stop highlighting
            if (preferences.getBond_fixed()) { // fix endpoint if fixed set
                CorrectEndpoint_bond();
                nearestPoint = c->FindNearestPoint(endpoint, dist);
                if (dist < 2.0) {
                    highlightpoint = nearestPoint;
                    endpoint = nearestPoint;
                }
            }
        }
        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        drawLine(startpoint->toQPoint(), endpoint->toQPoint(), 1, currentColor, 1);
        directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWCHAIN_DRAWING: draw temporary aliphatic chain
    if ((mode == MODE_DRAWCHAIN_DRAWING) && mouse1down) {
        // if within range of existing point AND the end point is not the same
        // as the start point, snap to that point
        if ((dist < 6.0) && (startpoint != nearestPoint)) {
            highlightpoint = nearestPoint;
            endpoint = nearestPoint;
        } else {
            endpoint = new DPoint;
            endpoint->x = curqpt.x();
            endpoint->y = curqpt.y();
            highlightpoint = 0; // stop highlighting
        }
        ang = Drawable::getAngle(startpoint, endpoint);
        double alt_ang1 = (ang - 30.0) * M_PI / 180.0;
        double alt_ang2 = (ang + 30.0) * M_PI / 180.0;
        double alt_swap;

        if (e1->modifiers() == Qt::ShiftModifier) {
            alt_swap = alt_ang1;
            alt_ang1 = alt_ang2;
            alt_ang2 = alt_swap;
        }
        len = startpoint->distanceTo(endpoint);
        double blen = preferences.getBond_fixedlength();
        int n1, nseg = qRound(len / blen);

        slen.setNum(nseg);
        emit SignalSetStatusBar(tr("Draw aliphatic chain - Length = ") + slen + tr(" bonds"));
        QPoint spoint, epoint, ipoint1, ipoint2;

        spoint = startpoint->toQPoint();
        ipoint1 = spoint;
        chainPoints.clear();
        //    painter->begin(this);
        for (n1 = 0; n1 < nseg; n1++) {
            ipoint2.setX(qRound(ipoint1.x() + cos(alt_ang1) * blen));
            ipoint2.setY(qRound(ipoint1.y() + sin(alt_ang1) * blen));
            chainPoints << ipoint1 << ipoint2;
            drawLine(ipoint1, ipoint2, 1, currentColor);
            alt_swap = alt_ang1;
            alt_ang1 = alt_ang2;
            alt_ang2 = alt_swap;
            ipoint1 = ipoint2;
        }
        directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWLINE_UP_DRAWING: draw temporary line
    if ((mode == MODE_DRAWLINE_UP_DRAWING) && mouse1down) {
        // if within range of existing point AND the end point is not the same
        // as the start point, snap to that point
        if ((dist < 6.0) && (startpoint != nearestPoint)) {
            highlightpoint = nearestPoint;
            endpoint = nearestPoint;
        } else {
            endpoint = new DPoint;
            endpoint->x = curqpt.x();
            endpoint->y = curqpt.y();
            highlightpoint = 0;                // stop highlighting
            if (preferences.getBond_fixed()) { // fix endpoint if fixed set
                CorrectEndpoint_bond();
                nearestPoint = c->FindNearestPoint(endpoint, dist);
                if (dist < 2.0) {
                    highlightpoint = nearestPoint;
                    endpoint = nearestPoint;
                }
            }
        }
        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        //        drawUpLine( startpoint->toQPoint(), endpoint->toQPoint(),
        //        currentColor );
        directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWLINE_DOWN_DRAWING: draw temporary line
    if ((mode == MODE_DRAWLINE_DOWN_DRAWING) && mouse1down) {
        // if within range of existing point AND the end point is not the same
        // as the start point, snap to that point
        if ((dist < 6.0) && (startpoint != nearestPoint)) {
            highlightpoint = nearestPoint;
            endpoint = nearestPoint;
        } else {
            endpoint = new DPoint;
            endpoint->x = curqpt.x();
            endpoint->y = curqpt.y();
            highlightpoint = 0;                // stop highlighting
            if (preferences.getBond_fixed()) { // fix endpoint if fixed set
                CorrectEndpoint_bond();
                nearestPoint = c->FindNearestPoint(endpoint, dist);
                if (dist < 2.0) {
                    highlightpoint = nearestPoint;
                    endpoint = nearestPoint;
                }
            }
        }
        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        //        drawDownLine( startpoint->toQPoint(), endpoint->toQPoint(),
        //        currentColor );
        directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWWAVYLINE_DRAWING: draw temporary line
    if ((mode == MODE_DRAWWAVYLINE_DRAWING) && mouse1down) {
        // if within range of existing point AND the end point is not the same
        // as the start point, snap to that point
        if ((dist < 6.0) && (startpoint != nearestPoint)) {
            highlightpoint = nearestPoint;
            endpoint = nearestPoint;
        } else {
            endpoint = new DPoint;
            endpoint->x = curqpt.x();
            endpoint->y = curqpt.y();
            highlightpoint = 0;                // stop highlighting
            if (preferences.getBond_fixed()) { // fix endpoint if fixed set
                CorrectEndpoint_bond();
                nearestPoint = c->FindNearestPoint(endpoint, dist);
                if (dist < 2.0) {
                    highlightpoint = nearestPoint;
                    endpoint = nearestPoint;
                }
            }
        }
        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        //        drawWavyLine( startpoint->toQPoint(), endpoint->toQPoint(),
        //        currentColor );
        directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWARROW_DRAWING: draw temporary arrow
    if ((mode == MODE_DRAWARROW_DRAWING) && mouse1down) {
        endpoint = new DPoint;
        endpoint->x = curqpt.x();
        endpoint->y = curqpt.y();
        if (preferences.getArrow_fixed()) { // fix endpoint if fixed set
            CorrectEndpoint_arrow();
        }
        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        //        drawArrow( startpoint->toQPoint(), endpoint->toQPoint(),
        //        currentColor, bracket_type, thick );
        directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWCURVEARROW_DRAWING: draw temporary curve arrow
    if ((mode == MODE_DRAWCURVEARROW_DRAWING) && mouse1down) {
        endpoint = new DPoint;
        endpoint->x = curqpt.x();
        endpoint->y = curqpt.y();
        // should not draw curve arrows fixed, even if selected
        // if (fixed) { // fix endpoint if fixed set
        //  CorrectEndpoint();
        //}
        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        // drawCurveArrow( startpoint->toQPoint(), endpoint->toQPoint(),
        // currentColor, symbolfile ); directdraw = false;
        //    painter->end();
        return;
    }
    // MODE_DRAWBRACKET_DRAWING: draw temporary bracket
    if ((mode == MODE_DRAWBRACKET_DRAWING) && mouse1down) {
        endpoint = new DPoint;
        endpoint->x = curqpt.x();
        endpoint->y = curqpt.y();
        // adjust position for square and circle "brackets"
        if ((bracket_type == BRACKET_CLOSEDSQUARE) || (bracket_type == BRACKET_CIRCLE)) {
            double dx = endpoint->x - startpoint->x;
            double dy = endpoint->y - startpoint->y;

            if (fabs(dx) < fabs(dy)) {
                endpoint->x = startpoint->x + std::copysign(dy, dx);
            } else {
                endpoint->y = startpoint->y + std::copysign(dx, dy);
            }
        }

        QColor icv;
        //    painter->begin(this);
        startPoint = startpoint->toQPoint();
        endPoint = endpoint->toQPoint();
        //        drawBracket( startpoint->toQPoint(), endpoint->toQPoint(),
        //        currentColor, bracket_type, icv );
        directdraw = false;
        //    painter->end();
        return;
    }
    // just in case
    directdraw = false;
    qDebug() << "mouseMoveEvent end";
}

// handle resize event
void Render2D::resizeEvent(QResizeEvent *rz) { dbuffer = QPixmap(renderWidth, renderHeight); }

void Render2D::paintEvent(QPaintEvent *) {
    if (localstring == "XDC_DELETE_ME") {
        delete localtexteditor;

        localtexteditor = 0;
        localstring = " ";
    }
    // draw background
    // drawFillBox(QPoint(0,0), QPoint(renderWidth, renderHeight), bgcolor);

    // initialize global painter object
    if (outputDevice == OUTPUT_PRINTER) {
        // qDebug() << "printer";
        painter->begin(printer);
    } else if (outputDevice == OUTPUT_SCREEN) {
        // qDebug() << "screen";
        if (directdraw) {
            painter->begin(this);
        } else {
            // qDebug() << "paintEvent painter begin";
            painter->begin(this);
        }
        /// TODO: make that an option
        painter->setRenderHint(QPainter::Antialiasing, true);
    }
    // clear buffer pixmap
    dbuffer.fill(bgcolor);
    // draw grid as needed
    // int i1, cl1;

    if (preferences.getDrawGrid() == GRID_SQUARE || preferences.getDrawGrid() == GRID_HEX)
        painter->drawPixmap(0, 0, grid);

    painter->scale(zoomFactor, zoomFactor);

    // draw all data
    c->drawAll();

    // qDebug() << "paintEvent mode: " << mode;
    // local/temporary draw operations
    if (highlightpoint != 0) {
        qDebug() << "tempBox";
        drawBox(QPoint(qRound(highlightpoint->x - 2.0), qRound(highlightpoint->y - 2.0)),
                QPoint(qRound(highlightpoint->x + 2.0), qRound(highlightpoint->y + 2.0)),
                QColor(0, 0, 0));
    }
    // if multiple selection, draw rotate and resize handles
    if (mode == MODE_SELECT_MULTIPLE) {
        drawBox(zoomCorrectReverse(selectOrigin), zoomCorrectReverse(selectPrev), QColor(0, 0, 0));
    }

    // draw box if over molecule in Tool modes
    if ((mode >= 600) && (mode < 698)) {
        if (tmp_molecule != 0) {
            qDebug() << "tool mode";
            drawBox((selectionBox.topLeft()), (selectionBox.bottomRight()), QColor(0, 0, 255));
        }
    }

    if ((mode > 120) && (mode < 129)) {
        // draw selection box
        qDebug() << "multiple selectionBox";
        drawBox((selectionBox.topLeft()), (selectionBox.bottomRight()), QColor(0, 0, 0));
        // draw rotate handle
        rotateHandle = selectionBox.topRight();
        drawFillBox((QPoint(rotateHandle.x() - 5, rotateHandle.y())),
                    (QPoint(rotateHandle.x(), rotateHandle.y() + 5)), QColor(0, 0, 0));
        // draw resize handle
        resizeHandle = selectionBox.bottomRight();
        drawFillBox((QPoint(resizeHandle.x() - 5, resizeHandle.y() - 5)), (resizeHandle),
                    QColor(0, 0, 0));
    }

    if (mode == 91) {
        // draw selection box
        qDebug() << "simple selectionBox";
        drawBox((selectionBox.topLeft()), (selectionBox.bottomRight()), QColor(0, 0, 0));
        // draw rotate handle
        rotateHandle = selectionBox.topRight();
        drawFillBox((QPoint(rotateHandle.x() - 5, rotateHandle.y())),
                    (QPoint(rotateHandle.x(), rotateHandle.y() + 5)), QColor(0, 0, 0));
        // draw resize handle
        resizeHandle = selectionBox.bottomRight();
        drawFillBox((QPoint(resizeHandle.x() - 5, resizeHandle.y() - 5)), (resizeHandle),
                    QColor(0, 0, 0));
    }

    /// previews
    if (mode == MODE_SELECT_LASSO) {
        drawPolyline(curr_lasso, Qt::blue);
    } else if (mode == MODE_DRAWLINE_DRAWING) {
        drawLine(startPoint, endPoint, thick, currentColor);
    } else if (mode == MODE_DRAWLINE_DASH_DRAWING) {
        drawLine(startPoint, endPoint, thick, currentColor, 1);
    } else if (mode == MODE_DRAWCHAIN_DRAWING) {
        drawPolyline(chainPoints, currentColor);
    } else if (mode == MODE_DRAWLINE_UP_DRAWING) {
        drawUpLine(startPoint, endPoint, currentColor);
    } else if (mode == MODE_DRAWLINE_DOWN_DRAWING) {
        drawDownLine(startPoint, endPoint, currentColor);
    } else if (mode == MODE_DRAWWAVYLINE_DRAWING) {
        drawWavyLine(startPoint, endPoint, currentColor);
    } else if (mode == MODE_DRAWARROW_DRAWING) {
        drawArrow(startPoint, endPoint, currentColor, bracket_type, thick);
    } else if (mode == MODE_DRAWCURVEARROW_DRAWING) {
        drawCurveArrow(startPoint, endPoint, currentColor, symbolfile);
    } else if (mode == MODE_DRAWBRACKET_DRAWING) {
        drawBracket(startPoint, endPoint, currentColor, bracket_type, currentColor);
    }
    // now bitBlt buffer onto widget
    //    bitBlt(this, 0, 0, &dbuffer);
    finishedPainting = true;
    // qDebug() << "paintEvent painter end";
    painter->end();
}

void Render2D::zoomEvent() {
    // read zoom factor from preferences and act accordingly
    double ozf = zoomFactor;

    zoomFactor = (double)preferences.getZoom() / 100.0;

    // scale canvas
    double sf = zoomFactor / ozf;

    renderWidth = qRound((double)renderWidth * sf);
    renderHeight = qRound((double)renderHeight * sf);
    dbuffer = QPixmap(renderWidth, renderHeight);
    resize(renderWidth, renderHeight);
    createGrid();
    // redraw
    update();
}

void Render2D::createGrid() {
    grid = QPixmap(renderWidth, renderHeight);
    grid.fill(bgcolor);
    QPainter *gridPainter = new QPainter(&grid);

    /// TODO: make that an option
    gridPainter->setRenderHint(QPainter::Antialiasing, true);

    float i1, cl1;

    if (preferences.getDrawGrid() == GRID_SQUARE) {
        i1 = preferences.getGridSpacing() * zoomFactor;
        // int width = parentWidget()->size().width();
        // int height = parentWidget()->size().height();

        gridPainter->setPen(QPen(QColor(128, 128, 128), 1, Qt::DotLine));
        // draw horizontal
        for (cl1 = i1; cl1 < renderHeight; cl1 += i1) {
            gridPainter->drawLine(0, cl1, renderWidth, cl1);
        }

        // draw vertical
        for (cl1 = i1; cl1 < renderWidth; cl1 += i1) {
            gridPainter->drawLine(cl1, 0, cl1, renderHeight);
        }
    } else if (preferences.getDrawGrid() == GRID_HEX) {
        bool hexoffset = true;

        i1 = preferences.getGridSpacing() * zoomFactor;
        float hexhoriz = i1 / 2.0;
        float hexgap = 2.0 * hexhoriz + i1;
        float hexelevation = hexhoriz * 1.73205;
        float hex_initx, hex_rightx;

        gridPainter->setPen(QPen(QColor(128, 128, 128), 1, Qt::DotLine));

        for (float hex_y = 0.0; hex_y < renderHeight; hex_y += hexelevation) {
            if (hexoffset) {
                hex_initx = hexhoriz;

                // Draw missing line in first column
                gridPainter->drawLine(hexhoriz, hex_y, 0, hex_y - hexelevation);
            } else {
                hex_initx = hexgap;
            }

            for (float hex_x = hex_initx; hex_x < renderWidth; hex_x += hexgap) {
                hex_rightx = hex_x + i1;
                gridPainter->drawLine(hex_x, hex_y, hex_rightx, hex_y);
                gridPainter->drawLine(hex_x, hex_y, hex_x - hexhoriz, hex_y + hexelevation);
                gridPainter->drawLine(hex_rightx, hex_y, hex_rightx + hexhoriz,
                                      hex_y + hexelevation);
                hex_x = hex_rightx;
            }
            hexoffset = !hexoffset;
        }
    }
    delete gridPainter;
}
