Commit 3c69d390 authored by Dag Andersen's avatar Dag Andersen
Browse files

Fix Bug 286564 - plan do not update the resources allocation number when selected

Add tooltips/whatsthis and show more info to make the dialog more userfriendly
Fix bug in dynamic allocation
BUG: 286564
parent b052d05b
......@@ -1150,7 +1150,6 @@ bool KPlatoXmlLoaderBase::load( ResourceGroupRequest* gr, const KoXmlElement& el
}
gr->group()->registerRequest( gr );
gr->setUnits( element.attribute( "units" ).toInt() );
KoXmlNode n = element.firstChild();
for ( ; ! n.isNull(); n = n.nextSibling() ) {
......@@ -1168,6 +1167,10 @@ bool KPlatoXmlLoaderBase::load( ResourceGroupRequest* gr, const KoXmlElement& el
}
}
}
// meaning of m_units changed
int x = element.attribute("units").toInt() -gr->count();
gr->setUnits( x > 0 ? x : 0 );
return true;
}
......
......@@ -28,8 +28,8 @@
// The Plan file syntax is used in parts of the KPlatoWork file, so:
// * If you change PLAN_FILE_SYNTAX_VERSION, change PLANWORK_FILE_SYNTAX_VERSION too!
// * You don't need to change PLAN_FILE_SYNTAX_VERSION when you change KPLATOWORK_FILE_SYNTAX_VERSION
#define PLAN_FILE_SYNTAX_VERSION "0.6.5"
#define PLANWORK_FILE_SYNTAX_VERSION "0.6.5"
#define PLAN_FILE_SYNTAX_VERSION "0.6.6"
#define PLANWORK_FILE_SYNTAX_VERSION "0.6.6"
#define CURRENTSCHEDULE -1
#define NOTSCHEDULED -2
......
......@@ -1444,10 +1444,10 @@ long Resource::allocationSuitability( const DateTime &time, const Duration &dura
Duration e;
if ( m_type == Type_Team ) {
foreach ( Resource *r, teamMembers() ) {
e += r->effort( time, duration, backward );
e += r->effort( time, duration, 100, backward );
}
} else {
e = effort( time, duration, backward );
e = effort( time, duration, 100, backward );
}
return e.minutes();
}
......@@ -1899,16 +1899,14 @@ QList<ResourceRequest*> ResourceGroupRequest::resourceRequests( bool resolveTeam
return lst;
}
bool ResourceGroupRequest::load(KoXmlElement &element, Project &project) {
bool ResourceGroupRequest::load(KoXmlElement &element, XMLLoaderObject &status) {
//kDebug(planDbg());
m_group = project.findResourceGroup(element.attribute("group-id"));
m_group = status.project().findResourceGroup(element.attribute("group-id"));
if (m_group == 0) {
kError()<<"The referenced resource group does not exist: group id="<<element.attribute("group-id");
return false;
}
m_group->registerRequest(this);
m_units = element.attribute("units").toInt();
KoXmlNode n = element.firstChild();
for ( ; ! n.isNull(); n = n.nextSibling() ) {
......@@ -1918,7 +1916,7 @@ bool ResourceGroupRequest::load(KoXmlElement &element, Project &project) {
KoXmlElement e = n.toElement();
if (e.tagName() == "resource-request") {
ResourceRequest *r = new ResourceRequest();
if (r->load(e, project))
if (r->load(e, status.project()))
addResourceRequest(r);
else {
kError()<<"Failed to load resource request";
......@@ -1926,6 +1924,13 @@ bool ResourceGroupRequest::load(KoXmlElement &element, Project &project) {
}
}
}
// meaning of m_units changed
// Pre 0.6.6 the number *included* all requests, now it is in *addition* to resource requests
m_units = element.attribute("units").toInt();
if ( status.version() < "0.6.6" ) {
int x = m_units - m_resourceRequests.count();
m_units = x > 0 ? x : 0;
}
return true;
}
......@@ -2064,7 +2069,7 @@ void ResourceGroupRequest::resetDynamicAllocations()
void ResourceGroupRequest::allocateDynamicRequests( const DateTime &time, const Duration &effort, Schedule *ns, bool backward )
{
int num = m_units - m_resourceRequests.count();
int num = m_units;
if ( num <= 0 ) {
return;
}
......@@ -2074,10 +2079,17 @@ void ResourceGroupRequest::allocateDynamicRequests( const DateTime &time, const
Duration e = effort / m_units;
QMap<long, ResourceRequest*> map;
foreach ( Resource *r, m_group->resources() ) {
if ( r->type() == Resource::Type_Team || find( r ) ) {
if ( r->type() == Resource::Type_Team ) {
continue;
}
ResourceRequest *rr = find( r );
if ( rr ) {
if ( rr->isDynamicallyAllocated() ) {
--num; // allready allocated
}
continue;
}
ResourceRequest *rr = new ResourceRequest( r, r->units() );
rr = new ResourceRequest( r, r->units() );
long s = rr->allocationSuitability( time, e, ns, backward );
if ( s == 0 ) {
// not suitable at all
......@@ -2086,11 +2098,12 @@ void ResourceGroupRequest::allocateDynamicRequests( const DateTime &time, const
map.insertMulti( s, rr );
}
}
num = qMin( map.count(), num );
for ( --num; num >= 0; --num ) {
ResourceRequest *r = map.take( map.keys().last() );
for ( --num; num >= 0 && ! map.isEmpty(); --num ) {
long key = map.keys().last();
ResourceRequest *r = map.take( key );
r->setAllocatedDynaically( true );
addResourceRequest( r );
kDebug(planDbg())<<key<<r;
}
qDeleteAll( map ); // delete the unused
}
......
......@@ -739,7 +739,7 @@ public:
/// Team resources are included but *not* the team members.
/// Any dynamically allocated resource is not included.
QList<Resource*> requestedResources() const;
bool load( KoXmlElement &element, Project &project );
bool load( KoXmlElement &element, XMLLoaderObject &status );
void save( QDomElement &element ) const;
/// The number of requested resources
......
......@@ -331,12 +331,12 @@ bool Task::load(KoXmlElement &element, XMLLoaderObject &status ) {
ResourceGroupRequest *r = m_requests.findGroupRequestById( e.attribute("group-id") );
if ( r ) {
kWarning()<<"Multiple requests to same group, loading into existing group";
if ( ! r->load( e, status.project() ) ) {
if ( ! r->load( e, status ) ) {
kError()<<"Failed to load resource request";
}
} else {
r = new ResourceGroupRequest();
if (r->load(e, status.project())) {
if (r->load(e, status)) {
addRequest(r);
} else {
kError()<<"Failed to load resource request";
......
......@@ -255,8 +255,14 @@ QVariant ResourceAllocationModel::required( const Resource *res, int role ) cons
case Qt::TextAlignmentRole:
return Qt::AlignCenter;
case Qt::StatusTipRole:
case Qt::WhatsThisRole:
return QVariant();
case Qt::WhatsThisRole:
return i18nc( "@info:whatsthis", "<title>Required Resources</title>"
"<para>A working resource can be assigned one or more required resource."
" A required resource is a material resource that the working resource depends on"
" in order to do the work.</para>"
"<para>To be able to use a material resource as a required resource, the material resource"
" must be part of a group of type <emphasis>Material</emphasis>.</para>" );
}
return QVariant();
}
......@@ -345,13 +351,23 @@ QVariant ResourceAllocationModel::headerData( int section, int role )
case RequestType: return ToolTip::resourceType();
case RequestAllocation: return i18n( "Resource allocation" );
case RequestMaximum: return i18nc( "@info:tootip", "Available resources or resource units" );
case RequestRequired: return i18nc( "@info:tootip", "Required Resources" );
case RequestRequired: return i18nc( "@info:tootip", "Required material resources" );
default: return QVariant();
}
} else if ( role == Qt::WhatsThisRole ) {
switch ( section ) {
case RequestRequired:
return i18nc( "@info:whatsthis", "<title>Required Resources</title>"
"<para>A working resource can be assigned one or more required resource."
" A required resource is a material resource that the working resource depends on"
" in order to do the work.</para>"
"<para>To be able to use a material resource as a required resource, the material resource"
" must be part of a group of type Material.</para>" );
default: return QVariant();
}
}
return QVariant();
}
//--------------------------------------
ResourceAllocationItemModel::ResourceAllocationItemModel( QObject *parent )
......@@ -507,6 +523,23 @@ void ResourceAllocationItemModel::filldata( Task *task )
}
}
bool ResourceAllocationItemModel::hasMaterialResources() const
{
if ( ! m_project ) {
return false;
}
foreach ( const ResourceGroup *g, m_project->resourceGroups() ) {
if ( g->type() == ResourceGroup::Type_Material ) {
foreach ( const Resource *r, g->resources() ) {
if ( r->type() == Resource::Type_Material ) {
return true;
}
}
}
}
return false;
}
Qt::ItemFlags ResourceAllocationItemModel::flags( const QModelIndex &index ) const
{
Qt::ItemFlags flags = ItemModelBase::flags( index );
......@@ -524,11 +557,13 @@ Qt::ItemFlags ResourceAllocationItemModel::flags( const QModelIndex &index ) con
break;
case ResourceAllocationModel::RequestRequired: {
Resource *r = resource( index );
if ( m_resourceCache.contains( r ) && m_resourceCache[ r ]->units() > 0 ) {
flags |= ( Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
}
if ( r && r->type() != Resource::Type_Work ) {
flags &= ~( Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
} else if ( m_resourceCache.contains( r ) && m_resourceCache[ r ]->units() > 0 ) {
flags |= ( Qt::ItemIsEditable | Qt::ItemIsUserCheckable );
if ( ! hasMaterialResources() ) {
flags &= ~Qt::ItemIsEnabled;
}
}
break;
}
......@@ -663,6 +698,17 @@ QVariant ResourceAllocationItemModel::allocation( const ResourceGroup *group, co
return QVariant();
}
int ResourceAllocationItemModel::requestedResources( const ResourceGroup *res ) const
{
int c = 0;
foreach ( const Resource *r, res->resources() ) {
if ( m_resourceCache.contains( r ) && m_resourceCache[ r ]->units() > 0 ) {
++c;
}
}
return c;
}
QVariant ResourceAllocationItemModel::allocation( const ResourceGroup *res, int role ) const
{
if ( m_model.task() == 0 ) {
......@@ -673,13 +719,36 @@ QVariant ResourceAllocationItemModel::allocation( const ResourceGroup *res, int
}
switch ( role ) {
case Qt::DisplayRole:
return QString(" %1 (%2)" )
.arg( qMax( m_groupCache[ res ]->units(), allocation( res, Role::Minimum ).toInt() ) )
.arg(requestedResources( res ) );
case Qt::EditRole:
return qMax( m_groupCache[ res ]->units(), allocation( res, Role::Minimum ).toInt() );
case Qt::ToolTipRole:
return i18np( "%1 resource allocated", "%1 resources allocated", allocation( res, Qt::DisplayRole ).toInt() );
case Qt::ToolTipRole: {
QString s1 = i18ncp( "@info:tooltip",
"%1 resource requested for dynamic allocation",
"%1 resources requested for dynamic allocation",
allocation( res, Qt::EditRole ).toInt() );
QString s2 = i18ncp( "@info:tooltip",
"%1 resource allocated",
"%1 resources allocated",
requestedResources( res ) );
return i18nc( "@info:tooltip", "%1<nl/>%2", s1, s2 );
}
case Qt::WhatsThisRole: {
return i18nc( "@info:whatsthis",
"<title>Group allocations</title>"
"<para>You can allocate a number of resources from a group and let"
" the scheduler select from the available resources at the time of scheduling.</para>"
" These dynamically allocated resources will be in addition to any resource you have allocated specifically." );
}
case Role::Minimum: {
return 0;
}
case Role::Maximum: {
return res->numResources() - requestedResources( res );
}
default:
return m_model.allocation( res, role );
}
......@@ -691,18 +760,30 @@ bool ResourceAllocationItemModel::setAllocation( Resource *res, const QVariant &
switch ( role ) {
case Qt::EditRole: {
m_resourceCache[ res ]->setUnits( value.toInt() );
QModelIndex idx = index( res->parentGroup() );
emit dataChanged( index( idx.row(), 0, QModelIndex() ), index( idx.row(), columnCount() - 1, QModelIndex() ) );
return true;
}
case Qt::CheckStateRole:
case Qt::CheckStateRole: {
if ( ! m_resourceCache.contains( res ) ) {
m_resourceCache[ res ] = new ResourceRequest( res, 0 );
}
if ( m_resourceCache[ res ]->units() == 0 ) {
m_resourceCache[ res ]->setUnits( 100 );
ResourceGroup *g = res->parentGroup();
if ( m_groupCache.contains( g ) ) {
ResourceGroupRequest *gr = m_groupCache[ g ];
if ( gr->units() + requestedResources( g ) > g->numResources() ) {
gr->setUnits( gr->units() - 1 );
}
}
} else {
m_resourceCache[ res ]->setUnits( 0 );
}
QModelIndex idx = index( res->parentGroup() );
emit dataChanged( index( idx.row(), 0, QModelIndex() ), index( idx.row(), columnCount() - 1, QModelIndex() ) );
return true;
}
}
return false;
}
......@@ -715,11 +796,30 @@ bool ResourceAllocationItemModel::setAllocation( ResourceGroup *res, const QVari
m_groupCache[ res ] = new ResourceGroupRequest( res, 0 );
}
m_groupCache[ res ]->setUnits( value.toInt() );
emit dataChanged( index( res ), index( res ) );
return true;
}
return false;
}
QVariant ResourceAllocationItemModel::maximum( const ResourceGroup *res, int role ) const
{
switch ( role ) {
case Qt::DisplayRole: {
int c = res->numResources() - requestedResources( res );
if ( m_groupCache.contains( res ) ) {
c -= m_groupCache[ res ]->units();
}
return i18nc( "1: free resources, 2: number of resources", "%1 of %2", c, res->numResources() );
}
case Qt::ToolTipRole:
return i18ncp( "@info:tooltip", "There is %1 resource available in this group", "There are %1 resources available in this group", res->numResources() );
default:
return m_model.maximum( res, role );
}
return QVariant();
}
QVariant ResourceAllocationItemModel::required( const QModelIndex &idx, int role ) const
{
if ( m_model.task() == 0 ) {
......@@ -746,6 +846,9 @@ QVariant ResourceAllocationItemModel::required( const QModelIndex &idx, int role
case Qt::ToolTipRole:
switch ( res->type() ) {
case Resource::Type_Work: {
if ( ! hasMaterialResources() ) {
return i18nc( "@info:tooltip", "No material resources available" );
}
QStringList lst;
if ( m_requiredChecked[ res ] ) {
foreach ( const Resource *r, required( idx ) ) {
......@@ -766,7 +869,7 @@ QVariant ResourceAllocationItemModel::required( const QModelIndex &idx, int role
}
break;
default:
break;
return m_model.required( res, role );
}
return QVariant();
}
......@@ -828,10 +931,17 @@ QVariant ResourceAllocationItemModel::data( const QModelIndex &index, int role )
} else {
ResourceGroup *g = qobject_cast<ResourceGroup*>( obj );
if ( g ) {
if ( index.column() == ResourceAllocationModel::RequestAllocation ) {
return allocation( g, role );
switch ( index.column() ) {
case ResourceAllocationModel::RequestAllocation:
result = allocation( g, role );
break;
case ResourceAllocationModel::RequestMaximum:
result = maximum( g, role );
break;
default:
result = m_model.data( g, index.column(), role );
break;
}
result = m_model.data( g, index.column(), role );
}
}
if ( role == Qt::DisplayRole && ! result.isValid() ) {
......@@ -898,10 +1008,7 @@ QVariant ResourceAllocationItemModel::headerData( int section, Qt::Orientation o
return Qt::AlignCenter;
}
}
if ( role == Qt::ToolTipRole ) {
return m_model.headerData( section, role );
}
return ItemModelBase::headerData(section, orientation, role);
return m_model.headerData( section, role );
}
QAbstractItemDelegate *ResourceAllocationItemModel::createDelegate( int col, QWidget *parent ) const
......
......@@ -152,9 +152,15 @@ protected:
bool setAllocation( ResourceGroup *res, const QVariant &value, int role );
bool setAllocation( Resource *res, const QVariant &value, int role );
QVariant maximum( const ResourceGroup *res, int role ) const;
bool setRequired( const QModelIndex &idx, const QVariant &value, int role );
QVariant required( const QModelIndex &idx, int role ) const;
private:
int requestedResources( const ResourceGroup *res ) const;
bool hasMaterialResources() const;
private:
ResourceAllocationModel m_model;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment