calendarui/views/dayview/src/calendaycontainer.cpp
author hgs
Mon, 28 Jun 2010 15:22:02 +0530
changeset 45 b6db4fd4947b
child 55 2c54b51f39c4
child 58 ef813d54df51
permissions -rw-r--r--
201025

/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Day view control of calendar
*
*/

//System includes
#include <QTime>
#include <QGraphicsLinearLayout>
#include <QGesture>

#ifdef _DEBUG
	#include <QPainter>
#endif

#include <hbabstractitemview.h>
#include <hbtextitem.h>
#include <hbmodeliterator.h>
#include <hbinstance.h>

//User includes
#include "calendaycontainer.h"
#include "calendayutils.h"
#include "calendayeventspane.h"
#include "calendayitem.h"
#include "calendaymodel.h"
#include "calendayinfo.h"
#include "calendayview.h"

// -----------------------------------------------------------------------------
// CalenDayContainer()
// Constructor
// -----------------------------------------------------------------------------
//
CalenDayContainer::CalenDayContainer(QGraphicsItem *parent) :
    HbAbstractItemContainer(parent), mGeometryUpdated(false), mInfo(0)
{
    getTimedEventLayoutValues(mLayoutValues);
    
    // Get the height of element
    qreal paneHeight = CalenDayUtils::instance()->hourElementHeight();
    
    QGraphicsLinearLayout* timeLinesLayout = new QGraphicsLinearLayout(
        Qt::Vertical, this);
    for (int i = 0; i < 24; i++) {
        CalenDayEventsPane* element = new CalenDayEventsPane(this);
        element->setPreferredHeight(paneHeight);
        element->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
		// Draw top line at midnight
        if (i == 0) {
            element->drawTopLine(true);
        }
        timeLinesLayout->addItem(element);
    }
    timeLinesLayout->setContentsMargins(0.0, 0.0, 0.0, 0.0);
    timeLinesLayout->setSpacing(0.0);

    setLayout(timeLinesLayout);
}

// -----------------------------------------------------------------------------
// ~CalenDayContainer()
// Destructor
// -----------------------------------------------------------------------------
//
CalenDayContainer::~CalenDayContainer()
{
}

// -----------------------------------------------------------------------------
// itemAdded()
// 
// -----------------------------------------------------------------------------
//
void CalenDayContainer::itemAdded( int index, HbAbstractViewItem *item, 
                                     bool animate )
{
    Q_UNUSED( index )
    Q_UNUSED( item )
    Q_UNUSED( animate )
}

// -----------------------------------------------------------------------------
// reset()
// 
// -----------------------------------------------------------------------------
//
void CalenDayContainer::reset()
{
	// remove absorbers if exist
	if (mAbsorbers.count())
		{
		qDeleteAll(mAbsorbers);
		mAbsorbers.clear();
		}
	
	// shrink event area when all-day events available after reset
	getTimedEventLayoutValues(mLayoutValues);
	
    // position need to be maintained while changing model
    QPointF position(pos());
    HbAbstractItemContainer::reset();
    setPos( position );
}

// -----------------------------------------------------------------------------
// itemRemoved()
// 
// -----------------------------------------------------------------------------
//
void CalenDayContainer::itemRemoved( HbAbstractViewItem *item, bool animate )
{
    Q_UNUSED( item )
    Q_UNUSED( animate )
}

// -----------------------------------------------------------------------------
// viewResized()
// 
// -----------------------------------------------------------------------------
//
void CalenDayContainer::viewResized (const QSizeF &size)
{
    resize(size);
    if (!mGeometryUpdated) {
        mGeometryUpdated = true;
        updateGeometry();
    }
}

// -----------------------------------------------------------------------------
// createDefaultPrototype()
// 
// -----------------------------------------------------------------------------
//
HbAbstractViewItem * CalenDayContainer::createDefaultPrototype() const
{
    CalenDayItem *calendarViewItem = new CalenDayItem;
    return calendarViewItem;
}

