/*
    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 "EventView.h"
#include "EventViewItem.h"
#include "TrivialVelocityDialog.h"
#include "base/BaseProperties.h"
#include "misc/Debug.h"
#include "misc/Strings.h"
#include "base/Clipboard.h"
#include "base/Event.h"
#include "base/MidiTypes.h"
#include "base/NotationTypes.h"
#include "base/RealTime.h"
#include "base/Segment.h"
#include "base/SegmentPerformanceHelper.h"
#include "base/Selection.h"
#include "base/Track.h"
#include "base/TriggerSegment.h"
#include "commands/edit/CopyCommand.h"
#include "commands/edit/CutCommand.h"
#include "commands/edit/EraseCommand.h"
#include "commands/edit/EventEditCommand.h"
#include "commands/edit/PasteEventsCommand.h"
#include "commands/edit/EventInsertionCommand.h"
#include "commands/segment/SegmentLabelCommand.h"
#include "commands/segment/SetTriggerSegmentBasePitchCommand.h"
#include "commands/segment/SetTriggerSegmentBaseVelocityCommand.h"
#include "commands/segment/SetTriggerSegmentDefaultRetuneCommand.h"
#include "commands/segment/SetTriggerSegmentDefaultTimeAdjustCommand.h"
#include "document/RosegardenGUIDoc.h"
#include "document/ConfigGroups.h"
#include "gui/dialogs/EventEditDialog.h"
#include "gui/dialogs/PitchDialog.h"
#include "gui/dialogs/SimpleEventEditDialog.h"
#include "gui/general/EditViewBase.h"
#include "gui/general/MidiPitchLabel.h"
#include "gui/kdeext/KTmpStatusMsg.h"
#include "gui/dialogs/EventFilterDialog.h"
#include <tdeaction.h>
#include <tdeconfig.h>
#include <tdelocale.h>
#include <kstatusbar.h>
#include <kstandarddirs.h>
#include <tdeglobal.h>
#include <klineeditdlg.h>
#include <tdelistview.h>
#include <kxmlguiclient.h>
#include <tqbuttongroup.h>
#include <tqcanvas.h>
#include <tqcheckbox.h>
#include <tqdialog.h>
#include <tqframe.h>
#include <tqgroupbox.h>
#include <tqiconset.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqlistview.h>
#include <tqpixmap.h>
#include <tqpoint.h>
#include <tqpopupmenu.h>
#include <tqpushbutton.h>
#include <tqsize.h>
#include <tqstring.h>
#include <tqwidget.h>
#include <algorithm>


namespace Rosegarden
{

int
EventView::m_lastSetEventFilter = -1;


EventView::EventView(RosegardenGUIDoc *doc,
                     std::vector<Segment *> segments,
                     TQWidget *parent):
        EditViewBase(doc, segments, 2, parent, "eventview"),
        m_eventFilter(Note | Text | SystemExclusive | Controller |
                      ProgramChange | PitchBend | Indication | Other),
        m_menu(0)
{
    m_isTriggerSegment = false;
    m_triggerName = m_triggerPitch = m_triggerVelocity = 0;

    if (!segments.empty()) {
        Segment *s = *segments.begin();
        if (s->getComposition()) {
            int id = s->getComposition()->getTriggerSegmentId(s);
            if (id >= 0)
                m_isTriggerSegment = true;
        }
    }

    if (m_lastSetEventFilter < 0)
        m_lastSetEventFilter = m_eventFilter;
    else
        m_eventFilter = m_lastSetEventFilter;

    initStatusBar();
    setupActions();

    // define some note filtering buttons in a group
    //
    m_filterGroup =
        new TQButtonGroup(1, TQt::Horizontal, i18n("Event filters"), getCentralWidget());

    m_noteCheckBox = new TQCheckBox(i18n("Note"), m_filterGroup);
    m_programCheckBox = new TQCheckBox(i18n("Program Change"), m_filterGroup);
    m_controllerCheckBox = new TQCheckBox(i18n("Controller"), m_filterGroup);
    m_pitchBendCheckBox = new TQCheckBox(i18n("Pitch Bend"), m_filterGroup);
    m_sysExCheckBox = new TQCheckBox(i18n("System Exclusive"), m_filterGroup);
    m_keyPressureCheckBox = new TQCheckBox(i18n("Key Pressure"), m_filterGroup);
    m_channelPressureCheckBox = new TQCheckBox(i18n("Channel Pressure"), m_filterGroup);
    m_restCheckBox = new TQCheckBox(i18n("Rest"), m_filterGroup);
    m_indicationCheckBox = new TQCheckBox(i18n("Indication"), m_filterGroup);
    m_textCheckBox = new TQCheckBox(i18n("Text"), m_filterGroup);
    m_otherCheckBox = new TQCheckBox(i18n("Other"), m_filterGroup);
    m_grid->addWidget(m_filterGroup, 2, 0);

    // Connect up
    //
    connect(m_filterGroup, TQ_SIGNAL(released(int)),
            TQ_SLOT(slotModifyFilter(int)));

    m_eventList = new TDEListView(getCentralWidget());
    m_eventList->setItemsRenameable(true);

    m_grid->addWidget(m_eventList, 2, 1);

    if (m_isTriggerSegment) {

        int id = segments[0]->getComposition()->getTriggerSegmentId(segments[0]);
        TriggerSegmentRec *rec =
            segments[0]->getComposition()->getTriggerSegmentRec(id);

        TQGroupBox *groupBox = new TQGroupBox
                              (1, TQt::Horizontal, i18n("Triggered Segment Properties"), getCentralWidget());

        TQFrame *frame = new TQFrame(groupBox);
        TQGridLayout *layout = new TQGridLayout(frame, 5, 3, 5, 5);

        layout->addWidget(new TQLabel(i18n("Label:  "), frame), 0, 0);
        TQString label = strtoqstr(segments[0]->getLabel());
        if (label == "")
            label = i18n("<no label>");
        m_triggerName = new TQLabel(label, frame);
        layout->addWidget(m_triggerName, 0, 1);
        TQPushButton *editButton = new TQPushButton(i18n("edit"), frame);
        layout->addWidget(editButton, 0, 2);
        connect(editButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotEditTriggerName()));

        layout->addWidget(new TQLabel(i18n("Base pitch:  "), frame), 1, 0);
        m_triggerPitch = new TQLabel(TQString("%1").arg(rec->getBasePitch()), frame);
        layout->addWidget(m_triggerPitch, 1, 1);
        editButton = new TQPushButton(i18n("edit"), frame);
        layout->addWidget(editButton, 1, 2);
        connect(editButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotEditTriggerPitch()));

        layout->addWidget(new TQLabel(i18n("Base velocity:  "), frame), 2, 0);
        m_triggerVelocity = new TQLabel(TQString("%1").arg(rec->getBaseVelocity()), frame);
        layout->addWidget(m_triggerVelocity, 2, 1);
        editButton = new TQPushButton(i18n("edit"), frame);
        layout->addWidget(editButton, 2, 2);
        connect(editButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotEditTriggerVelocity()));

        /*!!! Comment out these two options, which are not yet used
          anywhere else -- intended for use with library ornaments, not
          yet implemented

        layout->addWidget(new TQLabel(i18n("Default timing:  "), frame), 3, 0);

        KComboBox *adjust = new KComboBox(frame);
        layout->addMultiCellWidget(adjust, 3, 3, 1, 2);
        adjust->insertItem(i18n("As stored"));
        adjust->insertItem(i18n("Truncate if longer than note"));
        adjust->insertItem(i18n("End at same time as note")); 
        adjust->insertItem(i18n("Stretch or squash segment to note duration"));

        std::string timing = rec->getDefaultTimeAdjust();
        if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE) {
            adjust->setCurrentItem(0);
        } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH) {
            adjust->setCurrentItem(3);
        } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START) {
            adjust->setCurrentItem(1);
        } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END) {
            adjust->setCurrentItem(2);
        }

        connect(adjust, TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotTriggerTimeAdjustChanged(int)));
            
        TQCheckBox *retune = new TQCheckBox(i18n("Adjust pitch to trigger note by default"), frame);
        retune->setChecked(rec->getDefaultRetune());
        connect(retune, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotTriggerRetuneChanged()));
        layout->addMultiCellWidget(retune, 4, 4, 1, 2);

        */

        m_grid->addWidget(groupBox, 2, 2);

    }

    updateViewCaption();

    for (unsigned int i = 0; i < m_segments.size(); ++i) {
        m_segments[i]->addObserver(this);
    }

    // Connect double clicker
    //
    connect(m_eventList, TQ_SIGNAL(doubleClicked(TQListViewItem*)),
            TQ_SLOT(slotPopupEventEditor(TQListViewItem*)));

    connect(m_eventList,
            TQ_SIGNAL(rightButtonPressed(TQListViewItem*, const TQPoint&, int)),
            TQ_SLOT(slotPopupMenu(TQListViewItem*, const TQPoint&, int)));

    m_eventList->setAllColumnsShowFocus(true);
    m_eventList->setSelectionMode(TQListView::Extended);

    m_eventList->addColumn(i18n("Time  "));
    m_eventList->addColumn(i18n("Duration  "));
    m_eventList->addColumn(i18n("Event Type  "));
    m_eventList->addColumn(i18n("Pitch  "));
    m_eventList->addColumn(i18n("Velocity  "));
    m_eventList->addColumn(i18n("Type (Data1)  "));
    m_eventList->addColumn(i18n("Value (Data2)  "));

    for (int col = 0; col < m_eventList->columns(); ++col)
        m_eventList->setRenameable(col, true);

    readOptions();
    setButtonsToFilter();
    applyLayout();

    makeInitialSelection(doc->getComposition().getPosition());

    slotCompositionStateUpdate();

    setOutOfCtor();
}

