Merge "Remove obsolete and non functional SizeChanged signal from actor" into tizen
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / table-view / table-view-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/table-view/table-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <sstream>
23 #include <dali/public-api/object/ref-object.h>
24 #include <dali/public-api/object/type-registry.h>
25 #include <dali/public-api/scripting/scripting.h>
26 #include <dali/integration-api/debug.h>
27
28 using namespace Dali;
29
30 namespace
31 {
32 const float DEFAULT_CONSTRAINT_DURATION = 0.0f;
33
34 /**
35  * sets a child property relative to parents size and applies a unit based padding before the relative calculation.
36  * @param[in] scale of parent minus padding between 0 and 1
37  * @param[in] padding in world coordinate units
38  * @param[in] fixed part in world coordinate units
39  * @param[in] size of the parent
40  * @return The relative size with padding.
41  */
42 Vector2 RelativeToSize( const Vector2& scale, const Vector2& padding, const Vector2& fixed, const Vector2& parentSize)
43 {
44   return fixed + ( parentSize - padding ) * scale;
45 }
46
47 #if defined(DEBUG_ENABLED)
48 // debugging support, very useful when new features are added or bugs are hunted down
49 // currently not called from code so compiler will optimize these away, kept here for future debugging
50
51 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
52 #define TV_LOG(fmt, args...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
53
54 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
55 {
56   TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
57   // print values
58   for( unsigned int i = 0; i < array.GetRows(); ++i )
59   {
60     for( unsigned int j = 0; j < array.GetColumns(); ++j )
61     {
62       Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
63       char actor = ' ';
64       if( data.actor )
65       {
66         actor = 'A';
67       }
68       TV_LOG("Array[%d,%d]=%c %d,%d,%d,%d  ", i, j, actor,
69           data.position.rowIndex, data.position.columnIndex,
70           data.position.rowSpan, data.position.columnSpan );
71     }
72     TV_LOG( "\n" );
73   }
74 }
75
76 // debugging support, very useful when new features are added or bugs are hunted down
77 // currently not called from code so compiler will optimize these away, kept here for future debugging
78 void PrintArray( Array2d<Size>& array )
79 {
80   TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
81   // print values
82   for( unsigned int i = 0; i < array.GetRows(); ++i )
83   {
84     for( unsigned int j = 0; j < array.GetColumns(); ++j )
85     {
86       TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
87     }
88     TV_LOG( "\n" );
89   }
90 }
91 // debugging support, very useful when new features are added or bugs are hunted down
92 // currently not called from code so compiler will optimize these away, kept here for future debugging
93 void PrintVector( std::vector<float>& array )
94 {
95   TV_LOG( "vector, size [%d]\n", array.size() );
96   // print values
97   for( unsigned int i = 0; i < array.size(); ++i )
98   {
99     TV_LOG( "vector[%d]=%.2f ", i, array[i] );
100   }
101   TV_LOG( "\n" );
102 }
103 #endif // defined(DEBUG_ENABLED)
104
105 } // namespace
106
107 namespace Dali
108 {
109
110 namespace Toolkit
111 {
112
113 const Property::Index TableView::PROPERTY_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX );
114 const Property::Index TableView::PROPERTY_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 1 );
115 const Property::Index TableView::PROPERTY_CELL_PADDING( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 2 );
116 const Property::Index TableView::PROPERTY_LAYOUT_ROWS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 3 );
117 const Property::Index TableView::PROPERTY_LAYOUT_COLUMNS( Internal::TableView::TABLEVIEW_PROPERTY_START_INDEX + 4 );
118
119 namespace Internal
120 {
121
122 namespace
123 {
124
125 const Scripting::StringEnum< Toolkit::TableView::LayoutPolicy > LAYOUT_POLICY_STRING_TABLE[] =
126 {
127  { "fixed",    Toolkit::TableView::Fixed    },
128  { "relative", Toolkit::TableView::Relative },
129  { "fill",     Toolkit::TableView::Fill }
130 };
131
132 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
133
134 // Type registration
135 BaseHandle Create()
136 {
137   return Toolkit::TableView::New(0, 0);
138 }
139 TypeRegistration mType( typeid(Toolkit::TableView), typeid(Toolkit::Control), Create );
140
141 PropertyRegistration property1( mType, "rows", Toolkit::TableView::PROPERTY_ROWS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
142 PropertyRegistration property2( mType, "columns", Toolkit::TableView::PROPERTY_COLUMNS, Property::UNSIGNED_INTEGER, &TableView::SetProperty, &TableView::GetProperty );
143 PropertyRegistration property3( mType, "cell-padding", Toolkit::TableView::PROPERTY_CELL_PADDING, Property::VECTOR2, &TableView::SetProperty, &TableView::GetProperty );
144 PropertyRegistration property4( mType, "layout-rows", Toolkit::TableView::PROPERTY_LAYOUT_ROWS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
145 PropertyRegistration property5( mType, "layout-columns", Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS, Property::MAP, &TableView::SetProperty, &TableView::GetProperty );
146
147 } // namespace
148
149 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
150 {
151   // Create the implementation, temporarily owned by this handle on stack
152   IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
153
154   // Pass ownership to CustomActor handle
155   Toolkit::TableView handle( *impl );
156
157   // Second-phase init of the implementation
158   // This can only be done after the CustomActor connection has been made...
159   impl->Initialize();
160
161   return handle;
162 }
163
164 bool TableView::AddChild( Actor child, Toolkit::TableView::CellPosition position )
165 {
166   // check that the child is valid
167   DALI_ASSERT_ALWAYS( child );
168
169   // if child is already parented, we adopt it
170   if( child.GetParent() )
171   {
172     child.GetParent().Remove( child );
173   }
174   // check if we need to expand our data array
175   if( position.rowIndex >= mCellData.GetRows() )
176   {
177     // only adding new rows
178     ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
179   }
180   if( position.columnIndex >= mCellData.GetColumns() )
181   {
182     // only adding new columns
183     ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
184   }
185   // check if there already is something in this cell
186   if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
187   {
188     return false; // cannot share a cell, it would complicate all logic and not bring much benefit
189   }
190   RelayoutingLock lock( *this );
191   // adopt the child
192   Self().Add( child );
193
194   // put the actor to the main cell
195   CellData data;
196   data.actor = child;
197   data.position = position;
198   mCellData[ position.rowIndex ][ position.columnIndex ] = data;
199   // if child spans multiple rows of columns
200   bool spanned = false;
201   if( position.rowSpan > 1 )
202   {
203     // span might go outside table
204     if( position.rowIndex + position.rowSpan > mCellData.GetRows() )
205     {
206       // increase table size for the full span, only increasing rows
207       ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
208     }
209     spanned = true;
210   }
211   if( position.columnSpan > 1 )
212   {
213     // span might go outside table
214     if( position.columnIndex + position.columnSpan > mCellData.GetColumns() )
215     {
216       // increase table size for the full span, only increasing columns
217       ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
218     }
219     spanned = true;
220   }
221   // if it spanned multiple rows, put the cellinfo in all of those
222   if( spanned )
223   {
224     for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
225     {
226       // store same information to all cells, this way we can identify
227       // if a cell is the prime location of an actor or a spanned one
228       for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
229       {
230         // store same information to all cells, this way we can identify
231         // if a cell is the prime location of an actor or a spanned one
232         mCellData[ row ][ column ] = data;
233       }
234     }
235   }
236   // relayout the whole table
237   RelayoutRequest();
238   return true; // addition successful
239 }
240
241 Actor TableView::GetChildAt( Toolkit::TableView::CellPosition position )
242 {
243   // check if we have this row and column in the table
244   if( ( position.columnIndex >= mCellData.GetColumns() )||
245       ( position.rowIndex >= mCellData.GetRows() ) )
246   {
247     // return an empty handle
248     return Actor();
249   }
250   // return the child handle
251   return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
252 }
253
254 Actor TableView::RemoveChildAt( Toolkit::TableView::CellPosition position )
255 {
256   // get the child handle
257   Actor child = GetChildAt( position );
258   // if no real actor there, nothing else to be done
259   if( child )
260   {
261     RelayoutingLock lock( *this );
262     // Remove the child, this will trigger a call to OnControlChildRemove
263     Self().Remove( child );
264
265     // relayout the table only if instances were found
266     if( RemoveAllInstances( child ) )
267     {
268       RelayoutRequest();
269     }
270   }
271   // return the child back to caller
272   return child;
273 }
274
275 bool TableView::FindChildPosition( Actor child, Toolkit::TableView::CellPosition& position )
276 {
277   // only find valid child actors
278   if( child )
279   {
280     // walk through the layout data
281     const unsigned int rowCount = mCellData.GetRows();
282     const unsigned int columnCount = mCellData.GetColumns();
283     for( unsigned int row = 0; row < rowCount; ++row )
284     {
285       for( unsigned int column = 0; column < columnCount; ++column )
286       {
287         if( mCellData[ row ][ column ].actor == child )
288         {
289           position = mCellData[ row ][ column ].position;
290           return true;
291         }
292       }
293     }
294   }
295   return false;
296 }
297
298 void TableView::InsertRow( unsigned int rowIndex )
299 {
300   RelayoutingLock lock( *this );
301   mCellData.InsertRow( rowIndex );
302   // need to update the cellinfos for the items that moved
303   const unsigned int rowCount = mCellData.GetRows();
304   const unsigned int columnCount = mCellData.GetColumns();
305   for( unsigned int row = 0; row < rowCount; ++row )
306   {
307     for( unsigned int column = 0; column < columnCount; ++column )
308     {
309       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
310       // if cell is spanning and above and spans to inserted row
311       if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
312           ( position.rowIndex + position.rowSpan > rowIndex ) )
313       {
314         // increase span by one
315         position.rowSpan++;
316         // copy cell to occupy the new column
317         mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
318       }
319       // if below of inserted row, increase row index
320       else if( row > rowIndex )
321       {
322         // increase index by one
323         position.rowIndex++;
324       }
325     }
326   }
327   mRelativeSizes.InsertRow( rowIndex );
328   // inserting a row requires adjusting the height vectors
329   mFixedHeights.insert( mFixedHeights.begin() + rowIndex, 0 );
330   mRelativeHeights.insert( mRelativeHeights.begin() + rowIndex, 0 );
331   RelayoutRequest();
332 }
333
334 void TableView::DeleteRow( unsigned int rowIndex )
335 {
336   std::vector< Actor > ignored;
337   DeleteRow( rowIndex, ignored );
338 }
339
340 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
341 {
342   RelayoutingLock lock( *this );
343   std::vector< CellData > lost;
344   mCellData.DeleteRow( rowIndex, lost );
345   // need to update the cellinfos for the items that moved
346   const unsigned int rowCount = mCellData.GetRows();
347   const unsigned int columnCount = mCellData.GetColumns();
348   for( unsigned int row = 0; row < rowCount; ++row )
349   {
350     for( unsigned int column = 0; column < columnCount; ++column )
351     {
352       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
353       // if cell is spanning and above and spans to deleted row
354       if( ( position.rowSpan > 1 )&&( position.rowIndex <= rowIndex )&&
355           ( position.rowIndex + position.rowSpan > rowIndex ) )
356       {
357         // decrease span by one
358         if( position.rowSpan > 1 )
359         {
360           position.rowSpan--;
361         }
362       }
363       // if below of or at the inserted row, decrease row index
364       else if( row >= rowIndex )
365       {
366         // decrease index by one
367         if( position.rowIndex > 1 )
368         {
369           position.rowIndex--;
370         }
371       }
372     }
373   }
374   // 1 row removed, 0 columns
375   RemoveAndGetLostActors( lost, removed, 1u, 0u );
376   // resize the data structures
377   mRelativeSizes.DeleteRow( rowIndex );
378   // deleting a row requires adjusting the height vectors
379   mFixedHeights.erase( mFixedHeights.begin() + rowIndex );
380   mRelativeHeights.erase( mRelativeHeights.begin() + rowIndex );
381   RelayoutRequest();
382 }
383
384 void TableView::InsertColumn( unsigned int columnIndex )
385 {
386   RelayoutingLock lock( *this );
387   mCellData.InsertColumn( columnIndex );
388   // need to update the cellinfos for the items that moved
389   const unsigned int rowCount = mCellData.GetRows();
390   const unsigned int columnCount = mCellData.GetColumns();
391   for( unsigned int row = 0; row < rowCount; ++row )
392   {
393     for( unsigned int column = 0; column < columnCount; ++column )
394     {
395       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
396       // if cell is spanning and left side and spans to inserted column
397       if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
398           ( position.columnIndex + position.columnSpan > columnIndex ) )
399       {
400         // increase span by one
401         position.columnSpan++;
402         // copy cell to occupy the new column
403         mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
404       }
405       // if on the right side of inserted column, increase column index
406       else if( column > columnIndex )
407       {
408         // increase index by one
409         position.columnIndex++;
410       }
411     }
412   }
413   // relative sizes gets recalculated on Relayout
414   mRelativeSizes.InsertColumn( columnIndex );
415   // inserting a column requires adjusting the width vectors
416   mFixedWidths.insert( mFixedWidths.begin() + columnIndex, 0 );
417   mRelativeWidths.insert( mRelativeWidths.begin() + columnIndex, 0 );
418   RelayoutRequest();
419 }
420
421 void TableView::DeleteColumn( unsigned int columnIndex )
422 {
423   std::vector< Actor > ignored;
424   DeleteColumn( columnIndex, ignored );
425 }
426
427 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
428 {
429   RelayoutingLock lock( *this );
430   std::vector< CellData > lost;
431   mCellData.DeleteColumn( columnIndex, lost );
432   // need to update the cellinfos for the items that moved
433   const unsigned int rowCount = mCellData.GetRows();
434   const unsigned int columnCount = mCellData.GetColumns();
435   for( unsigned int row = 0; row < rowCount; ++row )
436   {
437     for( unsigned int column = 0; column < columnCount; ++column )
438     {
439       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
440       // if cell is spanning and left side and spans to inserted column
441       if( ( position.columnSpan > 1 )&&( position.columnIndex <= columnIndex )&&
442           ( position.columnIndex + position.columnSpan > columnIndex ) )
443       {
444         // decrease span by one
445         if( position.columnSpan > 1 )
446         {
447           position.columnSpan--;
448         }
449       }
450       // if on the right side of or at the inserted column, decrease column index
451       else if( column >= columnIndex )
452       {
453         // decrease index by one
454         if( position.columnIndex > 0 )
455         {
456           position.columnIndex--;
457         }
458       }
459     }
460   }
461   // 0 rows, 1 column removed
462   RemoveAndGetLostActors( lost, removed, 0u, 1u );
463   // resize the data structures
464   mRelativeSizes.DeleteColumn( columnIndex );
465   // deleting a column requires adjusting the width vectors
466   mFixedWidths.erase( mFixedWidths.begin() + columnIndex );
467   mRelativeWidths.erase( mRelativeWidths.begin() + columnIndex );
468   // relayout
469   RelayoutRequest();
470 }
471
472 void TableView::Resize( unsigned int rows, unsigned int columns )
473 {
474   std::vector< Actor > ignored;
475   Resize( rows, columns, ignored );
476 }
477
478 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
479 {
480   RelayoutingLock lock( *this );
481   unsigned int oldRows = GetRows();
482   unsigned int oldColumns = GetColumns();
483   // resize data array
484   std::vector< CellData > lost;
485   ResizeContainers( rows, columns, lost );
486   // calculate if we lost rows or columns
487   unsigned int rowsRemoved = 0;
488   unsigned int newRows = GetRows();
489   if( oldRows < newRows )
490   {
491     rowsRemoved = newRows - oldRows;
492   }
493   unsigned int columnsRemoved = 0;
494   unsigned int newColumns = GetColumns();
495   if( oldColumns < newColumns )
496   {
497     rowsRemoved = newColumns - oldColumns;
498   }
499   RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
500   // finally relayout once all actors are removed
501   RelayoutRequest();
502 }
503
504 void TableView::SetCellPadding( Size padding )
505 {
506   // if padding really changed
507   if( padding != mPadding )
508   {
509     mPadding = padding;
510     // do a relayout
511     RelayoutRequest();
512   }
513 }
514
515 Size TableView::GetCellPadding()
516 {
517   return mPadding;
518 }
519
520 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
521 {
522   DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
523   // add the fixed height to the array of fixed heights
524   mFixedHeights[ rowIndex ] = height;
525   // remove the relative height of the same row
526   mRelativeHeights[ rowIndex ] = 0.f;
527   // relayout all cells, no lock needed as nothing added or removed
528   RelayoutRequest();
529 }
530
531 float TableView::GetFixedHeight( unsigned int rowIndex ) const
532 {
533   DALI_ASSERT_ALWAYS( rowIndex < mFixedHeights.size() );
534
535   return mFixedHeights[ rowIndex ];
536 }
537
538 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
539 {
540   DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
541   // add the relative height to the array of relative heights
542   mRelativeHeights[ rowIndex ] = heightPercentage;
543   // remove the fixed height of the same row
544   mFixedHeights[ rowIndex ] = 0.f;
545   // relayout all cells, no lock needed as nothing added or removed
546   RelayoutRequest();
547 }
548
549 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
550 {
551   DALI_ASSERT_ALWAYS( rowIndex < mRelativeHeights.size() );
552
553   return mRelativeHeights[ rowIndex ];
554 }
555
556 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
557 {
558   DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
559   // add the fixed width to the array of fixed column widths
560   mFixedWidths[ columnIndex ] = width;
561   // remove the relative width of the same column
562   mRelativeWidths[ columnIndex ] = 0.f;
563   // relayout all cells, no lock needed as nothing added or removed
564   RelayoutRequest();
565 }
566
567 float TableView::GetFixedWidth( unsigned int columnIndex ) const
568 {
569   DALI_ASSERT_ALWAYS( columnIndex < mFixedWidths.size() );
570
571   return mFixedWidths[ columnIndex ];
572 }
573
574 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
575 {
576   DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
577   // add the relative widths to the array of relative widths
578   mRelativeWidths[ columnIndex ] = widthPercentage;
579   // remove the fixed width of the same column
580   mFixedWidths[ columnIndex ] = 0.f;
581   // relayout all cells, no lock needed as nothing added or removed
582   RelayoutRequest();
583 }
584
585 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
586 {
587   DALI_ASSERT_ALWAYS( columnIndex < mRelativeWidths.size() );
588
589   return mRelativeWidths[ columnIndex ];
590 }
591
592 void TableView::OnRelayout( const Vector2& size, ActorSizeContainer& container )
593 {
594   float fixedHeightsTotal = 0.0f;
595   float fixedWidthsTotal = 0.0f;
596
597   // 1. update the relative sizes and calculate total fixed height and width
598   UpdateRelativeSizes( fixedHeightsTotal, fixedWidthsTotal );
599
600   // 2. go through the layout data and create constraints
601   float cumulatedFixedHeight = 0.0f;
602   float cumulatedRelativeHeight = 0.0f;
603
604   // iterate the table
605   const unsigned int rowCount = mCellData.GetRows();
606   const unsigned int columnCount = mCellData.GetColumns();
607   // float versions of the count + 1 to keep precision
608   const float maxRowPlusOne( rowCount + 1 );
609   const float maxColumnPlusOne( columnCount + 1 );
610   for( unsigned int row = 0; row < rowCount; ++row )
611   {
612     // reset widths at the start of each row
613     float cumulatedFixedWidth = 0.0f;
614     float cumulatedRelativeWidth = 0.0f;
615     for( unsigned int column = 0; column < columnCount; ++column )
616     {
617       // check if this cell has an actor
618       Actor actor = mCellData[ row ][ column ].actor;
619       const Toolkit::TableView::CellPosition position = mCellData[ row ][ column ].position;
620       // if there is an actor and this is the main cell of the actor
621       // an actor can be in multiple cells if its row or columnspan is more than 1
622       // we however must only lay out each actor only once
623       if( ( actor )&&( position.rowIndex == row )&&( position.columnIndex == column ) )
624       {
625         // anchor actor correctly
626         actor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
627         actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
628         // remove old constraints
629         actor.RemoveConstraints();
630
631         // 1. set position
632         // get the row and column indices
633         float rowPos( position.rowIndex );
634         float colPos( position.columnIndex );
635         // constrain the actor position to be relative to the width and height of table
636         // minus the padding of course (padding is all around cells)
637         Vector2 relativePosition( cumulatedRelativeWidth, cumulatedRelativeHeight );
638         // fixed height rows and fixed width cells are considered as padding so
639         // they are removed from the total size for relative
640         // for position only consider cumulated fixed rows and columns from top and left
641         Vector2 positionPadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
642                                  maxRowPlusOne * mPadding.height + fixedHeightsTotal );
643         Vector2 fixedPosition( ( colPos + 1.0f ) * mPadding.width + cumulatedFixedWidth,
644                                  ( rowPos + 1.0f ) * mPadding.height + cumulatedFixedHeight );
645
646         Vector3 actorPosition( RelativeToSize( relativePosition, positionPadding, fixedPosition, size ) );
647         actor.SetPosition( actorPosition );
648
649         // 2. set size
650         // constrain the actor size to be relative to the size of table
651         // get the relative size for this cell
652         Vector2 relativeSize( mRelativeSizes[ row ][ column ] );
653         Vector2 fixedSize( mFixedWidths[ column ], mFixedHeights[ row ] );
654         // if we span multiple cells, need to sum them all up, both fixed and relative parts
655         if( position.rowSpan > 1 )
656         {
657           for( unsigned int i = 1; i < position.rowSpan; ++i )
658           {
659             // accumulate the height only
660             relativeSize.height += mRelativeSizes[ row + i ][ column ].height;
661             fixedSize.height += mFixedHeights[ row + i ];
662           }
663         }
664         if( position.columnSpan > 1 )
665         {
666           for( unsigned int i = 1; i < position.columnSpan; ++i )
667           {
668             // accumulate the width only
669             relativeSize.width += mRelativeSizes[ row ][ column + i ].width;
670             fixedSize.width += mFixedWidths[ column + i ];
671           }
672         }
673         // minus the padding from size (padding is all around cells)
674         // if item spans multiple columns or rows then less padding is added (default span is 1)
675         // fixed height rows and fixed width cells are considered as padding so they are removed
676         // from the total available size for relative cells
677         Vector2 sizePadding( maxColumnPlusOne * mPadding.width + fixedWidthsTotal,
678                              maxRowPlusOne * mPadding.height + fixedHeightsTotal );
679         // and added to the fixed size multiplied by the span of rows and columns
680         fixedSize.width += ( position.columnSpan - 1.0f ) * mPadding.width;
681         fixedSize.height += ( position.rowSpan - 1.0f ) * mPadding.height;
682
683         Vector2 actorSize( RelativeToSize( relativeSize, sizePadding, fixedSize, size ) );
684         actor.SetSize(actorSize.x, actorSize.y);
685
686         // Relayout Children
687         Relayout ( actor, actorSize, container );
688       }
689       // for position we need to keep track of current fixed width and relative width
690       // increase for next column
691       cumulatedFixedWidth += mFixedWidths[ column ];
692       cumulatedRelativeWidth += mRelativeSizes[ row ][ column ].width;
693     }
694     // for position we need to keep track of current fixed height and relative height
695     // increase for next row
696     cumulatedFixedHeight += mFixedHeights[ row ];
697     cumulatedRelativeHeight += mRelativeSizes[ row ][ 0 ].height; // all columns share same height
698   }
699 }
700
701 unsigned int TableView::GetRows()
702 {
703   return mCellData.GetRows();
704 }
705
706 unsigned int TableView::GetColumns()
707 {
708   return mCellData.GetColumns();
709 }
710
711 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
712 {
713   Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
714
715   if( tableView )
716   {
717     TableView& tableViewImpl( GetImpl( tableView ) );
718     switch( index )
719     {
720       case Toolkit::TableView::PROPERTY_ROWS:
721       {
722         if( value.Get<unsigned int>() != tableViewImpl.GetRows() )
723         {
724           tableViewImpl.Resize( value.Get<unsigned int>(), tableViewImpl.GetColumns() );
725         }
726         break;
727       }
728       case Toolkit::TableView::PROPERTY_COLUMNS:
729       {
730         if( value.Get<unsigned int>() != tableViewImpl.GetColumns() )
731         {
732           tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<unsigned int>() );
733         }
734         break;
735       }
736       case Toolkit::TableView::PROPERTY_CELL_PADDING:
737       {
738         tableViewImpl.SetCellPadding( value.Get<Vector2>() );
739         break;
740       }
741       case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
742       {
743         SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, value );
744         break;
745       }
746       case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
747       {
748         SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, value );
749         break;
750       }
751     }
752   }
753 }
754
755 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
756 {
757   Property::Value value;
758
759   Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
760
761   if( tableView )
762   {
763     TableView& tableViewImpl( GetImpl( tableView ) );
764     switch( index )
765     {
766       case Toolkit::TableView::PROPERTY_ROWS:
767       {
768         value = tableViewImpl.GetRows();
769         break;
770       }
771       case Toolkit::TableView::PROPERTY_COLUMNS:
772       {
773         value = tableViewImpl.GetColumns();
774         break;
775       }
776       case Toolkit::TableView::PROPERTY_CELL_PADDING:
777       {
778         value = tableViewImpl.GetCellPadding();
779         break;
780       }
781       case Toolkit::TableView::PROPERTY_LAYOUT_ROWS:
782       {
783         value = tableViewImpl.GetRowHeightsPropertyValue();
784         break;
785       }
786       case Toolkit::TableView::PROPERTY_LAYOUT_COLUMNS:
787       {
788         value = tableViewImpl.GetColumnWidthsPropertyValue();
789         break;
790       }
791     }
792   }
793
794   return value;
795 }
796
797 void TableView::OnControlChildAdd( Actor& child )
798 {
799   if( mLayoutingChild )
800   {
801     // we're in the middle of laying out children so no point doing anything here
802     return;
803   }
804
805   Toolkit::TableView::CellPosition cellPosition;
806   if( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
807   {
808     cellPosition.rowSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::ROW_SPAN_PROPERTY_NAME) ).Get<float>() );
809   }
810   if( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) != Property::INVALID_INDEX )
811   {
812     cellPosition.columnSpan = static_cast<unsigned int>( child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::COLUMN_SPAN_PROPERTY_NAME) ).Get<float>() );
813   }
814   if( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) != Property::INVALID_INDEX )
815   {
816     Vector2 indices = child.GetProperty( child.GetPropertyIndex(Toolkit::TableView::CELL_INDICES_PROPERTY_NAME) ).Get<Vector2 >();
817     cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
818     cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
819
820     AddChild( child, cellPosition );
821     // donot continue
822     return;
823   }
824
825   // check if we're already laying out this child somewhere on the table
826   // walk through the layout data
827   const unsigned int rowCount = mCellData.GetRows();
828   const unsigned int columnCount = mCellData.GetColumns();
829   // child not yet laid out, find the first free slot
830   for( unsigned int row = 0; row < rowCount; ++row )
831   {
832     for( unsigned int column = 0; column < columnCount; ++column )
833     {
834       // no actor means free cell
835       if( !(mCellData[ row ][ column ].actor) )
836       {
837         // put the actor in the cell
838         CellData data;
839         data.actor = child;
840         data.position.columnIndex = column;
841         data.position.rowIndex = row;
842         mCellData[ row ][ column ] = data;
843         RelayoutRequest();
844         // don' continue
845         return;
846       }
847     }
848   }
849   // still here, no room for the poor child so increase the array. Need a new row
850   ResizeContainers( rowCount + 1, columnCount );
851   // put the actor to the first cell of the new row
852   CellData data;
853   data.actor = child;
854   data.position.rowIndex = rowCount;
855   data.position.columnIndex = 0;
856   mCellData[ rowCount ][ 0 ] = data;
857   // finally relayout the table
858   RelayoutRequest();
859 }
860
861 void TableView::OnControlChildRemove( Actor& child )
862 {
863   // dont process if we're in the middle of bigger operation like delete row, column or resize
864   if( !mLayoutingChild )
865   {
866     // relayout the table only if instances were found
867     if( RemoveAllInstances( child ) )
868     {
869       RelayoutRequest();
870     }
871   }
872 }
873
874 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
875 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
876   mCellData( initialRows, initialColumns ),
877   mLayoutingChild( false )
878 {
879   SetKeyboardNavigationSupport( true );
880   ResizeContainers( initialRows, initialColumns );
881 }
882
883 void TableView::OnInitialize()
884 {
885   // Make self as keyboard focusable and focus group
886   Actor self = Self();
887   self.SetKeyboardFocusable(true);
888   SetAsKeyboardFocusGroup(true);
889 }
890
891 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
892 {
893   std::vector<CellData> ignored;
894   ResizeContainers( rows, columns, ignored );
895 }
896
897 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
898 {
899   mCellData.Resize( rows, columns, removed );
900   // we dont care if these go smaller, data will be regenerated or is not needed anymore
901   mRelativeSizes.Resize( rows, columns );
902   mFixedHeights.resize( rows );
903   mRelativeHeights.resize( rows );
904   mFixedWidths.resize( columns );
905   mRelativeWidths.resize( columns );
906 }
907
908 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
909                                         unsigned int rowsRemoved, unsigned int columnsRemoved )
910 {
911   // iterate through all lost cells
912   std::vector< CellData >::const_iterator iter = lost.begin();
913   for( ; iter != lost.end(); ++iter )
914   {
915     // if it is a valid actor
916     if( (*iter).actor )
917     {
918       // is this actor still somewhere else in the table
919       Toolkit::TableView::CellPosition position;
920       if( FindChildPosition( (*iter).actor, position ) )
921       {
922         // it must be spanning multiple cells, position contains the top left most one
923         // check if position is left of the removed location
924         if( position.columnIndex < (*iter).position.columnIndex )
925         {
926           // if column span is greater than 1
927           if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
928           {
929             // decrease column span
930             mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
931           }
932         }
933         // check if position is left of the removed location
934         if( position.rowIndex < (*iter).position.rowIndex )
935         {
936           // if row span is greater than 1
937           if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
938           {
939             // decrease row span
940             mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
941           }
942         }
943       }
944       else
945       {
946         // this actor is gone for good
947         // add actor to removed container
948         removed.push_back( (*iter).actor );
949         // we dont want the child actor anymore
950         Self().Remove( (*iter).actor );
951       }
952     }
953   }
954 }
955
956 bool TableView::RemoveAllInstances( Actor child )
957 {
958   bool found = false;
959   // walk through the layout data
960   const unsigned int rowCount = mCellData.GetRows();
961   const unsigned int columnCount = mCellData.GetColumns();
962   for( unsigned int row = 0; row < rowCount; ++row )
963   {
964     for( unsigned int column = 0; column < columnCount; ++column )
965     {
966       if( mCellData[ row ][ column ].actor == child )
967       {
968         // clear the cell, NOTE that the cell might be spanning multiple cells
969         mCellData[ row ][ column ] = CellData();
970         found = true;
971       }
972     }
973   }
974   return found;
975 }
976
977 void TableView::UpdateRelativeSizes( float& fixedHeightsTotal, float& fixedWidthsTotal )
978 {
979   // 1. check all the fixed heights and widths to know how much size they take in total
980   // as well as the relative heights and widths to know how much is left for the 'fill' cells
981   unsigned int fixedRowCount = 0;
982   unsigned int relativeRowCount = 0;
983   float relativeHeightsTotal = 0.0f;
984   const unsigned int rowCount = mCellData.GetRows();
985   for( unsigned int row = 0; row < rowCount; ++row )
986   {
987     if( mFixedHeights[ row ] > 0.0f )
988     {
989       ++fixedRowCount;
990       fixedHeightsTotal += mFixedHeights[ row ];
991     }
992     if( mRelativeHeights[ row ] > 0.0f )
993     {
994       ++relativeRowCount;
995       relativeHeightsTotal += mRelativeHeights[ row ];
996     }
997   }
998   unsigned int fixedColumnCount = 0;
999   unsigned int relativeColumnCount = 0;
1000   const unsigned int columnCount = mCellData.GetColumns();
1001   float relativeWidthsTotal = 0.0f;
1002   for( unsigned int column = 0; column < columnCount; ++column )
1003   {
1004     if( mFixedWidths[ column ] > 0.0f )
1005     {
1006       ++fixedColumnCount;
1007       fixedWidthsTotal += mFixedWidths[ column ];
1008     }
1009     if( mRelativeWidths[ column ] > 0.0f )
1010     {
1011       ++relativeColumnCount;
1012       relativeWidthsTotal += mRelativeWidths[ column ];
1013     }
1014   }
1015
1016   // 2. cap the relative width and height totals to 100%
1017   if( relativeHeightsTotal > 1.0f )
1018   {
1019     relativeHeightsTotal = 1.0f;
1020   }
1021   if( relativeWidthsTotal > 1.0f )
1022   {
1023     relativeWidthsTotal = 1.0f;
1024   }
1025
1026   // 3. create a table of relative sizes so we can lookup for cells that span multiple rows & colums
1027   const float fillRowCount( rowCount - relativeRowCount - fixedRowCount );
1028   const float fillColumnCount( columnCount - relativeColumnCount - fixedColumnCount );
1029
1030   // walk through the data containers
1031   for( unsigned int row = 0; row < rowCount; ++row )
1032   {
1033     float relativeHeight = 0.0f;
1034     // if we have a fixed height, relative height is 0
1035     if( mFixedHeights[ row ] > 0.0f )
1036     {
1037       relativeHeight = 0.0f;
1038     }
1039     // else if we're given a specific row height %, use that
1040     else if( mRelativeHeights[ row ] > 0.0f )
1041     {
1042       relativeHeight = mRelativeHeights[ row ];
1043     }
1044     // else if there are fill rows
1045     else if( fillRowCount > 0 )
1046     {
1047       // this is a 'fill' row. it gets the remainder of the 100% divided evenly between 'fill' rows
1048       relativeHeight = (1.0f - relativeHeightsTotal ) / fillRowCount;
1049     }
1050     for( unsigned int column = 0; column < columnCount; ++column )
1051     {
1052       float relativeWidth = 0.0f;
1053       // if we have a fixed width, relative width is 0
1054       if( mFixedWidths[ column ] > 0.0f )
1055       {
1056         relativeWidth = 0.0f;
1057       }
1058       // else if we're given a specific column width %, use that
1059       else if( mRelativeWidths[ column ] > 0.0f )
1060       {
1061         relativeWidth = mRelativeWidths[ column ];
1062       }
1063       // else if there are fill columns
1064       else if( fillColumnCount > 0 )
1065       {
1066         // this is a 'fill' column. it gets the remainder of the 100% divided evenly between 'fill' columns
1067         relativeWidth = (1.0f - relativeWidthsTotal ) / fillColumnCount;
1068       }
1069       // store the value
1070       mRelativeSizes[ row ][ column ] = Size( relativeWidth, relativeHeight );
1071     }
1072   }
1073 }
1074
1075 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1076                                          void(TableView::*funcFixed)(unsigned int, float),
1077                                          void(TableView::*funcRelative)(unsigned int, float),
1078                                          const Property::Value& value )
1079 {
1080   if( Property::MAP == value.GetType() )
1081   {
1082     Property::Map map = value.Get<Property::Map>();
1083     unsigned int rowIndex(0);
1084     for ( unsigned int i = 0, count = map.Count(); i < count; ++i )
1085     {
1086       Property::Value& item = map.GetValue(i);
1087
1088       if( std::istringstream(map.GetKey(i)) >> rowIndex  // the key is a number
1089           && Property::MAP == item.GetType())
1090       {
1091         if( item.HasKey( "policy" ) && item.HasKey( "value" ) )
1092         {
1093           Toolkit::TableView::LayoutPolicy policy = Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( item.GetValue("policy").Get<std::string>(), LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT );
1094           if( policy == Toolkit::TableView::Fixed )
1095           {
1096             (tableViewImpl.*funcFixed)( rowIndex, item.GetValue("value").Get<float>() );
1097           }
1098           else if( policy == Toolkit::TableView::Relative )
1099           {
1100             (tableViewImpl.*funcRelative)( rowIndex, item.GetValue("value").Get<float>() );
1101           }
1102         }
1103       }
1104     }
1105   }
1106 }
1107
1108 Property::Value TableView::GetRowHeightsPropertyValue()
1109 {
1110   Property::Map map;
1111   GetMapPropertyValue( mFixedHeights, mRelativeHeights, map);
1112   return Property::Value(map);
1113 }
1114
1115 Property::Value TableView::GetColumnWidthsPropertyValue()
1116 {
1117   Property::Map map;
1118   GetMapPropertyValue( mFixedWidths, mRelativeWidths, map);
1119   return Property::Value(map);
1120 }
1121
1122 void TableView::GetMapPropertyValue( const std::vector<float>& fixedSize, const std::vector<float>& relativeSize, Property::Map& map )
1123 {
1124   std::string fixedPolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Fixed, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1125   std::string relativePolicy( Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::Relative, LAYOUT_POLICY_STRING_TABLE, LAYOUT_POLICY_STRING_TABLE_COUNT ) );
1126
1127   size_t count = fixedSize.size();
1128   for( size_t index = 0; index < count; index++ )
1129   {
1130     if( ! EqualsZero( fixedSize[index] ) )
1131     {
1132       Property::Map item;
1133       item[ "policy" ] = fixedPolicy;
1134       item[ "value" ] = fixedSize[index];
1135
1136       map[ static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str() ] = item;
1137     }
1138     else if( ! EqualsZero( relativeSize[index] ) )
1139     {
1140       Property::Map item;
1141       item[ "policy" ] = relativePolicy;
1142       item[ "value" ] = relativeSize[index];
1143
1144       map[ static_cast<std::ostringstream*>( &(std::ostringstream() << index ) )->str() ] = item;
1145     }
1146   }
1147 }
1148
1149 TableView::~TableView()
1150 {
1151   // nothing to do
1152 }
1153
1154 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1155 {
1156   Actor nextFocusableActor;
1157
1158   if ( !currentFocusedActor )
1159   {
1160     // Nothing is currently focused, so the child in the first cell should be focused.
1161     nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1162   }
1163   else
1164   {
1165     Toolkit::TableView::CellPosition position;
1166     if( FindChildPosition( currentFocusedActor, position ) )
1167     {
1168       // The current focused actor is a child of TableView
1169       bool focusLost = false;
1170       int currentRow = position.rowIndex;
1171       int currentColumn = position.columnIndex;
1172       int numberOfColumns = GetColumns();
1173       int numberOfRows = GetRows();
1174
1175       switch ( direction )
1176       {
1177         case Toolkit::Control::Left:
1178         {
1179           if(--currentColumn < 0)
1180           {
1181             currentColumn = numberOfColumns - 1;
1182             if(--currentRow < 0)
1183             {
1184               currentRow = loopEnabled ? numberOfRows - 1 : 0;
1185               focusLost = (currentRow == 0);
1186             }
1187           }
1188           break;
1189         }
1190         case Toolkit::Control::Right:
1191         {
1192           if(++currentColumn > numberOfColumns - 1)
1193           {
1194             currentColumn = 0;
1195             if(++currentRow > numberOfRows - 1)
1196             {
1197               currentRow = loopEnabled ? 0 : numberOfRows - 1;
1198               focusLost = (currentRow == numberOfRows - 1);
1199             }
1200           }
1201           break;
1202         }
1203         case Toolkit::Control::Up:
1204         {
1205           if(--currentRow < 0)
1206           {
1207             currentRow = loopEnabled ? numberOfRows - 1 : 0;
1208             focusLost = (currentRow == 0);
1209           }
1210           break;
1211         }
1212         case Toolkit::Control::Down:
1213         {
1214           if(++currentRow > numberOfRows - 1)
1215           {
1216             currentRow = loopEnabled ? 0 : numberOfRows - 1;
1217             focusLost = (currentRow == numberOfRows - 1);
1218           }
1219           break;
1220         }
1221       }
1222
1223       // Move the focus if we haven't lost it.
1224       if(!focusLost)
1225       {
1226         nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1227       }
1228     }
1229     else
1230     {
1231       // The current focused actor is not within table view, so the child in the first cell should be focused.
1232       nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1233     }
1234   }
1235
1236   return nextFocusableActor;
1237 }
1238
1239 } // namespace Internal
1240
1241 } // namespace Toolkit
1242
1243 } // namespace Dali