// -----------------------------------------------------------------------------
// setItemModelIndex()
// 
// -----------------------------------------------------------------------------
//
void CalenDayContainer::setItemModelIndex(HbAbstractViewItem *item, 
                                            const QModelIndex &index)
{
    QVariant variant = index.data( CalenDayEntry );
    AgendaEntry entry = variant.value<AgendaEntry>();
    
    if (entry.isTimedEntry()) {
        updateTimedEventGeometry( item, index );
        item->setParentItem(this);
    	}
    else if( entry.type() == AgendaEntry::TypeEvent ){
        updateAllDayEventGeometry( item, index );
        item->setParentItem(this);
    	} 
    else {
    	item->setVisible(false);
    }
    
    // last item
    if (index.row() == index.model()->rowCount() - 1) {
    	createTouchEventAbsorbers();
    }

    HbAbstractItemContainer::setItemModelIndex(item, index);
}

// -----------------------------------------------------------------------------
// updateTimedEventGeometry()
// Updates geometry of a timed event.
// -----------------------------------------------------------------------------
//
void CalenDayContainer::updateTimedEventGeometry(HbAbstractViewItem *item, 
                                                   const QModelIndex &index)
{
//safety check
    if ( !mInfo ) {
        return;
    }
    
    QVariant variant = index.data( CalenDayEntry );
    AgendaEntry entry = variant.value<AgendaEntry>();
    
//1. get 'virtual' event position from DayInfo
//TODO: k.g.: Day Info should store model index instead of keeping redundant data
    SCalenApptInfo apptInfo;
    apptInfo.iIndex = index;
    
    QDateTime start;
    QDateTime end;
    QDateTime currentDate;
    currentDate = static_cast<const CalenDayModel*>(index.model())->modelDate();
    CalenDayUtils::instance()->getEventValidStartEndTime( start, end, entry,
                                                          currentDate );
    apptInfo.iStartTime = start;
    apptInfo.iEndTime = end;
    
    TCalenInstanceId id = TCalenInstanceId::nullInstanceId();
    id.mEntryLocalUid = index.row(); //index.row() - temporary ID
    id.mInstanceTime = apptInfo.iStartTime; 
    apptInfo.iId = id;
    apptInfo.iAllDay = 0;
    apptInfo.iColor = 0xffff;
    
    int startSlot, endSlot, columnIdx, columns;
    mInfo->GetLocation( apptInfo, startSlot, endSlot, columnIdx, columns );
    

//2. set timed event's geometry
    qreal eventStartX(mLayoutValues.eventAreaX );
    qreal eventStartY(0.0);
    qreal eventWidth(mLayoutValues.eventAreaWidth);
    qreal eventHeight(0.0);
       
    //event's startY/height
    eventStartY = startSlot * mLayoutValues.slotHeight;
    eventHeight = (endSlot - startSlot) * mLayoutValues.slotHeight;

    //event's startX/width
    eventWidth /= columns;
    
    //In case when eventWidth will be smaller then 3.0un we need to 
    //make spacings between events smaller.
    //Check whether it's possible to shring them so the bubbles 
    //width can stay at 3.0un (time stripe + frame margins).
    qreal minWidth = 3.0 * mLayoutValues.unitInPixels; 
    if(eventWidth - mLayoutValues.eventMargin < minWidth){
        
        //Calculate new margin value
        //from totalMarginSpace we need to subtract 
        //mLayoutValues.eventMargin because first margin is always 1.5un
        qreal totalMarginSpace =  mLayoutValues.eventAreaWidth - minWidth * columns - mLayoutValues.eventMargin;
        qreal newMarginValue = totalMarginSpace / (columns - 1);
        
        //check if we managed to pack all the events into space we have
        if(newMarginValue > 0){
            
            eventWidth = minWidth;
        }
        else{
            //there's not enough space
            //new minWidth it's 1.5un (time stripe only)
            minWidth = 1.5 * mLayoutValues.unitInPixels; 
            totalMarginSpace =  mLayoutValues.eventAreaWidth - minWidth * columns - mLayoutValues.eventMargin;
            newMarginValue = totalMarginSpace / (columns - 1);
            eventWidth = minWidth;
        }
        
        //First column margin should be always 1.5un (mLayoutValues.eventMargin)
        eventStartX += columnIdx * (eventWidth + newMarginValue) + mLayoutValues.eventMargin;
    }
    else{
        //add margins between the event
        eventStartX += columnIdx * eventWidth + mLayoutValues.eventMargin;
        eventWidth -= mLayoutValues.eventMargin;
    }
    
    QRectF eventGeometry( eventStartX, eventStartY, eventWidth, eventHeight );
    item->setGeometry(eventGeometry);}


// -----------------------------------------------------------------------------
// updateAllDayEventGeometry()
// Updates geometry of a timed event.
// -----------------------------------------------------------------------------
//
void CalenDayContainer::updateAllDayEventGeometry(HbAbstractViewItem *item, 
                                                   const QModelIndex &index)
{
	//safety check
	if ( !mInfo ) {
	      return;
	}
	    
	QVariant variant = index.data( CalenDayEntry );
	AgendaEntry entry = variant.value<AgendaEntry>();
	    
	//1. get 'virtual' event position from DayInfo
	//TODO: k.g.: Day Info should store model index instead of keeping redundant data    
	SCalenApptInfo apptInfo;
	apptInfo.iIndex = index;
	
	
	QDateTime start;
    QDateTime end;
    QDateTime currentDate;
    currentDate = static_cast<const CalenDayModel*>(index.model())->modelDate();
    CalenDayUtils::instance()->getEventValidStartEndTime( start, end, entry,
                                                          currentDate );
    apptInfo.iStartTime = start;
    apptInfo.iEndTime = end;
	
	TCalenInstanceId id = TCalenInstanceId::nullInstanceId();
	id.mEntryLocalUid = index.row(); //index.row() - temporary ID
	id.mInstanceTime = apptInfo.iStartTime; 
	apptInfo.iId = id;
	apptInfo.iAllDay = true;
	apptInfo.iColor = 0xffff;
	
	int startSlot, endSlot, columnIdx, columns;
	mInfo->GetLocation( apptInfo, startSlot, endSlot, columnIdx, columns );
	
	//2. set timed event's geometry
	qreal eventStartX(0.0);
	qreal eventStartY(0.0);
	qreal eventWidth(mLayoutValues.eventAreaX);
	qreal eventHeight = (endSlot - startSlot) * mLayoutValues.slotHeight;

	
	//event's startX/width
	if ( columns > 1 ) {
		eventWidth /= columns;
		eventStartX += columnIdx * eventWidth + mLayoutValues.eventMargin;
		//add margins between the event
		eventWidth -= mLayoutValues.eventMargin;
	} else {
		eventStartX += mLayoutValues.eventMargin;
		eventWidth -= mLayoutValues.eventMargin;
	}
	
	QRectF eventGeometry( eventStartX, eventStartY, eventWidth, eventHeight );
	item->setGeometry(eventGeometry);
	    
}


// -----------------------------------------------------------------------------
// movingBackwards()
// 
// -----------------------------------------------------------------------------
//
void CalenDayContainer::getTimedEventLayoutValues(LayoutValues& layoutValues)
{
    // get the width of content area
    qreal contentWidth = CalenDayUtils::instance()->contentWidth();
//1.time column width -> eventAreaX[out]
    HbStyle style;
    HbDeviceProfile deviceProfile;
    layoutValues.unitInPixels = deviceProfile.unitValue();
    
    if ( mInfo && mInfo->AlldayCount())
    	{ // 9.5 -> all-day area width 
    	layoutValues.eventAreaX = 9.5 * layoutValues.unitInPixels;
    	}
    else
    	{
    	layoutValues.eventAreaX = 0;
    	}
    
//2. event area width -> eventAreaWidth[out]
    qreal emptyRightColumnWidth(0.0);
    emptyRightColumnWidth = 6.0 * layoutValues.unitInPixels; //pix (according to UI spec)
    layoutValues.eventAreaWidth = contentWidth - emptyRightColumnWidth - layoutValues.eventAreaX ;
//3. margins between the overlapping events -> eventMargin[out]
    layoutValues.eventMargin = 1.5 * layoutValues.unitInPixels;
//4. half-hour slot'h height -> slotHeight[out]
    //curent slot height corresponds to half an hour
    layoutValues.slotHeight = 
        CalenDayUtils::instance()->hourElementHeight() / 2;
    
    // 8.2 un (min. touchable event) from layout guide
    layoutValues.maxColumns = layoutValues.eventAreaWidth / (8.2 * layoutValues.unitInPixels);  
}

// -----------------------------------------------------------------------------
// setDayInfo()
// Sets day's info structer to the container.
// -----------------------------------------------------------------------------
//
void CalenDayContainer::setDayInfo( CalenDayInfo* dayInfo )
{
    mInfo = dayInfo;
}