EventView::~EventView()
{
    for (unsigned int i = 0; i < m_segments.size(); ++i) {
        RG_DEBUG << "~EventView - removing this observer from " << m_segments[i] << endl;
        m_segments[i]->removeObserver(this);
    }
}

void
EventView::eventRemoved(const Segment *, Event *e)
{
    m_deletedEvents.insert(e);
}

void
EventView::segmentDeleted(const Segment *s)
{
    std::vector<Segment *>::iterator i = std::find(m_segments.begin(), m_segments.end(), s);

    if (i != m_segments.end()) {
        m_segments.erase(i);
    } else {
        RG_DEBUG << "%%% WARNING - EventView::segmentDeleted() called on non-registered segment - should not happen\n";
    }

}

bool
EventView::applyLayout(int /*staffNo*/)
{
    // If no selection has already been set then we copy what's
    // already set and try to replicate this after the rebuild
    // of the view.
    //
    if (m_listSelection.size() == 0) {
        TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();

        if (selection.count()) {
            TQPtrListIterator<TQListViewItem> it(selection);
            TQListViewItem *listItem;

            while ((listItem = it.current()) != 0) {
                m_listSelection.push_back(m_eventList->itemIndex(*it));
                ++it;
            }
        }
    }

    // Ok, recreate list
    //
    m_eventList->clear();

    m_config->setGroup(EventViewConfigGroup);
    int timeMode = m_config->readNumEntry("timemode", 0);

    for (unsigned int i = 0; i < m_segments.size(); i++) {
        SegmentPerformanceHelper helper(*m_segments[i]);

        for (Segment::iterator it = m_segments[i]->begin();
                m_segments[i]->isBeforeEndMarker(it); it++) {
            timeT eventTime =
                helper.getSoundingAbsoluteTime(it);

            TQString velyStr;
            TQString pitchStr;
            TQString data1Str = "";
            TQString data2Str = "";
            TQString durationStr;

            // Event filters
            //

            if ((*it)->isa(Note::EventRestType)) {
                if (!(m_eventFilter & Rest))
                    continue;

            } else if ((*it)->isa(Note::EventType)) {
                if (!(m_eventFilter & Note))
                    continue;

            } else if ((*it)->isa(Indication::EventType)) {
                if (!(m_eventFilter & Indication))
                    continue;

            } else if ((*it)->isa(PitchBend::EventType)) {
                if (!(m_eventFilter & PitchBend))
                    continue;

            } else if ((*it)->isa(SystemExclusive::EventType)) {
                if (!(m_eventFilter & SystemExclusive))
                    continue;

            } else if ((*it)->isa(ProgramChange::EventType)) {
                if (!(m_eventFilter & ProgramChange))
                    continue;

            } else if ((*it)->isa(ChannelPressure::EventType)) {
                if (!(m_eventFilter & ChannelPressure))
                    continue;

            } else if ((*it)->isa(KeyPressure::EventType)) {
                if (!(m_eventFilter & KeyPressure))
                    continue;

            } else if ((*it)->isa(Controller::EventType)) {
                if (!(m_eventFilter & Controller))
                    continue;

            } else if ((*it)->isa(Text::EventType)) {
                if (!(m_eventFilter & Text))
                    continue;

            } else {
                if (!(m_eventFilter & Other))
                    continue;
            }

            // avoid debug stuff going to stderr if no properties found

            if ((*it)->has(BaseProperties::PITCH)) {
                int p = (*it)->get
                        <Int>(BaseProperties::PITCH);
                pitchStr = TQString("%1 %2  ")
                           .arg(p).arg(MidiPitchLabel(p).getTQString());
            } else if ((*it)->isa(Note::EventType)) {
                pitchStr = "<not set>";
            }

            if ((*it)->has(BaseProperties::VELOCITY)) {
                velyStr = TQString("%1  ").
                          arg((*it)->get
                              <Int>(BaseProperties::VELOCITY));
            } else if ((*it)->isa(Note::EventType)) {
                velyStr = "<not set>";
            }

            if ((*it)->has(Controller::NUMBER)) {
                data1Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(Controller::NUMBER));
            } else if ((*it)->has(Text::TextTypePropertyName)) {
                data1Str = TQString("%1  ").
                           arg(strtoqstr((*it)->get
                                         <String>
                                         (Text::TextTypePropertyName)));
            } else if ((*it)->has(Indication::
                                  IndicationTypePropertyName)) {
                data1Str = TQString("%1  ").
                           arg(strtoqstr((*it)->get
                                         <String>
                                         (Indication::
                                          IndicationTypePropertyName)));
            } else if ((*it)->has(::Rosegarden::Key::KeyPropertyName)) {
                data1Str = TQString("%1  ").
                           arg(strtoqstr((*it)->get
                                         <String>
                                         (::Rosegarden::Key::KeyPropertyName)));
            } else if ((*it)->has(Clef::ClefPropertyName)) {
                data1Str = TQString("%1  ").
                           arg(strtoqstr((*it)->get
                                         <String>
                                         (Clef::ClefPropertyName)));
            } else if ((*it)->has(PitchBend::MSB)) {
                data1Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(PitchBend::MSB));
            } else if ((*it)->has(BaseProperties::BEAMED_GROUP_TYPE)) {
                data1Str = TQString("%1  ").
                           arg(strtoqstr((*it)->get
                                         <String>
                                         (BaseProperties::BEAMED_GROUP_TYPE)));
            }

            if ((*it)->has(Controller::VALUE)) {
                data2Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(Controller::VALUE));
            } else if ((*it)->has(Text::TextPropertyName)) {
                data2Str = TQString("%1  ").
                           arg(strtoqstr((*it)->get
                                         <String>
                                         (Text::TextPropertyName)));
                /*!!!
                	    } else if ((*it)->has(Indication::
                				  IndicationTypePropertyName)) {
                		data2Str = TQString("%1  ").
                		    arg((*it)->get<Int>(Indication::
                					IndicationDurationPropertyName));
                */
            } else if ((*it)->has(PitchBend::LSB)) {
                data2Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(PitchBend::LSB));
            } else if ((*it)->has(BaseProperties::BEAMED_GROUP_ID)) {
                data2Str = i18n("(group %1)  ").
                           arg((*it)->get
                               <Int>(BaseProperties::BEAMED_GROUP_ID));
            }

            if ((*it)->has(ProgramChange::PROGRAM)) {
                data1Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(ProgramChange::PROGRAM) + 1);
            }

            if ((*it)->has(ChannelPressure::PRESSURE)) {
                data1Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(ChannelPressure::PRESSURE));
            }

            if ((*it)->isa(KeyPressure::EventType) &&
                    (*it)->has(KeyPressure::PITCH)) {
                data1Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(KeyPressure::PITCH));
            }

            if ((*it)->has(KeyPressure::PRESSURE)) {
                data2Str = TQString("%1  ").
                           arg((*it)->get
                               <Int>(KeyPressure::PRESSURE));
            }


            if ((*it)->getDuration() > 0 ||
                    (*it)->isa(Note::EventType) ||
                    (*it)->isa(Note::EventRestType)) {
                durationStr = makeDurationString(eventTime,
                                                 (*it)->getDuration(),
                                                 timeMode);
            }

            TQString timeStr = makeTimeString(eventTime, timeMode);

            new EventViewItem(m_segments[i],
                              *it,
                              m_eventList,
                              timeStr,
                              durationStr,
                              strtoqstr((*it)->getType()),
                              pitchStr,
                              velyStr,
                              data1Str,
                              data2Str);
        }
    }


    if (m_eventList->childCount() == 0) {
        if (m_segments.size())
            new TQListViewItem(m_eventList,
                              i18n("<no events at this filter level>"));
        else
            new TQListViewItem(m_eventList, i18n("<no events>"));

        m_eventList->setSelectionMode(TQListView::NoSelection);
        stateChanged("have_selection", KXMLGUIClient::StateReverse);
    } else {
        m_eventList->setSelectionMode(TQListView::Extended);

        // If no selection then select the first event
        if (m_listSelection.size() == 0)
            m_listSelection.push_back(0);

        stateChanged("have_selection", KXMLGUIClient::StateNoReverse);
    }

    // Set a selection from a range of indexes
    //
    std::vector<int>::iterator sIt = m_listSelection.begin();
    int index = 0;

    for (; sIt != m_listSelection.end(); ++sIt) {
        index = *sIt;

        while (index > 0 && !m_eventList->itemAtIndex(index))
            index--;

        m_eventList->setSelected(m_eventList->itemAtIndex(index), true);
        m_eventList->setCurrentItem(m_eventList->itemAtIndex(index));

        // ensure visible
        m_eventList->ensureItemVisible(m_eventList->itemAtIndex(index));
    }

    m_listSelection.clear();
    m_deletedEvents.clear();

    return true;
}

