/*
    Rosegarden
    A MIDI and audio sequencer and musical notation editor.
 
    This program is Copyright 2000-2008
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <richard.bown@ferventsoftware.com>
 
    The moral rights of Guillaume Laurent, Chris Cannam, and Richard
    Bown to claim authorship of this work have been asserted.
 
    Other copyrights also apply to some parts of this work.  Please
    see the AUTHORS file and individual file headers for details.
 
    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 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/


#include "MarkerRuler.h"

#include "misc/Debug.h"
#include "misc/Strings.h"
#include "base/Composition.h"
#include "base/RulerScale.h"
#include "document/RosegardenGUIDoc.h"
#include "gui/general/GUIPalette.h"
#include "gui/general/HZoomable.h"
#include "gui/dialogs/MarkerModifyDialog.h"
#include "commands/edit/ModifyMarkerCommand.h"
#include "document/MultiViewCommandHistory.h"
#include <kxmlguifactory.h>
#include <tqbrush.h>
#include <tqcursor.h>
#include <tqfont.h>
#include <tqfontmetrics.h>
#include <tqpainter.h>
#include <tqpen.h>
#include <tqpoint.h>
#include <tqpopupmenu.h>
#include <tqrect.h>
#include <tqsize.h>
#include <tqstring.h>
#include <tqwidget.h>
#include <tdelocale.h>
#include <tdeaction.h>
#include <kstandarddirs.h>
#include <tqtooltip.h>


namespace Rosegarden
{

MarkerRuler::MarkerRuler(RosegardenGUIDoc *doc,
                         RulerScale *rulerScale,
                         int barHeight,
                         double xorigin,
                         TQWidget* parent,
                         const char* name,
                         WFlags f)
        : TQWidget(parent, name, f),
        m_barHeight(barHeight),
        m_xorigin(xorigin),
        m_currentXOffset(0),
        m_width(-1),
        m_clickX(0),
        m_menu(0),
        m_doc(doc),
        m_rulerScale(rulerScale),
        m_parentMainWindow(dynamic_cast<TDEMainWindow*>(doc->parent()))
{
    // If the parent window has a main window above it, we need to use
    // that as the parent main window, not the document's parent.
    // Otherwise we'll end up adding all actions to the same
    // (document-level) action collection regardless of which window
    // we're in.
    TQObject *probe = parent;
    while (probe && !dynamic_cast<TDEMainWindow *>(probe)) probe = probe->parent();
    if (probe) m_parentMainWindow = dynamic_cast<TDEMainWindow *>(probe);

    //    m_barFont = new TQFont("helvetica", 12);
    //    m_barFont->setPixelSize(12);
    m_barFont = new TQFont();
    m_barFont->setPointSize(10);

    TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
    TQIconSet icon;

    // Use the event insert, delete, edit icons because they are
    // actually generic enough to serve for anything.  Let's hope they
    // don't become more event-specific in future...

    icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/event-insert.png"));
    new TDEAction(i18n("Insert Marker"), icon, 0, this,
             TQ_SLOT(slotInsertMarkerHere()), actionCollection(),
             "insert_marker_here");
    
    new TDEAction(i18n("Insert Marker at Playback Position"), 0, this,
             TQ_SLOT(slotInsertMarkerAtPointer()), actionCollection(),
             "insert_marker_at_pointer");

    icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/event-delete.png"));
    new TDEAction(i18n("Delete Marker"), icon, 0, this,
             TQ_SLOT(slotDeleteMarker()), actionCollection(),
             "delete_marker");
    
    icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/event-edit.png"));
    new TDEAction(i18n("Edit Marker..."), icon, 0, this,
                 TQ_SLOT(slotEditMarker()), actionCollection(),
                 "edit_marker");

    TQToolTip::add
        (this, i18n("Click on a marker to move the playback pointer.\nShift-click to set a range between markers.\nDouble-click to open the marker editor."));
}

MarkerRuler::~MarkerRuler()
{
    delete m_barFont;
    // we have to do this so that the menu is re-created properly
    // when the main window is itself recreated (on a File->New for instance)
    KXMLGUIFactory* factory = m_parentMainWindow->factory();
    if (factory)
        factory->removeClient(this);
}

void
MarkerRuler::createMenu()
{             
    setXMLFile("markerruler.rc");
    
    KXMLGUIFactory* factory = m_parentMainWindow->factory();
    factory->addClient(this);

    TQWidget* tmp = factory->container("marker_ruler_menu", this);

//    if (!tmp) {
//        RG_DEBUG << "MarkerRuler::createMenu() menu not found\n"
//                 << domDocument().toString(4) << endl;
//    }
    
    m_menu = dynamic_cast<TQPopupMenu*>(tmp);
    
    if (!m_menu) {
        RG_DEBUG << "MarkerRuler::createMenu() failed\n";
    }
}


void 
MarkerRuler::scrollHoriz(int x)
{
    m_currentXOffset = static_cast<int>( -x / getHScaleFactor());
    repaint();
}

TQSize 
MarkerRuler::sizeHint() const
{
    int lastBar =
        m_rulerScale->getLastVisibleBar();
    double width =
        m_rulerScale->getBarPosition(lastBar) +
        m_rulerScale->getBarWidth(lastBar) + m_xorigin;

    return TQSize(std::max(int(width), m_width), m_barHeight);
}

TQSize 
MarkerRuler::minimumSizeHint() const
{
    double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;

    return TQSize(static_cast<int>(firstBarWidth), m_barHeight);
}

void
MarkerRuler::slotInsertMarkerHere()
{
    emit addMarker(getClickPosition());    
}

void
MarkerRuler::slotInsertMarkerAtPointer()
{
    emit addMarker(m_doc->getComposition().getPosition());
}

void
MarkerRuler::slotDeleteMarker()
{
    RG_DEBUG << "MarkerRuler::slotDeleteMarker()\n";
    
    Rosegarden::Marker* marker = getMarkerAtClickPosition();
    
    if (marker)
        emit deleteMarker(marker->getID(),
                          marker->getTime(),
                          marker->getName().c_str(),
                          marker->getDescription().c_str());
}

void
MarkerRuler::slotEditMarker()
{
    Rosegarden::Marker* marker = getMarkerAtClickPosition();

    if (!marker) return;

    // I think the ruler should be doing all this stuff itself, or
    // emitting signals connected to a dedicated marker model object,
    // not just relying on the app object.  Same goes for practically
    // everything else we do.  Hey ho.  Having this here is
    // inconsistent with the other methods, so if anyone wants to move
    // it, be my guest.

    MarkerModifyDialog dialog(this, &m_doc->getComposition(), marker);
    if (dialog.exec() == TQDialog::Accepted) {
        ModifyMarkerCommand *command =
            new ModifyMarkerCommand(&m_doc->getComposition(),
                                    marker->getID(),
                                    dialog.getOriginalTime(),
                                    dialog.getTime(),
                                    qstrtostr(dialog.getName()),
                                    qstrtostr(dialog.getDescription()));
        m_doc->getCommandHistory()->addCommand(command);
    }
}

timeT
MarkerRuler::getClickPosition()
{
    timeT t = m_rulerScale->getTimeForX
              (m_clickX - m_xorigin - m_currentXOffset);

    return t;
}

Rosegarden::Marker*
MarkerRuler::getMarkerAtClickPosition()
{
    TQRect clipRect = visibleRect();

    int firstBar = m_rulerScale->getBarForX(clipRect.x() -
                                            m_currentXOffset -
                                            m_xorigin);
    int lastBar = m_rulerScale->getLastVisibleBar();
    if (firstBar < m_rulerScale->getFirstVisibleBar()) {
        firstBar = m_rulerScale->getFirstVisibleBar();
    }

    Composition &comp = m_doc->getComposition();
    Composition::markercontainer markers = comp.getMarkers();

    timeT start = comp.getBarStart(firstBar);
    timeT end = comp.getBarEnd(lastBar);

    // need these to calculate the visible extents of a marker tag
    TQPainter painter(this);
    painter.setFont(*m_barFont);
    TQFontMetrics metrics = painter.fontMetrics();

    for (Composition::markerconstiterator i = markers.begin();
            i != markers.end(); ++i) {

        if ((*i)->getTime() >= start && (*i)->getTime() < end) {

            TQString name(strtoqstr((*i)->getName()));

            int x = m_rulerScale->getXForTime((*i)->getTime())
                    + m_xorigin + m_currentXOffset;

            int width = metrics.width(name) + 5;

            int nextX = -1;
            Composition::markerconstiterator j = i;
            ++j;
            if (j != markers.end()) {
                nextX = m_rulerScale->getXForTime((*j)->getTime())
                        + m_xorigin + m_currentXOffset;
            }

            if (m_clickX >= x && m_clickX <= x + width) {

                if (nextX < x || m_clickX <= nextX) {

                    return *i;
                }
            }
        }
    }

    return 0L;
}
    
void
MarkerRuler::paintEvent(TQPaintEvent*)
{
    TQPainter painter(this);
    painter.setFont(*m_barFont);

    if (getHScaleFactor() != 1.0)
        painter.scale(getHScaleFactor(), 1.0);

    TQRect clipRect = visibleRect();

    int firstBar = m_rulerScale->getBarForX(clipRect.x() -
                                            m_currentXOffset -
                                            m_xorigin);
    int lastBar = m_rulerScale->getLastVisibleBar();
    if (firstBar < m_rulerScale->getFirstVisibleBar()) {
        firstBar = m_rulerScale->getFirstVisibleBar();
    }

    painter.drawLine(m_currentXOffset, 0, static_cast<int>(visibleRect().width() / getHScaleFactor()), 0);

    float minimumWidth = 25.0;
    float testSize = ((float)(m_rulerScale->getBarPosition(firstBar + 1) -
                              m_rulerScale->getBarPosition(firstBar)))
                     / minimumWidth;

    int every = 0;
    int count = 0;

    if (testSize < 1.0) {
        every = (int(1.0 / testSize));

        if (every % 2 == 0)
            every++;
    }

    for (int i = firstBar; i <= lastBar; ++i) {

        double x = m_rulerScale->getBarPosition(i) + m_xorigin + m_currentXOffset;

        if ((x * getHScaleFactor()) > clipRect.x() + clipRect.width())
            break;

        // always the first bar number
        if (every && i != firstBar) {
            if (count < every) {
                count++;
                continue;
            }

            // reset count if we passed
            count = 0;
        }

        // adjust count for first bar line
        if (every == firstBar)
            count++;

        if (i != lastBar) {
            painter.drawLine(static_cast<int>(x), 0, static_cast<int>(x), m_barHeight);

            // disable worldXForm for text
            TQPoint textDrawPoint = painter.xForm(TQPoint(static_cast<int>(x + 4), 12));

            bool enableXForm = painter.hasWorldXForm();
            painter.setWorldXForm(false);

            if (i >= 0)
                painter.drawText(textDrawPoint, TQString("%1").arg(i + 1));

            painter.setWorldXForm(enableXForm);
        } else {
            const TQPen normalPen = painter.pen();
            ;
            TQPen endPen(black, 2);
            painter.setPen(endPen);
            painter.drawLine(static_cast<int>(x), 0, static_cast<int>(x), m_barHeight);
            painter.setPen(normalPen);
        }
    }

    if (m_doc) {
        Composition &comp = m_doc->getComposition();
        Composition::markercontainer markers = comp.getMarkers();
        Composition::markerconstiterator it;

        timeT start = comp.getBarStart(firstBar);
        timeT end = comp.getBarEnd(lastBar);

        TQFontMetrics metrics = painter.fontMetrics();

        for (it = markers.begin(); it != markers.end(); ++it) {
            if ((*it)->getTime() >= start && (*it)->getTime() < end) {
                TQString name(strtoqstr((*it)->getName()));

                double x = m_rulerScale->getXForTime((*it)->getTime())
                           + m_xorigin + m_currentXOffset;

                painter.fillRect(static_cast<int>(x), 1,
                                 static_cast<int>(metrics.width(name) + 5),
                                 m_barHeight - 2,
                                 TQBrush(GUIPalette::getColour(GUIPalette::MarkerBackground)));

                painter.drawLine(int(x), 1, int(x), m_barHeight - 2);
                painter.drawLine(int(x) + 1, 1, int(x) + 1, m_barHeight - 2);

                TQPoint textDrawPoint = painter.xForm
                                       (TQPoint(static_cast<int>(x + 3), m_barHeight - 4));

                // disable worldXForm for text
                bool enableXForm = painter.hasWorldXForm();
                painter.setWorldXForm(false);

                painter.drawText(textDrawPoint, name);

                painter.setWorldXForm(enableXForm);
            }
        }
    }
}

void
MarkerRuler::mousePressEvent(TQMouseEvent *e)
{
    RG_DEBUG << "MarkerRuler::mousePressEvent: x = " << e->x() << endl;

    if (!m_doc || !e)
        return;

    m_clickX = e->x();
    Rosegarden::Marker* clickedMarker = getMarkerAtClickPosition();
    
    // if right-click, show popup menu
    //
    if (e->button() == TQt::RightButton) {
        if (!m_menu)
            createMenu();
        if (m_menu) {
            actionCollection()->action("delete_marker")->setEnabled(clickedMarker != 0);
            actionCollection()->action("edit_marker")->setEnabled(clickedMarker != 0);
            m_menu->exec(TQCursor::pos());
        }
        return;       
    }
            
    bool shiftPressed = ((e->state() & TQt::ShiftButton) != 0);

    Composition &comp = m_doc->getComposition();
    Composition::markercontainer markers = comp.getMarkers();

    if (shiftPressed) { // set loop

        timeT t = m_rulerScale->getTimeForX
                  (e->x() - m_xorigin - m_currentXOffset);

        timeT prev = 0;

        for (Composition::markerconstiterator i = markers.begin();
                i != markers.end(); ++i) {

            timeT cur = (*i)->getTime();

            if (cur >= t) {
                emit setLoop(prev, cur);
                return ;
            }

            prev = cur;
        }

        if (prev > 0)
            emit setLoop(prev, comp.getEndMarker());

    } else { // set pointer to clicked marker

        if (clickedMarker)
            emit setPointerPosition(clickedMarker->getTime());
    }
}

void
MarkerRuler::mouseDoubleClickEvent(TQMouseEvent *)
{
    RG_DEBUG << "MarkerRuler::mouseDoubleClickEvent" << endl;

    emit editMarkers();
}

}
#include "MarkerRuler.moc"