// -----------------------------------------------------------------------------
// orientationChanged()
// Slot handles layout switch.
// -----------------------------------------------------------------------------
//
void CalenDayContainer::orientationChanged(Qt::Orientation orientation)
{
	getTimedEventLayoutValues(mLayoutValues);
	
    Q_UNUSED( orientation )
    QList<HbAbstractViewItem *> items = this->items();
    int count(items.count());
    for (int i = 0; i < count; i++) {
        QModelIndex modelIndex = items[i]->modelIndex();
        if (modelIndex.isValid()) {
            QVariant variant = modelIndex.data(CalenDayEntry);
            AgendaEntry entry = variant.value<AgendaEntry> ();
            if (entry.isTimedEntry()) {
                updateTimedEventGeometry(items[i], modelIndex);
            }
        }
    }
    
    createTouchEventAbsorbers();
}

// -----------------------------------------------------------------------------
// createTouchEventAbsorbers()
// Creates absorbers which prevent touching to small items
// -----------------------------------------------------------------------------
//
void CalenDayContainer::createTouchEventAbsorbers()
{
	// remove absorbers if exist
	if (mAbsorbers.count())
		{
		qDeleteAll(mAbsorbers);
		mAbsorbers.clear();
		}
	
	const QList<CalenTimeRegion>& regionList = mInfo->RegionList();
	
	for(int i=0; i < regionList.count(); i++)
		{
		if(regionList[i].iColumns.count() > mLayoutValues.maxColumns )
			{
			TouchEventAbsorber* absorber = 
				crateAbsorberBetweenSlots(regionList[i].iStartSlot, regionList[i].iEndSlot);
			
			mAbsorbers.append(absorber);
			}
		}
	
}

// -----------------------------------------------------------------------------
// crateAbsorberBetweenSlots()
// Creates single absorber in given location
// -----------------------------------------------------------------------------
//
TouchEventAbsorber *CalenDayContainer::crateAbsorberBetweenSlots
												(int startSlot, int endSlot)
{
    TouchEventAbsorber *absorber = new TouchEventAbsorber(this);
    absorber->setZValue(1000);
    absorber->setVisible(true);
    
    absorber->setGeometry( mLayoutValues.eventAreaX,			// x
			startSlot * mLayoutValues.slotHeight,				// y
			mLayoutValues.eventAreaWidth,						// w
			(endSlot-startSlot) * mLayoutValues.slotHeight ); 	// h
    
    return absorber;
}


// -----------------------------------------------------------------------------
// TouchEventAbsorber::gestureEvent()
// Handles tap event on overlapping area (currently it leads to Agenda View - 
// as described in UI spec)
// -----------------------------------------------------------------------------
//
void TouchEventAbsorber::gestureEvent(QGestureEvent *event)
{
    QTapGesture *tapGesture = qobject_cast<QTapGesture*> (event->gesture(
        Qt::TapGesture));
    
    if (tapGesture && tapGesture->state() == Qt::GestureFinished)
    	{
    	CalenDayView* dayView = static_cast<CalenDayView*>
                (CalenDayUtils::instance()->mainWindow()->currentView());
    	
    	dayView->changeView(ECalenAgendaView);
    	}
}

// -----------------------------------------------------------------------------
// TouchEventAbsorber()
// default ctor
// -----------------------------------------------------------------------------
//
TouchEventAbsorber::TouchEventAbsorber(QGraphicsItem *parent) : HbWidget(parent)
{
#ifdef _DEBUG
    setFlag(QGraphicsItem::ItemHasNoContents, false);
#endif	
    grabGesture(Qt::TapGesture);    	    
}

// -----------------------------------------------------------------------------
// TouchEventAbsorber()
// default dtor
// -----------------------------------------------------------------------------
//
TouchEventAbsorber::~TouchEventAbsorber()
{
	
}

// -----------------------------------------------------------------------------
// TouchEventAbsorber::paint()
// used for debugging purposes to see absorbers areas
// -----------------------------------------------------------------------------
//
#ifdef _DEBUG
void TouchEventAbsorber::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
								QWidget *widget)
{
	Q_UNUSED(option)
	Q_UNUSED(widget)
	
	painter->save();
	QPen pen;
	pen.setWidth(2);
	pen.setColor(Qt::red);
	painter->setPen(pen);
	painter->drawRect(boundingRect());
	painter->restore();
}
#endif
// End of File