void
EventView::makeInitialSelection(timeT time)
{
    m_listSelection.clear();

    EventViewItem *goodItem = 0;
    int goodItemNo = 0;

    int i = 0;

    for (TQListViewItem *child = m_eventList->firstChild();
            child;
            child = child->nextSibling()) {

        EventViewItem * item = dynamic_cast<EventViewItem *>(child);

        if (item) {
            if (item->getEvent()->getAbsoluteTime() > time)
                break;
            goodItem = item;
            goodItemNo = i;
        }

        ++i;
    }
    /*!!!
        for (int i = 0; m_eventList->itemAtIndex(i); ++i) {
     
    	EventViewItem *item = dynamic_cast<EventViewItem *>
    	    (m_eventList->itemAtIndex(i));
     
    	if (item) {
    	    if (item->getEvent()->getAbsoluteTime() > time) break;
    	    goodItem = item;
    	    goodItemNo = i;
    	}
        }
    */
    if (goodItem) {
        m_listSelection.push_back(goodItemNo);
        m_eventList->setSelected(goodItem, true);
        m_eventList->ensureItemVisible(goodItem);
    }
}

TQString
EventView::makeTimeString(timeT time, int timeMode)
{
    switch (timeMode) {

    case 0:  // musical time
        {
            int bar, beat, fraction, remainder;
            getDocument()->getComposition().getMusicalTimeForAbsoluteTime
            (time, bar, beat, fraction, remainder);
            ++bar;
            return TQString("%1%2%3-%4%5-%6%7-%8%9   ")
                   .arg(bar / 100)
                   .arg((bar % 100) / 10)
                   .arg(bar % 10)
                   .arg(beat / 10)
                   .arg(beat % 10)
                   .arg(fraction / 10)
                   .arg(fraction % 10)
                   .arg(remainder / 10)
                   .arg(remainder % 10);
        }

    case 1:  // real time
        {
            RealTime rt =
                getDocument()->getComposition().getElapsedRealTime(time);
            //	return TQString("%1  ").arg(rt.toString().c_str());
            return TQString("%1  ").arg(rt.toText().c_str());
        }

    default:
        return TQString("%1  ").arg(time);
    }
}

