2018-07-03 01:42:31 -05:00
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2018 - equinor ASA
//
// ResInsight 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 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
# include "RimWellPathGeometryDef.h"
# include "cafCmdFeatureMenuBuilder.h"
# include "cafPdmUiTableViewEditor.h"
# include "cafPdmUiTreeOrdering.h"
# include "RigWellPath.h"
# include "RiaPolyArcLineSampler.h"
# include "RimWellPathTarget.h"
# include "RimModeledWellPath.h"
2018-07-04 09:49:04 -05:00
# include "RiaSCurveCalculator.h"
2018-08-07 06:15:41 -05:00
# include "RiaLogging.h"
2018-08-14 04:25:08 -05:00
# include "RiaJCurveCalculator.h"
2018-08-29 09:03:31 -05:00
# include "cafPdmUiPushButtonEditor.h"
# include "WellPathCommands/RicCreateWellTargetsPickEventHandler.h"
# include "RiuViewerCommands.h"
2018-07-03 01:42:31 -05:00
namespace caf
{
template < >
void caf : : AppEnum < RimWellPathGeometryDef : : WellStartType > : : setUp ( )
{
addItem ( RimWellPathGeometryDef : : START_AT_FIRST_TARGET , " START_AT_FIRST_TARGET " , " Start at First Target " ) ;
addItem ( RimWellPathGeometryDef : : START_AT_SURFACE , " START_AT_SURFACE " , " Start at Surface " ) ;
addItem ( RimWellPathGeometryDef : : START_FROM_OTHER_WELL , " START_FROM_OTHER_WELL " , " Branch " ) ;
addItem ( RimWellPathGeometryDef : : START_AT_AUTO_SURFACE , " START_AT_AUTO_SURFACE " , " Auto Surface " ) ;
setDefault ( RimWellPathGeometryDef : : START_AT_FIRST_TARGET ) ;
}
}
CAF_PDM_SOURCE_INIT ( RimWellPathGeometryDef , " WellPathGeometryDef " ) ;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPathGeometryDef : : RimWellPathGeometryDef ( )
2018-08-29 09:03:31 -05:00
: m_pickTargetsEventHandler ( new RicCreateWellTargetsPickEventHandler ( this ) )
2018-07-03 01:42:31 -05:00
{
CAF_PDM_InitObject ( " Trajectory " , " :/Well.png " , " " , " " ) ;
CAF_PDM_InitFieldNoDefault ( & m_wellStartType , " WellStartType " , " Start Type " , " " , " " , " " ) ;
2018-08-29 09:03:31 -05:00
CAF_PDM_InitField ( & m_referencePointXyz , " ReferencePos " , cvf : : Vec3d ( 0 , 0 , 0 ) , " UTM Reference Point " , " " , " " , " " ) ;
2018-07-03 01:42:31 -05:00
CAF_PDM_InitFieldNoDefault ( & m_parentWell , " ParentWell " , " Parent Well " , " " , " " , " " ) ;
CAF_PDM_InitField ( & m_kickoffDepthOrMD , " KickoffDepthOrMD " , 100.0 , " Kickoff Depth " , " " , " " , " " ) ;
CAF_PDM_InitFieldNoDefault ( & m_wellTargets , " WellPathTargets " , " Well Targets " , " " , " " , " " ) ;
m_wellTargets . uiCapability ( ) - > setUiEditorTypeName ( caf : : PdmUiTableViewEditor : : uiEditorTypeName ( ) ) ;
//m_wellTargets.uiCapability()->setUiTreeHidden(true);
m_wellTargets . uiCapability ( ) - > setUiTreeChildrenHidden ( true ) ;
m_wellTargets . uiCapability ( ) - > setUiLabelPosition ( caf : : PdmUiItemInfo : : TOP ) ;
m_wellTargets . uiCapability ( ) - > setCustomContextMenuEnabled ( true ) ;
2018-08-29 09:03:31 -05:00
CAF_PDM_InitField ( & m_pickPointsEnabled , " m_pickPointsEnabled " , false , " " , " " , " " , " " ) ;
caf : : PdmUiPushButtonEditor : : configureEditorForField ( & m_pickPointsEnabled ) ;
2018-07-03 01:42:31 -05:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPathGeometryDef : : ~ RimWellPathGeometryDef ( )
{
2018-08-30 06:22:28 -05:00
RiuViewerCommands : : removePickEventHandlerIfActive ( m_pickTargetsEventHandler ) ;
2018-08-29 09:03:31 -05:00
delete m_pickTargetsEventHandler ;
2018-07-03 01:42:31 -05:00
2018-08-29 09:03:31 -05:00
m_pickTargetsEventHandler = nullptr ;
2018-07-03 01:42:31 -05:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf : : ref < RigWellPath > RimWellPathGeometryDef : : createWellPathGeometry ( )
{
cvf : : ref < RigWellPath > wellPathGeometry = new RigWellPath ;
2018-07-31 01:44:37 -05:00
if ( activeWellTargets ( ) . size ( ) < 2 ) return wellPathGeometry ;
2018-07-03 01:42:31 -05:00
2018-07-04 09:49:04 -05:00
RiaPolyArcLineSampler arcLineSampler ( startTangent ( ) , lineArcEndpoints ( ) ) ;
2018-07-03 01:42:31 -05:00
arcLineSampler . sampledPointsAndMDs ( 30 ,
false ,
& ( wellPathGeometry - > m_wellPathPoints ) ,
& ( wellPathGeometry - > m_measuredDepths ) ) ;
return wellPathGeometry ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : updateWellPathVisualization ( )
{
RimModeledWellPath * modWellPath ;
this - > firstAncestorOrThisOfTypeAsserted ( modWellPath ) ;
modWellPath - > updateWellPathVisualization ( ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-08-29 09:03:31 -05:00
void RimWellPathGeometryDef : : insertTarget ( const RimWellPathTarget * targetToInsertBefore , RimWellPathTarget * targetToInsert )
2018-07-03 01:42:31 -05:00
{
size_t index = m_wellTargets . index ( targetToInsertBefore ) ;
if ( index < m_wellTargets . size ( ) ) m_wellTargets . insert ( index , targetToInsert ) ;
else m_wellTargets . push_back ( targetToInsert ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : deleteTarget ( RimWellPathTarget * targetTodelete )
{
m_wellTargets . removeChildObject ( targetTodelete ) ;
delete targetTodelete ;
}
2018-07-31 04:44:15 -05:00
//--------------------------------------------------------------------------------------------------
2018-08-06 00:52:02 -05:00
///
2018-07-31 04:44:15 -05:00
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : appendTarget ( )
{
RimWellPathTarget * wellPathTarget = nullptr ;
auto targets = m_wellTargets . childObjects ( ) ;
if ( targets . empty ( ) )
{
wellPathTarget = new RimWellPathTarget ;
}
else
{
wellPathTarget = dynamic_cast < RimWellPathTarget * > ( targets . back ( ) - > xmlCapability ( ) - > copyByXmlSerialization ( caf : : PdmDefaultObjectFactory : : instance ( ) ) ) ;
}
if ( wellPathTarget )
{
m_wellTargets . push_back ( wellPathTarget ) ;
}
}
2018-08-14 07:23:55 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RimWellPathTarget * RimWellPathGeometryDef : : firstActiveTarget ( ) const
{
for ( const RimWellPathTarget * target : m_wellTargets )
{
if ( target - > isEnabled ( ) )
{
return target ;
}
}
return nullptr ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RimWellPathTarget * RimWellPathGeometryDef : : lastActiveTarget ( ) const
{
if ( ! m_wellTargets . size ( ) ) return nullptr ;
for ( int tIdx = static_cast < int > ( m_wellTargets . size ( ) - 1 ) ; tIdx > = 0 ; - - tIdx )
{
if ( m_wellTargets [ tIdx ] - > isEnabled ( ) )
{
return m_wellTargets [ tIdx ] ;
}
}
return nullptr ;
}
2018-08-29 09:03:31 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : enableTargetPointPicking ( bool isEnabling )
{
if ( isEnabling )
{
m_pickPointsEnabled = true ;
2018-08-30 06:22:28 -05:00
RiuViewerCommands : : setPickEventHandler ( m_pickTargetsEventHandler ) ;
2018-08-29 09:03:31 -05:00
updateConnectedEditors ( ) ;
}
else
{
2018-08-30 06:22:28 -05:00
RiuViewerCommands : : removePickEventHandlerIfActive ( m_pickTargetsEventHandler ) ;
2018-08-29 09:03:31 -05:00
m_pickPointsEnabled = false ;
updateConnectedEditors ( ) ;
}
}
2018-07-04 09:49:04 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList < caf : : PdmOptionItemInfo > RimWellPathGeometryDef : : calculateValueOptions ( const caf : : PdmFieldHandle * fieldNeedingOptions ,
bool * useOptionsOnly )
{
QList < caf : : PdmOptionItemInfo > options ;
if ( fieldNeedingOptions = = & m_wellStartType )
{
options . push_back ( caf : : PdmOptionItemInfo ( " Start at First Target " , RimWellPathGeometryDef : : START_AT_FIRST_TARGET ) ) ;
}
return options ;
}
2018-07-03 01:42:31 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : fieldChangedByUi ( const caf : : PdmFieldHandle * changedField ,
const QVariant & oldValue ,
const QVariant & newValue )
{
2018-08-29 09:03:31 -05:00
if ( & m_referencePointXyz = = changedField )
2018-07-03 01:42:31 -05:00
{
std : : cout < < " fieldChanged " < < std : : endl ;
}
2018-08-29 09:03:31 -05:00
else if ( changedField = = & m_pickPointsEnabled )
{
enableTargetPointPicking ( m_pickPointsEnabled ) ;
}
2018-07-03 01:42:31 -05:00
updateWellPathVisualization ( ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : defineUiOrdering ( QString uiConfigName , caf : : PdmUiOrdering & uiOrdering )
{
uiOrdering . add ( & m_wellStartType ) ;
if ( m_wellStartType = = START_FROM_OTHER_WELL )
{
uiOrdering . add ( & m_parentWell ) ;
m_kickoffDepthOrMD . uiCapability ( ) - > setUiName ( " Measured Depth " ) ;
uiOrdering . add ( & m_kickoffDepthOrMD ) ;
}
if ( m_wellStartType = = START_AT_SURFACE )
{
m_kickoffDepthOrMD . uiCapability ( ) - > setUiName ( " Kick-Off Depth " ) ;
uiOrdering . add ( & m_kickoffDepthOrMD ) ;
}
2018-08-29 09:03:31 -05:00
uiOrdering . add ( & m_referencePointXyz ) ;
2018-07-03 01:42:31 -05:00
uiOrdering . add ( & m_wellTargets ) ;
2018-08-29 09:03:31 -05:00
uiOrdering . add ( & m_pickPointsEnabled ) ;
2018-07-03 01:42:31 -05:00
uiOrdering . skipRemainingFields ( true ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : defineUiTreeOrdering ( caf : : PdmUiTreeOrdering & uiTreeOrdering , QString uiConfigName )
{
uiTreeOrdering . skipRemainingChildren ( true ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-07-31 01:44:37 -05:00
std : : vector < RimWellPathTarget * > RimWellPathGeometryDef : : activeWellTargets ( ) const
2018-07-03 01:42:31 -05:00
{
2018-07-31 01:44:37 -05:00
std : : vector < RimWellPathTarget * > active ;
for ( const auto & wt : m_wellTargets )
{
if ( wt - > isEnabled ( ) )
{
active . push_back ( wt ) ;
}
}
2018-07-04 09:49:04 -05:00
2018-07-31 01:44:37 -05:00
return active ;
}
2018-07-04 09:49:04 -05:00
2018-07-31 01:44:37 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std : : vector < cvf : : Vec3d > RimWellPathGeometryDef : : lineArcEndpoints ( ) const
{
2018-08-14 04:25:08 -05:00
double prevSegmentEndAzi = 0 ;
double prevSegmentEndInc = 0 ;
2018-07-31 01:44:37 -05:00
std : : vector < RimWellPathTarget * > activeWellPathTargets = activeWellTargets ( ) ;
CVF_ASSERT ( activeWellPathTargets . size ( ) > 1 ) ;
2018-07-04 09:49:04 -05:00
2018-07-31 01:44:37 -05:00
std : : vector < cvf : : Vec3d > endPoints ;
2018-08-29 09:03:31 -05:00
endPoints . push_back ( activeWellPathTargets [ 0 ] - > targetPointXYZ ( ) + m_referencePointXyz ( ) ) ;
2018-07-04 09:49:04 -05:00
2018-07-31 01:44:37 -05:00
for ( size_t tIdx = 0 ; tIdx < activeWellPathTargets . size ( ) - 1 ; + + tIdx )
{
RimWellPathTarget * target1 = activeWellPathTargets [ tIdx ] ;
RimWellPathTarget * target2 = activeWellPathTargets [ tIdx + 1 ] ;
2018-07-04 09:49:04 -05:00
2018-08-14 04:25:08 -05:00
if ( target1 - > targetType ( ) = = RimWellPathTarget : : POINT_AND_TANGENT
2018-07-04 09:49:04 -05:00
& & target2 - > targetType ( ) = = RimWellPathTarget : : POINT_AND_TANGENT )
{
RiaSCurveCalculator sCurveCalc ( target1 - > targetPointXYZ ( ) ,
target1 - > azimuth ( ) ,
target1 - > inclination ( ) ,
2018-08-14 04:25:08 -05:00
target1 - > radius2 ( ) ,
2018-07-04 09:49:04 -05:00
target2 - > targetPointXYZ ( ) ,
target2 - > azimuth ( ) ,
target2 - > inclination ( ) ,
2018-08-14 04:25:08 -05:00
target2 - > radius1 ( ) ) ;
2018-07-04 09:49:04 -05:00
2018-08-07 06:15:41 -05:00
if ( ! sCurveCalc . isOk ( ) )
{
2018-08-10 05:10:39 -05:00
double p1p2Length = ( target2 - > targetPointXYZ ( ) - target1 - > targetPointXYZ ( ) ) . length ( ) ;
2018-08-07 06:15:41 -05:00
sCurveCalc = RiaSCurveCalculator : : fromTangentsAndLength ( target1 - > targetPointXYZ ( ) ,
target1 - > azimuth ( ) ,
target1 - > inclination ( ) ,
2018-08-10 05:10:39 -05:00
0.2 * p1p2Length ,
2018-08-07 06:15:41 -05:00
target2 - > targetPointXYZ ( ) ,
target2 - > azimuth ( ) ,
target2 - > inclination ( ) ,
2018-08-10 05:10:39 -05:00
0.2 * p1p2Length ) ;
2018-08-10 08:22:43 -05:00
RiaLogging : : warning ( " Using fall-back calculation of well path geometry between active target number: " + QString : : number ( tIdx + 1 ) + " and " + QString : : number ( tIdx + 2 ) ) ;
2018-08-07 06:15:41 -05:00
}
2018-08-14 04:25:08 -05:00
2018-08-29 09:03:31 -05:00
endPoints . push_back ( sCurveCalc . firstArcEndpoint ( ) + m_referencePointXyz ( ) ) ;
endPoints . push_back ( sCurveCalc . secondArcStartpoint ( ) + m_referencePointXyz ( ) ) ;
endPoints . push_back ( target2 - > targetPointXYZ ( ) + m_referencePointXyz ( ) ) ;
2018-08-14 04:25:08 -05:00
}
else if ( target1 - > targetType ( ) = = RimWellPathTarget : : POINT
& & target2 - > targetType ( ) = = RimWellPathTarget : : POINT_AND_TANGENT )
{
RiaSCurveCalculator sCurveCalc ( target1 - > targetPointXYZ ( ) ,
prevSegmentEndAzi ,
prevSegmentEndInc ,
target1 - > radius2 ( ) ,
target2 - > targetPointXYZ ( ) ,
target2 - > azimuth ( ) ,
target2 - > inclination ( ) ,
target2 - > radius1 ( ) ) ;
if ( ! sCurveCalc . isOk ( ) )
{
double p1p2Length = ( target2 - > targetPointXYZ ( ) - target1 - > targetPointXYZ ( ) ) . length ( ) ;
sCurveCalc = RiaSCurveCalculator : : fromTangentsAndLength ( target1 - > targetPointXYZ ( ) ,
prevSegmentEndAzi ,
prevSegmentEndInc ,
0.2 * p1p2Length ,
target2 - > targetPointXYZ ( ) ,
target2 - > azimuth ( ) ,
target2 - > inclination ( ) ,
0.2 * p1p2Length ) ;
RiaLogging : : warning ( " Using fall-back calculation of well path geometry between active target number: " + QString : : number ( tIdx + 1 ) + " and " + QString : : number ( tIdx + 2 ) ) ;
}
2018-08-29 09:03:31 -05:00
endPoints . push_back ( sCurveCalc . firstArcEndpoint ( ) + m_referencePointXyz ( ) ) ;
endPoints . push_back ( sCurveCalc . secondArcStartpoint ( ) + m_referencePointXyz ( ) ) ;
endPoints . push_back ( target2 - > targetPointXYZ ( ) + m_referencePointXyz ( ) ) ;
2018-08-14 04:25:08 -05:00
}
else if ( target1 - > targetType ( ) = = RimWellPathTarget : : POINT_AND_TANGENT
& & target2 - > targetType ( ) = = RimWellPathTarget : : POINT )
{
RiaJCurveCalculator jCurve ( target1 - > targetPointXYZ ( ) ,
target1 - > azimuth ( ) ,
target1 - > inclination ( ) ,
target1 - > radius2 ( ) ,
target2 - > targetPointXYZ ( ) ) ;
if ( jCurve . isOk ( ) )
{
2018-08-29 09:03:31 -05:00
endPoints . push_back ( jCurve . firstArcEndpoint ( ) + m_referencePointXyz ( ) ) ;
2018-08-14 04:25:08 -05:00
}
2018-08-29 09:03:31 -05:00
endPoints . push_back ( target2 - > targetPointXYZ ( ) + m_referencePointXyz ( ) ) ;
2018-08-14 04:25:08 -05:00
prevSegmentEndAzi = jCurve . endAzimuth ( ) ;
prevSegmentEndInc = jCurve . endInclination ( ) ;
2018-09-10 09:32:24 -05:00
target2 - > setDerivedTangent ( prevSegmentEndAzi , prevSegmentEndInc ) ;
2018-07-04 09:49:04 -05:00
}
2018-08-14 04:25:08 -05:00
else if ( target1 - > targetType ( ) = = RimWellPathTarget : : POINT
& & target2 - > targetType ( ) = = RimWellPathTarget : : POINT )
{
RiaJCurveCalculator jCurve ( target1 - > targetPointXYZ ( ) ,
prevSegmentEndAzi ,
prevSegmentEndInc ,
target1 - > radius2 ( ) ,
target2 - > targetPointXYZ ( ) ) ;
if ( jCurve . isOk ( ) )
{
2018-08-29 09:03:31 -05:00
endPoints . push_back ( jCurve . firstArcEndpoint ( ) + m_referencePointXyz ( ) ) ;
2018-08-14 04:25:08 -05:00
}
2018-08-29 09:03:31 -05:00
endPoints . push_back ( target2 - > targetPointXYZ ( ) + m_referencePointXyz ( ) ) ;
2018-08-14 04:25:08 -05:00
prevSegmentEndAzi = jCurve . endAzimuth ( ) ;
prevSegmentEndInc = jCurve . endInclination ( ) ;
2018-09-10 09:32:24 -05:00
target2 - > setDerivedTangent ( prevSegmentEndAzi , prevSegmentEndInc ) ;
2018-08-14 04:25:08 -05:00
}
else
{
CVF_ASSERT ( false ) ;
}
2018-07-03 01:42:31 -05:00
}
return endPoints ;
}
2018-07-04 09:49:04 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-07-31 01:44:37 -05:00
cvf : : Vec3d RimWellPathGeometryDef : : startTangent ( ) const
2018-07-04 09:49:04 -05:00
{
2018-07-31 01:44:37 -05:00
std : : vector < RimWellPathTarget * > wellTargets = activeWellTargets ( ) ;
if ( ! wellTargets . empty ( ) & & wellTargets [ 0 ] - > targetType ( ) = = RimWellPathTarget : : POINT_AND_TANGENT )
2018-07-04 09:49:04 -05:00
{
2018-07-31 01:44:37 -05:00
return wellTargets [ 0 ] - > tangent ( ) ;
2018-07-04 09:49:04 -05:00
}
else
{
2018-07-31 01:44:37 -05:00
return { 0 , 0 , - 1 } ;
2018-07-04 09:49:04 -05:00
}
}
2018-07-03 01:42:31 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : defineCustomContextMenu ( const caf : : PdmFieldHandle * fieldNeedingMenu ,
QMenu * menu ,
QWidget * fieldEditorWidget )
{
caf : : CmdFeatureMenuBuilder menuBuilder ;
menuBuilder < < " RicNewWellPathListTargetFeature " ;
menuBuilder < < " Separator " ;
menuBuilder < < " RicDeleteWellPathTargetFeature " ;
menuBuilder . appendToMenu ( menu ) ;
}
2018-08-29 09:03:31 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathGeometryDef : : defineEditorAttribute ( const caf : : PdmFieldHandle * field , QString uiConfigName , caf : : PdmUiEditorAttribute * attribute )
{
if ( field = = & m_pickPointsEnabled )
{
caf : : PdmUiPushButtonEditorAttribute * pbAttribute = dynamic_cast < caf : : PdmUiPushButtonEditorAttribute * > ( attribute ) ;
if ( pbAttribute )
{
if ( ! m_pickPointsEnabled )
{
pbAttribute - > m_buttonText = " Start Picking Targets " ;
}
else
{
pbAttribute - > m_buttonText = " Stop Picking Targets " ;
}
}
}
if ( field = = & m_wellTargets )
{
auto tvAttribute = dynamic_cast < caf : : PdmUiTableViewEditorAttribute * > ( attribute ) ;
if ( tvAttribute & & m_pickPointsEnabled )
{
tvAttribute - > baseColor . setRgb ( 255 , 220 , 255 ) ;
2018-08-29 10:08:03 -05:00
tvAttribute - > forceColumnWidthResize = true ;
2018-08-29 09:03:31 -05:00
}
}
}