mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 00:28:42 +00:00
.. Makes it easier to identify code that has been snaffled in from other repositories and check licensing .. The httpserver is now no longer optional, since it is delivered as contributed source.
767 lines
26 KiB
C++
767 lines
26 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) Qxt Foundation. Some rights reserved.
|
|
**
|
|
** This file is part of the QxtGui module of the Qxt library.
|
|
**
|
|
** This library is free software; you can redistribute it and/or modify it
|
|
** under the terms of the Common Public License, version 1.0, as published
|
|
** by IBM, and/or under the terms of the GNU Lesser General Public License,
|
|
** version 2.1, as published by the Free Software Foundation.
|
|
**
|
|
** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
|
|
** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
|
|
** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
|
|
** FITNESS FOR A PARTICULAR PURPOSE.
|
|
**
|
|
** You should have received a copy of the CPL and the LGPL along with this
|
|
** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
|
|
** included with the source distribution for more information.
|
|
** If you did not receive a copy of the licenses, contact the Qxt Foundation.
|
|
**
|
|
** <http://libqxt.org> <foundation@libqxt.org>
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qxtscheduleview_p.h"
|
|
#include "qxtscheduleview.h"
|
|
#include <QPainter>
|
|
#include <QScrollBar>
|
|
#include <QBrush>
|
|
#include <QMouseEvent>
|
|
#include <QDebug>
|
|
#include <QApplication>
|
|
#include <QTimer>
|
|
#include <QStringList>
|
|
#include <QWidget>
|
|
#include <QList>
|
|
#include <QListIterator>
|
|
#include <QMutableLinkedListIterator>
|
|
|
|
#include "qxtscheduleheaderwidget.h"
|
|
|
|
/*-------------------------------------start private functions-------------------------------------------------------*/
|
|
|
|
int QxtScheduleViewPrivate::offsetToVisualColumn(const int iOffset) const
|
|
{
|
|
if (iOffset >= 0)
|
|
return iOffset / qxt_p().rows();
|
|
return -1;
|
|
}
|
|
|
|
int QxtScheduleViewPrivate::visualIndexToOffset(const int iRow, const int iCol) const
|
|
{
|
|
return (iCol* qxt_p().rows()) + iRow;
|
|
}
|
|
|
|
int QxtScheduleViewPrivate::offsetToVisualRow(const int iOffset) const
|
|
{
|
|
if (iOffset >= 0 && qxt_p().model())
|
|
return iOffset % qxt_p().rows();
|
|
return -1;
|
|
}
|
|
|
|
QVector< QRect > QxtScheduleViewPrivate::calculateRangeGeometries(const int iStartOffset, const int iEndOffset) const
|
|
{
|
|
QVector<QRect> rects;
|
|
//qDebug()<<"calc geometries..."<<iStartOffset<<iEndOffset;
|
|
if (iStartOffset < 0 || iEndOffset < 0)
|
|
return rects;
|
|
|
|
if (iEndOffset < iStartOffset)
|
|
return rects;
|
|
|
|
int iCurrentStartOffset = iStartOffset;
|
|
int iCurrentEndOffset;
|
|
|
|
//qDebug()<<"calc geometries... got to the do!";
|
|
do
|
|
{
|
|
if (offsetToVisualColumn(iCurrentStartOffset) == offsetToVisualColumn(iEndOffset))
|
|
iCurrentEndOffset = iEndOffset;
|
|
else
|
|
iCurrentEndOffset = visualIndexToOffset(m_vHeader->count() - 1, offsetToVisualColumn(iCurrentStartOffset));
|
|
|
|
qint32 iLeft = m_hHeader->sectionPosition(offsetToVisualColumn(iCurrentStartOffset));
|
|
qint32 iTop = m_vHeader->sectionPosition(offsetToVisualRow(iCurrentStartOffset));
|
|
qint32 iBottom = m_vHeader->sectionPosition(offsetToVisualRow(iCurrentEndOffset)) + m_vHeader->sectionSize(offsetToVisualRow(iCurrentEndOffset));
|
|
qint32 iRight = m_hHeader->sectionPosition(offsetToVisualColumn(iCurrentEndOffset)) + m_hHeader->sectionSize(offsetToVisualColumn(iCurrentEndOffset));
|
|
rects.append(QRect(iLeft + 1, iTop + 1, iRight - iLeft - 1, iBottom - iTop - 1));
|
|
|
|
iCurrentStartOffset = visualIndexToOffset(0, offsetToVisualColumn(iCurrentEndOffset) + 1);
|
|
|
|
}
|
|
while (iCurrentEndOffset < iEndOffset);
|
|
|
|
//qDebug()<<"calc geometries... returing:"<<rects.size();
|
|
return rects;
|
|
|
|
}
|
|
|
|
int QxtScheduleViewPrivate::pointToOffset(const QPoint & point)
|
|
{
|
|
int iRow = m_vHeader->visualIndexAt(point.y());
|
|
int iCol = m_hHeader->visualIndexAt(point.x());
|
|
return visualIndexToOffset(iRow, iCol);
|
|
}
|
|
|
|
|
|
|
|
QxtScheduleInternalItem * QxtScheduleViewPrivate::internalItemAt(const QPoint & pt)
|
|
{
|
|
QListIterator<QxtScheduleInternalItem *>iterator(m_Items);
|
|
QxtScheduleInternalItem *currentItem;
|
|
iterator.toBack();
|
|
while (iterator.hasPrevious())
|
|
{
|
|
currentItem = iterator.previous();
|
|
if (currentItem->contains(pt))
|
|
return currentItem;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void QxtScheduleViewPrivate::reloadItemsFromModel()
|
|
{
|
|
qDeleteAll(m_Items.begin(), m_Items.end());
|
|
m_Items.clear();
|
|
m_selectedItem = NULL;
|
|
|
|
int iNumItems = qxt_p().model()->rowCount();
|
|
//delete all old stuff here
|
|
QxtScheduleInternalItem *currentItem;
|
|
for (int iLoop = 0; iLoop < iNumItems; iLoop++)
|
|
{
|
|
currentItem = new QxtScheduleInternalItem(&qxt_p(), qxt_p().model()->index(iLoop, 0));
|
|
m_Items.append(currentItem);
|
|
connect(currentItem, SIGNAL(geometryChanged(QxtScheduleInternalItem*, QVector<QRect>)), this, SLOT(itemGeometryChanged(QxtScheduleInternalItem * , QVector< QRect >)));
|
|
}
|
|
|
|
handleItemConcurrency(0, (qxt_p().rows()*qxt_p().cols()) - 1);
|
|
}
|
|
|
|
void QxtScheduleViewPrivate::init()
|
|
{
|
|
if (qxt_p().model())
|
|
{
|
|
qxt_p().viewport()->setMouseTracking(true);
|
|
|
|
if (!m_vHeader)
|
|
{
|
|
m_vHeader = new QxtScheduleHeaderWidget(Qt::Vertical, &qxt_p());
|
|
connect(m_vHeader, SIGNAL(geometriesChanged()), &qxt_p(), SLOT(updateGeometries()));
|
|
}
|
|
m_vHeader->show();
|
|
|
|
if (!m_hHeader)
|
|
{
|
|
m_hHeader = new QxtScheduleHeaderWidget(Qt::Horizontal, &qxt_p());
|
|
connect(m_hHeader, SIGNAL(geometriesChanged()), &qxt_p(), SLOT(updateGeometries()));
|
|
}
|
|
m_hHeader->show();
|
|
|
|
/*here we also initialize the items*/
|
|
m_vHeader->setDefaultSectionSize(20);
|
|
m_vHeader->setResizeMode(QHeaderView::Fixed);
|
|
reloadItemsFromModel();
|
|
}
|
|
qxt_p().updateGeometries();
|
|
}
|
|
|
|
/*!
|
|
* @desc collects groups of concurrent items in the offset range
|
|
*/
|
|
QList< QLinkedList<QxtScheduleInternalItem *> > QxtScheduleViewPrivate::findConcurrentItems(const int from, const int to) const
|
|
{
|
|
QList< QLinkedList<QxtScheduleInternalItem *> > allConcurrentItems;
|
|
|
|
QList<QxtScheduleInternalItem *> allItemsSorted = m_Items;
|
|
|
|
if(m_Items.size() == 0)
|
|
return allConcurrentItems;
|
|
|
|
qSort(allItemsSorted.begin(), allItemsSorted.end(), qxtScheduleItemLessThan);
|
|
|
|
int startItem = 0;
|
|
int endItem = allItemsSorted.size() - 1;
|
|
|
|
//find the startitem that interferes with our range
|
|
for (int i = 0; i < allItemsSorted.size(); i++)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
if (!(allItemsSorted.at(i - 1)->visualEndTableOffset() >= allItemsSorted.at(i)->visualStartTableOffset()
|
|
&& allItemsSorted.at(i - 1)->visualStartTableOffset() <= allItemsSorted.at(i)->visualEndTableOffset()))
|
|
startItem = i;
|
|
}
|
|
|
|
if (allItemsSorted.at(i)->visualEndTableOffset() >= from && allItemsSorted.at(i)->visualStartTableOffset() <= to)
|
|
break;
|
|
}
|
|
|
|
//find the last item that interferes with our range
|
|
for (int i = allItemsSorted.size() - 1; i >= 0 ; i--)
|
|
{
|
|
if (i < allItemsSorted.size() - 1)
|
|
{
|
|
if (!(allItemsSorted.at(i + 1)->visualEndTableOffset() >= allItemsSorted.at(i)->visualStartTableOffset()
|
|
&& allItemsSorted.at(i + 1)->visualStartTableOffset() <= allItemsSorted.at(i)->visualEndTableOffset()))
|
|
endItem = i;
|
|
}
|
|
|
|
if (allItemsSorted.at(i)->visualEndTableOffset() >= from && allItemsSorted.at(i)->visualStartTableOffset() <= to)
|
|
break;
|
|
}
|
|
|
|
int startOffset = allItemsSorted.at(startItem)->visualStartTableOffset();
|
|
int endOffset = allItemsSorted.at(endItem)->visualEndTableOffset();
|
|
|
|
/*now we have to populate a list with all items that interfere with our range */
|
|
QLinkedList<QxtScheduleInternalItem *> concurrentItems;
|
|
for (int iAllItemLoop = startItem; iAllItemLoop <= endItem; iAllItemLoop++)
|
|
{
|
|
int tempStartOffset = allItemsSorted.at(iAllItemLoop)->visualStartTableOffset();
|
|
int tempEndOffset = allItemsSorted.at(iAllItemLoop)->visualEndTableOffset();
|
|
|
|
if (tempEndOffset >= startOffset && tempStartOffset <= endOffset)
|
|
{
|
|
|
|
if (concurrentItems.size() >= 1)
|
|
{
|
|
bool bAppend = false;
|
|
/*check all items in the list if the current items interfers although the items are ordered by startIndex
|
|
*we can loose some of them if the endTime of the last Item is before the endTime of the pre last item
|
|
*/
|
|
|
|
for (QLinkedList<QxtScheduleInternalItem *>::iterator it = concurrentItems.begin(); it != concurrentItems.end(); ++it)
|
|
{
|
|
int lastStartOffset = (*it)->visualStartTableOffset();
|
|
int lastEndOffset = (*it)->visualEndTableOffset();
|
|
|
|
if (tempEndOffset >= lastStartOffset && tempStartOffset <= lastEndOffset)
|
|
{
|
|
bAppend = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bAppend)
|
|
{
|
|
concurrentItems.append(allItemsSorted.at(iAllItemLoop));
|
|
}
|
|
else
|
|
{
|
|
allConcurrentItems.append(concurrentItems);
|
|
concurrentItems.clear();
|
|
concurrentItems.append(allItemsSorted.at(iAllItemLoop));
|
|
}
|
|
}
|
|
else
|
|
concurrentItems.append(allItemsSorted.at(iAllItemLoop));
|
|
|
|
if (tempStartOffset < startOffset)
|
|
startOffset = tempStartOffset;
|
|
|
|
if (tempEndOffset > endOffset)
|
|
endOffset = tempEndOffset;
|
|
}
|
|
}
|
|
if (concurrentItems.size() > 0)
|
|
allConcurrentItems.append(concurrentItems);
|
|
|
|
return allConcurrentItems;
|
|
}
|
|
|
|
void QxtScheduleViewPrivate::handleItemConcurrency(const int from, const int to)
|
|
{
|
|
/*collect all items that interfere only in that range*/
|
|
if (from < 0 || to < 0 || m_Items.size() == 0){
|
|
//do a update or we may have artifacts
|
|
qxt_p().viewport()->update();
|
|
return;
|
|
}
|
|
|
|
//qDebug() << "handleItemConcurrency";
|
|
|
|
if (handlesConcurrency)
|
|
return;
|
|
|
|
handlesConcurrency = true;
|
|
|
|
QList< QLinkedList<QxtScheduleInternalItem *> > allConcurrentItems = findConcurrentItems(from, to);
|
|
|
|
/*thx to ahigerd for suggesting that algorithm*/
|
|
//[16:24] <ahigerd> Start with the first event. Put it in a list.
|
|
//[16:25] <ahigerd> Iterate until you find an event that doesn't overlap with the first event. Put it in the list. Repeat until you've reached the last event.
|
|
//[16:25] <ahigerd> This fills the left column optimally.
|
|
//[16:25] <ahigerd> Repeat the algorithm for the second column, etc., until there aren't any events left that don't have a column.
|
|
//[16:27] <ahigerd> This algorithm is O(n*m), where n is the number of events and m is the maximum number of overlapping events.
|
|
|
|
QList< QList< QxtScheduleInternalItem *> >virtualTable;
|
|
|
|
for (int iListLoop = 0; iListLoop < allConcurrentItems.size(); iListLoop++)
|
|
{
|
|
QLinkedList<QxtScheduleInternalItem *> & currentItems = allConcurrentItems[iListLoop];
|
|
QList< QxtScheduleInternalItem * > currentColumn;
|
|
|
|
//qDebug() << "handle overlapping for " << currentItems.size() << " Items";
|
|
|
|
virtualTable.clear();
|
|
|
|
//we iterate over the currect collection and remove every item that can be placed in the current column
|
|
//when the collection is empty we are done
|
|
while (currentItems.size())
|
|
{
|
|
QMutableLinkedListIterator< QxtScheduleInternalItem * > iter(currentItems);
|
|
|
|
while (iter.hasNext())
|
|
{
|
|
iter.next();
|
|
//initialize the current column
|
|
if (currentColumn.isEmpty() || currentColumn[currentColumn.size()-1]->visualEndTableOffset() < iter.value()->visualStartTableOffset())
|
|
{
|
|
currentColumn.append(iter.value());
|
|
iter.remove();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!currentColumn.isEmpty())
|
|
{
|
|
virtualTable.append(currentColumn);
|
|
currentColumn.clear();
|
|
}
|
|
}
|
|
|
|
//qDebug() << "Found columns" << virtualTable.size();
|
|
|
|
//this code part resizes the item geometries
|
|
for (int col = 0; col < virtualTable.size(); col++)
|
|
{
|
|
for (int item = 0; item < virtualTable.at(col).size() ; item++)
|
|
{
|
|
int startVisualCol = offsetToVisualColumn(virtualTable[col][item]->visualStartTableOffset());
|
|
QVector<QRect> geo = virtualTable[col][item]->geometry();
|
|
|
|
for (int rect = 0; rect < geo.size(); rect++)
|
|
{
|
|
int sectionStart = m_hHeader->sectionPosition(startVisualCol);
|
|
int fullWidth = m_hHeader->sectionSize(startVisualCol);
|
|
int oneItemWidth = fullWidth / virtualTable.size();
|
|
int itemWidth = oneItemWidth;
|
|
int itemXStart = (col * oneItemWidth) + sectionStart;
|
|
int overlap = oneItemWidth / 10;
|
|
int adjustX1 = 0;
|
|
int adjustX2 = 0;
|
|
|
|
//this is very expensive.I try to check if my item can span over more than one col
|
|
int possibleCols = 1;
|
|
bool foundCollision;
|
|
|
|
for (int tmpCol = col + 1; tmpCol < virtualTable.size(); tmpCol++)
|
|
{
|
|
foundCollision = false;
|
|
for (int tmpItem = 0; tmpItem < virtualTable.at(tmpCol).size() ; tmpItem++)
|
|
{
|
|
if ((virtualTable[tmpCol][tmpItem]->visualEndTableOffset() >= virtualTable[col][item]->visualStartTableOffset()
|
|
&& virtualTable[tmpCol][tmpItem]->visualStartTableOffset() <= virtualTable[col][item]->visualEndTableOffset()))
|
|
{
|
|
foundCollision = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundCollision)
|
|
possibleCols++;
|
|
else
|
|
break;
|
|
}
|
|
//now lets adjust the size to get a nice overlapping of items
|
|
if (virtualTable.size() > 1)
|
|
{
|
|
if (col == 0)
|
|
adjustX2 = overlap;
|
|
else if (col == virtualTable.size() - 1)
|
|
{
|
|
adjustX1 = -overlap;
|
|
adjustX2 = overlap;
|
|
}
|
|
else
|
|
{
|
|
if (col + possibleCols == virtualTable.size())
|
|
adjustX2 = overlap;
|
|
else
|
|
adjustX2 = overlap * 2;
|
|
|
|
adjustX1 = -overlap;
|
|
}
|
|
}
|
|
|
|
// possibleCols = 1;
|
|
itemWidth = oneItemWidth * possibleCols;
|
|
|
|
//qDebug() << "orginial rect" << geo[rect];
|
|
geo[rect].setLeft(itemXStart + adjustX1);
|
|
geo[rect].setWidth(itemWidth + adjustX2);
|
|
//qDebug() << "new rect" << geo[rect];
|
|
|
|
|
|
startVisualCol++;
|
|
}
|
|
virtualTable[col][item]->setGeometry(geo);
|
|
}
|
|
}
|
|
}
|
|
handlesConcurrency = false;
|
|
qxt_p().viewport()->update();
|
|
}
|
|
|
|
QxtScheduleViewPrivate::QxtScheduleViewPrivate()
|
|
{
|
|
m_Cols = 0;
|
|
m_vHeader = 0;
|
|
m_hHeader = 0;
|
|
m_Model = 0;
|
|
m_selectedItem = 0;
|
|
handlesConcurrency = false;
|
|
delegate = 0;
|
|
m_zoomStepWidth = 0;
|
|
|
|
connect(&scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimerTimeout()));
|
|
}
|
|
|
|
void QxtScheduleViewPrivate::itemGeometryChanged(QxtScheduleInternalItem * item, QVector< QRect > oldGeometry)
|
|
{
|
|
QRegion oldRegion;
|
|
|
|
if (item->geometry() == oldGeometry)
|
|
return;
|
|
|
|
QVectorIterator<QRect> iter(oldGeometry);
|
|
QRect currRect;
|
|
while (iter.hasNext())
|
|
{
|
|
currRect = iter.next();
|
|
currRect.adjust(-1, -1, 2, 2);
|
|
oldRegion += currRect;
|
|
}
|
|
//viewport()->update(oldRegion);
|
|
|
|
|
|
QRegion newRegion;
|
|
QVectorIterator<QRect> newIter(item->geometry());
|
|
while (newIter.hasNext())
|
|
{
|
|
currRect = newIter.next();
|
|
currRect.adjust(-1, -1, 2, 2);
|
|
newRegion += currRect;
|
|
}
|
|
//viewport()->update(newRegion);
|
|
qxt_p().viewport()->update();
|
|
}
|
|
|
|
int QxtScheduleViewPrivate::unixTimeToOffset(const uint constUnixTime, bool indexEndTime) const
|
|
{
|
|
uint unixTime = constUnixTime;
|
|
if (unixTime >= m_startUnixTime && unixTime <= m_endUnixTime)
|
|
{
|
|
if (indexEndTime)
|
|
{
|
|
unixTime -= m_currentZoomDepth;
|
|
}
|
|
qint32 rows = qxt_p().rows();
|
|
qint32 iOffset = unixTime - m_startUnixTime;
|
|
|
|
//round to the closest boundaries
|
|
iOffset = qRound((qreal)iOffset / (qreal)m_currentZoomDepth);
|
|
|
|
qint32 iCol = iOffset / rows;
|
|
qint32 iRow = iOffset % rows;
|
|
return visualIndexToOffset(iRow, iCol);
|
|
}
|
|
//virtual void handleItemOverlapping(QxtScheduleInternalItem *item);
|
|
return -1;
|
|
}
|
|
|
|
void QxtScheduleViewPrivate::scrollTimerTimeout()
|
|
{
|
|
QPoint globalPos = QCursor::pos();
|
|
QPoint viewportPos = qxt_p().viewport()->mapFromGlobal(globalPos);
|
|
|
|
int iScrollVertical = this->m_vHeader->defaultSectionSize();
|
|
int iScrollHorizontal = this->m_hHeader->defaultSectionSize();
|
|
|
|
if (viewportPos.y() <= iScrollVertical)
|
|
{
|
|
int iCurrPos = qxt_p().verticalScrollBar()->value();
|
|
if (iCurrPos > qxt_p().verticalScrollBar()->minimum() + iScrollVertical)
|
|
{
|
|
qxt_p().verticalScrollBar()->setValue(iCurrPos - iScrollVertical);
|
|
}
|
|
else
|
|
qxt_p().verticalScrollBar()->setValue(qxt_p().verticalScrollBar()->minimum());
|
|
|
|
}
|
|
else if (viewportPos.y() >= qxt_p().viewport()->height() - iScrollVertical)
|
|
{
|
|
int iCurrPos = qxt_p().verticalScrollBar()->value();
|
|
if (iCurrPos < qxt_p().verticalScrollBar()->maximum() - iScrollVertical)
|
|
{
|
|
qxt_p().verticalScrollBar()->setValue(iCurrPos + iScrollVertical);
|
|
}
|
|
else
|
|
qxt_p().verticalScrollBar()->setValue(qxt_p().verticalScrollBar()->maximum());
|
|
}
|
|
|
|
if (viewportPos.x() <= iScrollHorizontal / 2)
|
|
{
|
|
int iCurrPos = qxt_p().horizontalScrollBar()->value();
|
|
if (iCurrPos > qxt_p().horizontalScrollBar()->minimum() + iScrollHorizontal)
|
|
{
|
|
qxt_p().horizontalScrollBar()->setValue(iCurrPos - iScrollHorizontal);
|
|
}
|
|
else
|
|
qxt_p().horizontalScrollBar()->setValue(qxt_p().horizontalScrollBar()->minimum());
|
|
|
|
}
|
|
else if (viewportPos.x() >= qxt_p().viewport()->width() - (iScrollHorizontal / 2))
|
|
{
|
|
int iCurrPos = qxt_p().horizontalScrollBar()->value();
|
|
if (iCurrPos < qxt_p().horizontalScrollBar()->maximum() - iScrollHorizontal)
|
|
{
|
|
qxt_p().horizontalScrollBar()->setValue(iCurrPos + iScrollHorizontal);
|
|
}
|
|
else
|
|
qxt_p().horizontalScrollBar()->setValue(qxt_p().horizontalScrollBar()->maximum());
|
|
}
|
|
|
|
}
|
|
|
|
int QxtScheduleViewPrivate::offsetToUnixTime(const int offset, bool indexEndTime) const
|
|
{
|
|
qint32 rows = qxt_p().rows();
|
|
uint unixTime = (offsetToVisualRow(offset) + (offsetToVisualColumn(offset) * rows)) * m_currentZoomDepth;
|
|
unixTime += m_startUnixTime;
|
|
|
|
if (indexEndTime)
|
|
{
|
|
unixTime += m_currentZoomDepth;
|
|
}
|
|
|
|
if (unixTime >= m_startUnixTime && unixTime <= m_endUnixTime + 1)
|
|
return unixTime;
|
|
return -1;
|
|
}
|
|
|
|
|
|
QxtScheduleInternalItem::QxtScheduleInternalItem(QxtScheduleView *parent, QModelIndex index, QVector<QRect> geometries)
|
|
: QObject(parent), m_iModelRow(index.row()), m_geometries(geometries)
|
|
{
|
|
m_moving = false;
|
|
if (parent)
|
|
{
|
|
if (index.isValid())
|
|
{
|
|
if (m_geometries.empty())
|
|
{
|
|
//qDebug()<<"add item: geometries empty so adding";
|
|
int startOffset = this->startTableOffset();
|
|
int endOffset = startOffset + this->rows() - 1;
|
|
m_geometries = parent->qxt_d().calculateRangeGeometries(startOffset, endOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QxtScheduleView * QxtScheduleInternalItem::parentView() const
|
|
{
|
|
return qobject_cast<QxtScheduleView *>(parent());
|
|
}
|
|
|
|
/*!
|
|
* @desc returns the currently VISUAL offset (changes when a item moves)
|
|
*/
|
|
int QxtScheduleInternalItem::visualStartTableOffset() const
|
|
{
|
|
if (m_geometries.size() == 0 || !parentView())
|
|
return -1;
|
|
|
|
//are we in a move?
|
|
if (!m_moving)
|
|
return startTableOffset();
|
|
|
|
int offset = parentView()->qxt_d().pointToOffset(parentView()->mapToViewport((this->geometry()[0].topLeft())));
|
|
return offset;
|
|
}
|
|
|
|
/*!
|
|
* @desc returns the currently VISUAL offset (changes when a item moves)
|
|
*/
|
|
int QxtScheduleInternalItem::visualEndTableOffset() const
|
|
{
|
|
if (m_geometries.size() == 0 || !parentView())
|
|
return -1;
|
|
|
|
//are we in a move?
|
|
if (!m_moving)
|
|
return endTableOffset();
|
|
|
|
QRect rect = m_geometries[m_geometries.size()-1];
|
|
int endTableOffset = parentView()->qxt_d().pointToOffset(parentView()->mapToViewport(rect.bottomRight()));
|
|
return endTableOffset;
|
|
}
|
|
|
|
bool QxtScheduleInternalItem::setData(QVariant data, int role)
|
|
{
|
|
if (parentView() && parentView()->model())
|
|
{
|
|
return parentView()->model()->setData(modelIndex(), data, role);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QVariant QxtScheduleInternalItem::data(int role) const
|
|
{
|
|
if (modelIndex().isValid())
|
|
return modelIndex().data(role);
|
|
return QVariant();
|
|
}
|
|
|
|
int QxtScheduleInternalItem::startTableOffset() const
|
|
{
|
|
if (parentView() && parentView()->model())
|
|
{
|
|
int startTime = data(Qxt::ItemStartTimeRole).toInt();
|
|
int zoomDepth = parentView()->currentZoomDepth(Qxt::Second);
|
|
|
|
qint32 offset = startTime - parentView()->qxt_d().m_startUnixTime;
|
|
|
|
//the start of the current item does not fit in the view
|
|
//so we have to align it to the nearest boundaries
|
|
if (offset % zoomDepth)
|
|
{
|
|
int lower = offset / zoomDepth * zoomDepth;
|
|
int upper = lower + zoomDepth;
|
|
|
|
offset = (offset - lower >= upper - offset ? upper : lower);
|
|
|
|
return parentView()->qxt_d().unixTimeToOffset(offset + parentView()->qxt_d().m_startUnixTime);
|
|
}
|
|
|
|
//qDebug()<<"start Table offset kicked in... offset="<<offset<<"returning="<<parentView()->qxt_d().unixTimeToOffset(startTime);
|
|
return parentView()->qxt_d().unixTimeToOffset(startTime);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int QxtScheduleInternalItem::endTableOffset() const
|
|
{
|
|
return startTableOffset() + rows() - 1;
|
|
}
|
|
|
|
void QxtScheduleInternalItem::setStartTableOffset(int iOffset)
|
|
{
|
|
if (parentView() && parentView()->model())
|
|
{
|
|
setData(parentView()->qxt_d().offsetToUnixTime(iOffset), Qxt::ItemStartTimeRole);
|
|
}
|
|
}
|
|
|
|
void QxtScheduleInternalItem::setRowsUsed(int rows)
|
|
{
|
|
if (parentView() && parentView()->model())
|
|
{
|
|
int seconds = rows * parentView()->currentZoomDepth(Qxt::Second);
|
|
setData(seconds, Qxt::ItemDurationRole);
|
|
}
|
|
}
|
|
|
|
int QxtScheduleInternalItem::rows() const
|
|
{
|
|
if (parentView() && parentView()->model())
|
|
{
|
|
int iNumSecs = data(Qxt::ItemDurationRole).toInt();
|
|
int zoomDepth = parentView()->currentZoomDepth(Qxt::Second);
|
|
|
|
//the length of the current item does not fit in the view
|
|
//so we have to align it to the nearest boundaries
|
|
if (iNumSecs % zoomDepth)
|
|
{
|
|
int lower = iNumSecs / zoomDepth * zoomDepth;
|
|
int upper = lower + zoomDepth;
|
|
|
|
return (iNumSecs - lower >= upper - iNumSecs ? (upper / zoomDepth) : (lower / zoomDepth));
|
|
|
|
}
|
|
return (iNumSecs / zoomDepth);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool qxtScheduleItemLessThan(const QxtScheduleInternalItem * item1, const QxtScheduleInternalItem * item2)
|
|
{
|
|
if (item1->visualStartTableOffset() < item2->visualStartTableOffset())
|
|
return true;
|
|
if (item1->visualStartTableOffset() == item2->visualStartTableOffset() && item1->modelIndex().row() < item2->modelIndex().row())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool QxtScheduleInternalItem::contains(const QPoint & pt)
|
|
{
|
|
QVectorIterator<QRect> iterator(this->m_geometries);
|
|
while (iterator.hasNext())
|
|
{
|
|
if (iterator.next().contains(pt))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void QxtScheduleInternalItem::setGeometry(const QVector< QRect > geo)
|
|
{
|
|
if (!this->parent())
|
|
return;
|
|
|
|
QVector<QRect> oldGeo = this->m_geometries;
|
|
this->m_geometries.clear();
|
|
this->m_geometries = geo;
|
|
emit geometryChanged(this, oldGeo);
|
|
}
|
|
|
|
QVector< QRect > QxtScheduleInternalItem::geometry() const
|
|
{
|
|
return this->m_geometries;
|
|
}
|
|
|
|
void QxtScheduleInternalItem::startMove()
|
|
{
|
|
//save old geometry before we start to move
|
|
this->m_SavedGeometries = this->m_geometries;
|
|
m_moving = true;
|
|
}
|
|
|
|
void QxtScheduleInternalItem::resetMove()
|
|
{
|
|
this->setGeometry(this->m_SavedGeometries);
|
|
this->m_SavedGeometries.clear();
|
|
m_moving = false;
|
|
}
|
|
|
|
void QxtScheduleInternalItem::stopMove()
|
|
{
|
|
this->m_SavedGeometries.clear();
|
|
m_moving = false;
|
|
}
|
|
|
|
QModelIndex QxtScheduleInternalItem::modelIndex() const
|
|
{
|
|
QModelIndex indx;
|
|
if (parentView() && parentView()->model())
|
|
{
|
|
indx = parentView()->model()->index(this->m_iModelRow, 0);
|
|
}
|
|
return indx;
|
|
}
|