TQString
EventView::makeDurationString(timeT time,
                              timeT duration, int timeMode)
{
    switch (timeMode) {

    case 0:  // musical time
        {
            int bar, beat, fraction, remainder;
            getDocument()->getComposition().getMusicalTimeForDuration
            (time, duration, bar, beat, fraction, remainder);
            return TQString("%1%2%3-%4%5-%6%7-%8%9   ")
                   .arg(bar / 100)
                   .arg((bar % 100) / 10)
                   .arg(bar % 10)
                   .arg(beat / 10)
                   .arg(beat % 10)
                   .arg(fraction / 10)
                   .arg(fraction % 10)
                   .arg(remainder / 10)
                   .arg(remainder % 10);
        }

    case 1:  // real time
        {
            RealTime rt =
                getDocument()->getComposition().getRealTimeDifference
                (time, time + duration);
            //	return TQString("%1  ").arg(rt.toString().c_str());
            return TQString("%1  ").arg(rt.toText().c_str());
        }

    default:
        return TQString("%1  ").arg(duration);
    }
}

void
EventView::refreshSegment(Segment * /*segment*/,
                          timeT /*startTime*/,
                          timeT /*endTime*/)
{
    RG_DEBUG << "EventView::refreshSegment" << endl;
    applyLayout(0);
}

void
EventView::updateView()
{
    m_eventList->update();
}

void
EventView::slotEditTriggerName()
{
    bool ok = false;
    TQString newLabel = KLineEditDlg::getText(i18n("Segment label"), i18n("Label:"),
                       strtoqstr(m_segments[0]->getLabel()),
                       &ok, this);

    if (ok) {
        SegmentSelection selection;
        selection.insert(m_segments[0]);
        SegmentLabelCommand *cmd = new SegmentLabelCommand(selection, newLabel);
        addCommandToHistory(cmd);
        m_triggerName->setText(newLabel);
    }
}

void
EventView::slotEditTriggerPitch()
{
    int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);

    TriggerSegmentRec *rec =
        m_segments[0]->getComposition()->getTriggerSegmentRec(id);

    PitchDialog *dlg = new PitchDialog(this, i18n("Base pitch"), rec->getBasePitch());

    if (dlg->exec() == TQDialog::Accepted) {
        addCommandToHistory(new SetTriggerSegmentBasePitchCommand
                            (&getDocument()->getComposition(), id, dlg->getPitch()));
        m_triggerPitch->setText(TQString("%1").arg(dlg->getPitch()));
    }
}

void
EventView::slotEditTriggerVelocity()
{
    int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);

    TriggerSegmentRec *rec =
        m_segments[0]->getComposition()->getTriggerSegmentRec(id);

    TrivialVelocityDialog *dlg = new TrivialVelocityDialog
                                 (this, i18n("Base velocity"), rec->getBaseVelocity());

    if (dlg->exec() == TQDialog::Accepted) {
        addCommandToHistory(new SetTriggerSegmentBaseVelocityCommand
                            (&getDocument()->getComposition(), id, dlg->getVelocity()));
        m_triggerVelocity->setText(TQString("%1").arg(dlg->getVelocity()));
    }
}

void
EventView::slotTriggerTimeAdjustChanged(int option)
{
    std::string adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH;

    switch (option) {

    case 0:
        adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE;
        break;
    case 1:
        adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START;
        break;
    case 2:
        adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END;
        break;
    case 3:
        adjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH;
        break;

    default:
        break;
    }

    int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);

    TriggerSegmentRec *rec =
        m_segments[0]->getComposition()->getTriggerSegmentRec(id);

    addCommandToHistory(new SetTriggerSegmentDefaultTimeAdjustCommand
                        (&getDocument()->getComposition(), id, adjust));
}

void
EventView::slotTriggerRetuneChanged()
{
    int id = m_segments[0]->getComposition()->getTriggerSegmentId(m_segments[0]);

    TriggerSegmentRec *rec =
        m_segments[0]->getComposition()->getTriggerSegmentRec(id);

    addCommandToHistory(new SetTriggerSegmentDefaultRetuneCommand
                        (&getDocument()->getComposition(), id, !rec->getDefaultRetune()));
}

void
EventView::slotEditCut()
{
    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();

    if (selection.count() == 0)
        return ;

    RG_DEBUG << "EventView::slotEditCut - cutting "
    << selection.count() << " items" << endl;

    TQPtrListIterator<TQListViewItem> it(selection);
    TQListViewItem *listItem;
    EventViewItem *item;
    EventSelection *cutSelection = 0;
    int itemIndex = -1;

    while ((listItem = it.current()) != 0) {
        item = dynamic_cast<EventViewItem*>((*it));

        if (itemIndex == -1)
            itemIndex = m_eventList->itemIndex(*it);

        if (item) {
            if (cutSelection == 0)
                cutSelection =
                    new EventSelection(*(item->getSegment()));

            cutSelection->addEvent(item->getEvent());
        }
        ++it;
    }

    if (cutSelection) {
        if (itemIndex >= 0) {
            m_listSelection.clear();
            m_listSelection.push_back(itemIndex);
        }

        addCommandToHistory(new CutCommand(*cutSelection,
                                           getDocument()->getClipboard()));
    }
}

void
EventView::slotEditCopy()
{
    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();

    if (selection.count() == 0)
        return ;

    RG_DEBUG << "EventView::slotEditCopy - copying "
    << selection.count() << " items" << endl;

    TQPtrListIterator<TQListViewItem> it(selection);
    TQListViewItem *listItem;
    EventViewItem *item;
    EventSelection *copySelection = 0;

    // clear the selection for post modification updating
    //
    m_listSelection.clear();

    while ((listItem = it.current()) != 0) {
        item = dynamic_cast<EventViewItem*>((*it));

        m_listSelection.push_back(m_eventList->itemIndex(*it));

        if (item) {
            if (copySelection == 0)
                copySelection =
                    new EventSelection(*(item->getSegment()));

            copySelection->addEvent(item->getEvent());
        }
        ++it;
    }

    if (copySelection) {
        addCommandToHistory(new CopyCommand(*copySelection,
                                            getDocument()->getClipboard()));
    }
}

void
EventView::slotEditPaste()
{
    if (getDocument()->getClipboard()->isEmpty()) {
        slotStatusHelpMsg(i18n("Clipboard is empty"));
        return ;
    }

    KTmpStatusMsg msg(i18n("Inserting clipboard contents..."), this);

    timeT insertionTime = 0;

    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();
    if (selection.count()) {
        EventViewItem *item = dynamic_cast<EventViewItem*>(selection.at(0));

        if (item)
            insertionTime = item->getEvent()->getAbsoluteTime();

        // remember the selection
        //
        m_listSelection.clear();

        TQPtrListIterator<TQListViewItem> it(selection);
        TQListViewItem *listItem;

        while ((listItem = it.current()) != 0) {
            m_listSelection.push_back(m_eventList->itemIndex(*it));
            ++it;
        }
    }


    PasteEventsCommand *command = new PasteEventsCommand
                                  (*m_segments[0], getDocument()->getClipboard(),
                                   insertionTime, PasteEventsCommand::MatrixOverlay);

    if (!command->isPossible()) {
        slotStatusHelpMsg(i18n("Couldn't paste at this point"));
    } else
        addCommandToHistory(command);

    RG_DEBUG << "EventView::slotEditPaste - pasting "
    << selection.count() << " items" << endl;
}

void
EventView::slotEditDelete()
{
    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();
    if (selection.count() == 0)
        return ;

    RG_DEBUG << "EventView::slotEditDelete - deleting "
    << selection.count() << " items" << endl;

    TQPtrListIterator<TQListViewItem> it(selection);
    TQListViewItem *listItem;
    EventViewItem *item;
    EventSelection *deleteSelection = 0;
    int itemIndex = -1;

    while ((listItem = it.current()) != 0) {
        item = dynamic_cast<EventViewItem*>((*it));

        if (itemIndex == -1)
            itemIndex = m_eventList->itemIndex(*it);

        if (item) {
            if (m_deletedEvents.find(item->getEvent()) != m_deletedEvents.end()) {
                ++it;
                continue;
            }

            if (deleteSelection == 0)
                deleteSelection =
                    new EventSelection(*m_segments[0]);

            deleteSelection->addEvent(item->getEvent());
        }
        ++it;
    }

    if (deleteSelection) {

        if (itemIndex >= 0) {
            m_listSelection.clear();
            m_listSelection.push_back(itemIndex);
        }

        addCommandToHistory(new EraseCommand(*deleteSelection));

    }
}

void
EventView::slotEditInsert()
{
    RG_DEBUG << "EventView::slotEditInsert" << endl;

    timeT insertTime = m_segments[0]->getStartTime();
    timeT insertDuration = 960;

    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();

    if (selection.count() > 0) {
        EventViewItem *item =
            dynamic_cast<EventViewItem*>(selection.getFirst());

        if (item) {
            insertTime = item->getEvent()->getAbsoluteTime();
            insertDuration = item->getEvent()->getDuration();
        }
    }

    // Create default event
    //
    Event *event =
        new Event(Note::EventType,
                  insertTime,
                  insertDuration);
    event->set
    <Int>(BaseProperties::PITCH, 70);
    event->set
    <Int>(BaseProperties::VELOCITY, 100);

    SimpleEventEditDialog dialog(this, getDocument(), *event, true);

    if (dialog.exec() == TQDialog::Accepted) {
        EventInsertionCommand *command =
            new EventInsertionCommand(*m_segments[0],
                                      new Event(dialog.getEvent()));
        addCommandToHistory(command);
    }
}

void
EventView::slotEditEvent()
{
    RG_DEBUG << "EventView::slotEditEvent" << endl;

    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();

    if (selection.count() > 0) {
        EventViewItem *item =
            dynamic_cast<EventViewItem*>(selection.getFirst());

        if (item) {
            Event *event = item->getEvent();
            SimpleEventEditDialog dialog(this, getDocument(), *event, false);

            if (dialog.exec() == TQDialog::Accepted && dialog.isModified()) {
                EventEditCommand *command =
                    new EventEditCommand(*(item->getSegment()),
                                         event,
                                         dialog.getEvent());

                addCommandToHistory(command);
            }
        }
    }
}

void
EventView::slotEditEventAdvanced()
{
    RG_DEBUG << "EventView::slotEditEventAdvanced" << endl;

    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();

    if (selection.count() > 0) {
        EventViewItem *item =
            dynamic_cast<EventViewItem*>(selection.getFirst());

        if (item) {
            Event *event = item->getEvent();
            EventEditDialog dialog(this, *event);

            if (dialog.exec() == TQDialog::Accepted && dialog.isModified()) {
                EventEditCommand *command =
                    new EventEditCommand(*(item->getSegment()),
                                         event,
                                         dialog.getEvent());

                addCommandToHistory(command);
            }
        }
    }
}

void
EventView::slotSelectAll()
{
    m_listSelection.clear();
    for (int i = 0; m_eventList->itemAtIndex(i); ++i) {
        m_listSelection.push_back(i);
        m_eventList->setSelected(m_eventList->itemAtIndex(i), true);
    }
}

void
EventView::slotClearSelection()
{
    m_listSelection.clear();
    for (int i = 0; m_eventList->itemAtIndex(i); ++i) {
        m_eventList->setSelected(m_eventList->itemAtIndex(i), false);
    }
}

void
EventView::slotFilterSelection()
{
    m_listSelection.clear();
    TQPtrList<TQListViewItem> selection = m_eventList->selectedItems();
    if (selection.count() == 0)
        return ;

    EventFilterDialog dialog(this);
    if (dialog.exec() == TQDialog::Accepted) {

        TQPtrListIterator<TQListViewItem> it(selection);
        TQListViewItem *listItem;

        while ((listItem = it.current()) != 0) {

            EventViewItem * item = dynamic_cast<EventViewItem*>(*it);
            if (!item) {
                ++it;
                continue;
            }

            if (!dialog.keepEvent(item->getEvent())) {
                m_listSelection.push_back(m_eventList->itemIndex(*it));
                m_eventList->setSelected(item, false);
            }

            ++it;
        }
    }
}

void
EventView::setupActions()
{
    EditViewBase::setupActions("eventlist.rc");

    TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
    TQIconSet icon(TQPixmap(pixmapDir + "/toolbar/event-insert.png"));

    new TDEAction(i18n("&Insert Event"), icon, Key_I, this,
                TQ_SLOT(slotEditInsert()), actionCollection(),
                "insert");

    TQCanvasPixmap pixmap(pixmapDir + "/toolbar/event-delete.png");
    icon = TQIconSet(pixmap);

    new TDEAction(i18n("&Delete Event"), icon, Key_Delete, this,
                TQ_SLOT(slotEditDelete()), actionCollection(),
                "delete");

    pixmap.load(pixmapDir + "/toolbar/event-edit.png");
    icon = TQIconSet(pixmap);

    new TDEAction(i18n("&Edit Event"), icon, Key_E, this,
                TQ_SLOT(slotEditEvent()), actionCollection(),
                "edit_simple");

    pixmap.load(pixmapDir + "/toolbar/event-edit-advanced.png");
    icon = TQIconSet(pixmap);

    new TDEAction(i18n("&Advanced Event Editor"), icon, Key_A, this,
                TQ_SLOT(slotEditEventAdvanced()), actionCollection(),
                "edit_advanced");

    //    icon = TQIconSet(TQCanvasPixmap(pixmapDir + "/toolbar/eventfilter.xpm"));
    new TDEAction(i18n("&Filter Selection"), "filter", Key_F, this,
                TQ_SLOT(slotFilterSelection()), actionCollection(),
                "filter_selection");

    new TDEAction(i18n("Select &All"), Key_A + CTRL, this,
                TQ_SLOT(slotSelectAll()), actionCollection(),
                "select_all");

    new TDEAction(i18n("Clear Selection"), Key_Escape, this,
                TQ_SLOT(slotClearSelection()), actionCollection(),
                "clear_selection");

    m_config->setGroup(EventViewConfigGroup);
    int timeMode = m_config->readNumEntry("timemode", 0);

    TDERadioAction *action;

    pixmap.load(pixmapDir + "/toolbar/time-musical.png");
    icon = TQIconSet(pixmap);

    action = new TDERadioAction(i18n("&Musical Times"), icon, 0, this,
                              TQ_SLOT(slotMusicalTime()),
                              actionCollection(), "time_musical");
    action->setExclusiveGroup("timeMode");
    if (timeMode == 0)
        action->setChecked(true);

    pixmap.load(pixmapDir + "/toolbar/time-real.png");
    icon = TQIconSet(pixmap);

    action = new TDERadioAction(i18n("&Real Times"), icon, 0, this,
                              TQ_SLOT(slotRealTime()),
                              actionCollection(), "time_real");
    action->setExclusiveGroup("timeMode");
    if (timeMode == 1)
        action->setChecked(true);

    pixmap.load(pixmapDir + "/toolbar/time-raw.png");
    icon = TQIconSet(pixmap);

    action = new TDERadioAction(i18n("Ra&w Times"), icon, 0, this,
                              TQ_SLOT(slotRawTime()),
                              actionCollection(), "time_raw");
    action->setExclusiveGroup("timeMode");
    if (timeMode == 2)
        action->setChecked(true);

    if (m_isTriggerSegment) {
        TDEAction *action = actionCollection()->action("open_in_matrix");
        if (action)
            delete action;
        action = actionCollection()->action("open_in_notation");
        if (action)
            delete action;
    }

    createGUI(getRCFileName());
}

void
EventView::initStatusBar()
{
    KStatusBar* sb = statusBar();

    /*
    m_hoveredOverNoteName      = new TQLabel(sb);
    m_hoveredOverAbsoluteTime  = new TQLabel(sb);

    m_hoveredOverNoteName->setMinimumWidth(32);
    m_hoveredOverAbsoluteTime->setMinimumWidth(160);

    sb->addWidget(m_hoveredOverAbsoluteTime);
    sb->addWidget(m_hoveredOverNoteName);
    */

    sb->insertItem(KTmpStatusMsg::getDefaultMsg(),
                   KTmpStatusMsg::getDefaultId(), 1);
    sb->setItemAlignment(KTmpStatusMsg::getDefaultId(),
                         AlignLeft | AlignVCenter);

    //m_selectionCounter = new TQLabel(sb);
    //sb->addWidget(m_selectionCounter);
}

TQSize
EventView::getViewSize()
{
    return m_eventList->size();
}

void
EventView::setViewSize(TQSize s)
{
    m_eventList->setFixedSize(s);
}

void
EventView::readOptions()
{
    m_config->setGroup(EventViewConfigGroup);
    EditViewBase::readOptions();
    m_eventFilter = m_config->readNumEntry("eventfilter", m_eventFilter);
    m_eventList->restoreLayout(m_config, EventViewLayoutConfigGroupName);
}

void
EventView::slotSaveOptions()
{
    m_config->setGroup(EventViewConfigGroup);
    m_config->writeEntry("eventfilter", m_eventFilter);
    m_eventList->saveLayout(m_config, EventViewLayoutConfigGroupName);
}

Segment *
EventView::getCurrentSegment()
{
    if (m_segments.empty())
        return 0;
    else
        return *m_segments.begin();
}

void
EventView::slotModifyFilter(int button)
{
    TQCheckBox *checkBox = dynamic_cast<TQCheckBox*>(m_filterGroup->find(button));

    if (checkBox == 0)
        return ;

    if (checkBox->isChecked()) {
        switch (button) {
        case 0:
            m_eventFilter |= EventView::Note;
            break;

        case 1:
            m_eventFilter |= EventView::ProgramChange;
            break;

        case 2:
            m_eventFilter |= EventView::Controller;
            break;

        case 3:
            m_eventFilter |= EventView::PitchBend;
            break;

        case 4:
            m_eventFilter |= EventView::SystemExclusive;
            break;

        case 5:
            m_eventFilter |= EventView::KeyPressure;
            break;

        case 6:
            m_eventFilter |= EventView::ChannelPressure;
            break;

        case 7:
            m_eventFilter |= EventView::Rest;
            break;

        case 8:
            m_eventFilter |= EventView::Indication;
            break;

        case 9:
            m_eventFilter |= EventView::Text;
            break;

        case 10:
            m_eventFilter |= EventView::Other;
            break;

        default:
            break;
        }

    } else {
        switch (button) {
        case 0:
            m_eventFilter ^= EventView::Note;
            break;

        case 1:
            m_eventFilter ^= EventView::ProgramChange;
            break;

        case 2:
            m_eventFilter ^= EventView::Controller;
            break;

        case 3:
            m_eventFilter ^= EventView::PitchBend;
            break;

        case 4:
            m_eventFilter ^= EventView::SystemExclusive;
            break;

        case 5:
            m_eventFilter ^= EventView::KeyPressure;
            break;

        case 6:
            m_eventFilter ^= EventView::ChannelPressure;
            break;

        case 7:
            m_eventFilter ^= EventView::Rest;
            break;

        case 8:
            m_eventFilter ^= EventView::Indication;
            break;

        case 9:
            m_eventFilter ^= EventView::Text;
            break;

        case 10:
            m_eventFilter ^= EventView::Other;
            break;

        default:
            break;
        }
    }

    m_lastSetEventFilter = m_eventFilter;

    applyLayout(0);
}

void
EventView::setButtonsToFilter()
{
    if (m_eventFilter & Note)
        m_noteCheckBox->setChecked(true);
    else
        m_noteCheckBox->setChecked(false);

    if (m_eventFilter & ProgramChange)
        m_programCheckBox->setChecked(true);
    else
        m_programCheckBox->setChecked(false);

    if (m_eventFilter & Controller)
        m_controllerCheckBox->setChecked(true);
    else
        m_controllerCheckBox->setChecked(false);

    if (m_eventFilter & SystemExclusive)
        m_sysExCheckBox->setChecked(true);
    else
        m_sysExCheckBox->setChecked(false);

    if (m_eventFilter & Text)
        m_textCheckBox->setChecked(true);
    else
        m_textCheckBox->setChecked(false);

    if (m_eventFilter & Rest)
        m_restCheckBox->setChecked(true);
    else
        m_restCheckBox->setChecked(false);

    if (m_eventFilter & PitchBend)
        m_pitchBendCheckBox->setChecked(true);
    else
        m_pitchBendCheckBox->setChecked(false);

    if (m_eventFilter & ChannelPressure)
        m_channelPressureCheckBox->setChecked(true);
    else
        m_channelPressureCheckBox->setChecked(false);

    if (m_eventFilter & KeyPressure)
        m_keyPressureCheckBox->setChecked(true);
    else
        m_keyPressureCheckBox->setChecked(false);

    if (m_eventFilter & Indication) {
        m_indicationCheckBox->setChecked(true);
    } else {
        m_indicationCheckBox->setChecked(false);
    }

    if (m_eventFilter & Other) {
        m_otherCheckBox->setChecked(true);
    } else {
        m_otherCheckBox->setChecked(false);
    }
}

void
EventView::slotMusicalTime()
{
    m_config->setGroup(EventViewConfigGroup);
    m_config->writeEntry("timemode", 0);
    applyLayout();
}

void
EventView::slotRealTime()
{
    m_config->setGroup(EventViewConfigGroup);
    m_config->writeEntry("timemode", 1);
    applyLayout();
}

void
EventView::slotRawTime()
{
    m_config->setGroup(EventViewConfigGroup);
    m_config->writeEntry("timemode", 2);
    applyLayout();
}

void
EventView::slotPopupEventEditor(TQListViewItem *item)
{
    EventViewItem *eItem = dynamic_cast<EventViewItem*>(item);

    //!!! trigger events

    if (eItem) {
        Event *event = eItem->getEvent();
        SimpleEventEditDialog *dialog =
            new SimpleEventEditDialog(this, getDocument(), *event, false);

        if (dialog->exec() == TQDialog::Accepted && dialog->isModified()) {
            EventEditCommand *command =
                new EventEditCommand(*(eItem->getSegment()),
                                     event,
                                     dialog->getEvent());

            addCommandToHistory(command);
        }

    }
}

void
EventView::slotPopupMenu(TQListViewItem *item, const TQPoint &pos, int)
{
    if (!item)
        return ;

    EventViewItem *eItem = dynamic_cast<EventViewItem*>(item);
    if (!eItem || !eItem->getEvent())
        return ;

    if (!m_menu)
        createMenu();

    if (m_menu)
        //m_menu->exec(TQCursor::pos());
        m_menu->exec(pos);
    else
        RG_DEBUG << "EventView::showMenu() : no menu to show\n";
}

void
EventView::createMenu()
{
    m_menu = new TQPopupMenu(this);
    m_menu->insertItem(i18n("Open in Event Editor"), 0);
    m_menu->insertItem(i18n("Open in Expert Event Editor"), 1);

    connect(m_menu, TQ_SIGNAL(activated(int)),
            TQ_SLOT(slotMenuActivated(int)));
}

void
EventView::slotMenuActivated(int value)
{
    RG_DEBUG << "EventView::slotMenuActivated - value = " << value << endl;

    if (value == 0) {
        EventViewItem *eItem = dynamic_cast<EventViewItem*>
                               (m_eventList->currentItem());

        if (eItem) {
            Event *event = eItem->getEvent();
            SimpleEventEditDialog *dialog =
                new SimpleEventEditDialog(this, getDocument(), *event, false);

            if (dialog->exec() == TQDialog::Accepted && dialog->isModified()) {
                EventEditCommand *command =
                    new EventEditCommand(*(eItem->getSegment()),
                                         event,
                                         dialog->getEvent());

                addCommandToHistory(command);
            }

        }
    } else if (value == 1) {
        EventViewItem *eItem = dynamic_cast<EventViewItem*>
                               (m_eventList->currentItem());

        if (eItem) {
            Event *event = eItem->getEvent();
            EventEditDialog *dialog = new EventEditDialog(this, *event);

            if (dialog->exec() == TQDialog::Accepted && dialog->isModified()) {
                EventEditCommand *command =
                    new EventEditCommand(*(eItem->getSegment()),
                                         event,
                                         dialog->getEvent());

                addCommandToHistory(command);
            }

        }
    }

    return ;
}

void
EventView::updateViewCaption()
{
    if (m_isTriggerSegment) {

        setCaption(i18n("%1 - Triggered Segment: %2")
                   .arg(getDocument()->getTitle())
                   .arg(strtoqstr(m_segments[0]->getLabel())));


    } else if (m_segments.size() == 1) {

        TrackId trackId = m_segments[0]->getTrack();
        Track *track =
            m_segments[0]->getComposition()->getTrackById(trackId);

        int trackPosition = -1;
        if (track)
            trackPosition = track->getPosition();

        setCaption(i18n("%1 - Segment Track #%2 - Event List")
                   .arg(getDocument()->getTitle())
                   .arg(trackPosition + 1));

    } else {

        setCaption(i18n("%1 - %2 Segments - Event List")
                   .arg(getDocument()->getTitle())
                   .arg(m_segments.size()));
    }

}

}
#include "EventView.moc"
