Const correctness improvements for Property::Value.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / table-view / table-view-impl.cpp
1 /*
2  * Copyright (c) 2017 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/object/type-registry-helper.h>
26 #include <dali/devel-api/actors/actor-devel.h>
27 #include <dali/devel-api/scripting/scripting.h>
28 #include <dali/public-api/size-negotiation/relayout-container.h>
29 #include <dali/integration-api/debug.h>
30
31 using namespace Dali;
32
33 namespace
34 {
35 /**
36  * @brief Should the tableview fit around the given actor
37  *
38  * @param[in] actor The child actor to test against
39  * @param[dimension] The dimension to test against
40  */
41 bool FitToChild( Actor actor, Dimension::Type dimension )
42 {
43   return actor.GetResizePolicy( dimension ) != ResizePolicy::FILL_TO_PARENT && actor.GetRelayoutSize( dimension ) > 0.0f;
44 }
45
46 #if defined(DEBUG_ENABLED)
47 // debugging support, very useful when new features are added or bugs are hunted down
48 // currently not called from code so compiler will optimize these away, kept here for future debugging
49
50 #define TABLEVIEW_TAG "DALI Toolkit::TableView "
51 #define TV_LOG(fmt, args,...) Debug::LogMessage(Debug::DebugInfo, TABLEVIEW_TAG fmt, ## args)
52 //#define TABLEVIEW_DEBUG 1
53
54 #if defined(TABLEVIEW_DEBUG)
55 void PrintArray( Array2d<Dali::Toolkit::Internal::TableView::CellData>& array )
56 {
57   TV_LOG( "Array2d<CellData> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
58   // print values
59   for( unsigned int i = 0; i < array.GetRows(); ++i )
60   {
61     for( unsigned int j = 0; j < array.GetColumns(); ++j )
62     {
63       Dali::Toolkit::Internal::TableView::CellData data = array[i][j];
64       char actor = ' ';
65       std::string actorName;
66       if( data.actor )
67       {
68         actor = 'A';
69         actorName = data.actor.GetProperty< std::string >( Dali::Actor::Property::NAME );
70       }
71       TV_LOG("Array[%d,%d]=%c %s %d,%d,%d,%d  ", i, j, actor, actorName.c_str(),
72           data.position.rowIndex, data.position.columnIndex,
73           data.position.rowSpan, data.position.columnSpan );
74     }
75     TV_LOG( "\n" );
76   }
77 }
78
79 // debugging support, very useful when new features are added or bugs are hunted down
80 // currently not called from code so compiler will optimize these away, kept here for future debugging
81 void PrintArray( Array2d<Size>& array )
82 {
83   TV_LOG( "Array2d<Size> size [%d,%d] \n", array.GetRows(), array.GetColumns() );
84   // print values
85   for( unsigned int i = 0; i < array.GetRows(); ++i )
86   {
87     for( unsigned int j = 0; j < array.GetColumns(); ++j )
88     {
89       TV_LOG( "Array[%d,%d]=%.2f,%.2f ", i, j, array[i][j].width, array[i][j].height );
90     }
91     TV_LOG( "\n" );
92   }
93 }
94 // debugging support, very useful when new features are added or bugs are hunted down
95 // currently not called from code so compiler will optimize these away, kept here for future debugging
96 void PrintVector( std::vector<float>& array )
97 {
98   TV_LOG( "vector, size [%d]\n", array.size() );
99   // print values
100   for( unsigned int i = 0; i < array.size(); ++i )
101   {
102     TV_LOG( "vector[%d]=%.2f ", i, array[i] );
103   }
104   TV_LOG( "\n" );
105 }
106 #endif // defined(TABLEVIEW_DEBUG)
107 #endif // defined(DEBUG_ENABLED)
108
109 } // namespace
110
111 namespace Dali
112 {
113
114 namespace Toolkit
115 {
116
117 namespace Internal
118 {
119
120 namespace
121 {
122
123 // Type registration
124 BaseHandle Create()
125 {
126   return Toolkit::TableView::New( 0, 0 );
127 }
128
129 // Setup properties, signals and actions using the type-registry.
130 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TableView, Toolkit::Control, Create );
131
132 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "rows",           INTEGER, ROWS           )
133 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "columns",        INTEGER, COLUMNS        )
134 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "cellPadding",    VECTOR2, CELL_PADDING   )
135 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layoutRows",     MAP,     LAYOUT_ROWS    )
136 DALI_PROPERTY_REGISTRATION( Toolkit, TableView, "layoutColumns",  MAP,     LAYOUT_COLUMNS )
137 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "cellIndex",                VECTOR2, CELL_INDEX                )
138 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "rowSpan",                  INTEGER, ROW_SPAN                  )
139 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "columnSpan",               INTEGER, COLUMN_SPAN               )
140 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "cellHorizontalAlignment",  STRING,  CELL_HORIZONTAL_ALIGNMENT )
141 DALI_CHILD_PROPERTY_REGISTRATION( Toolkit, TableView, "cellVerticalAlignment",    STRING,  CELL_VERTICAL_ALIGNMENT   )
142
143 DALI_TYPE_REGISTRATION_END()
144
145 const Scripting::StringEnum LAYOUT_POLICY_STRING_TABLE[] =
146 {
147  { "fixed",    Toolkit::TableView::FIXED    },
148  { "relative", Toolkit::TableView::RELATIVE },
149  { "fill",     Toolkit::TableView::FILL     },
150  { "fit",      Toolkit::TableView::FIT      }
151 };
152 const unsigned int LAYOUT_POLICY_STRING_TABLE_COUNT = sizeof(LAYOUT_POLICY_STRING_TABLE) / sizeof( LAYOUT_POLICY_STRING_TABLE[0] );
153
154 const Scripting::StringEnum HORIZONTAL_ALIGNMENT_STRING_TABLE[] =
155 {
156   {"left",   HorizontalAlignment::LEFT},
157   {"center", HorizontalAlignment::CENTER},
158   {"right",  HorizontalAlignment::RIGHT}
159 };
160 const unsigned int HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(HORIZONTAL_ALIGNMENT_STRING_TABLE) / sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE[0] );
161
162 const Scripting::StringEnum VERTICAL_ALIGNMENT_STRING_TABLE[] =
163 {
164   {"top",    VerticalAlignment::TOP},
165   {"center", VerticalAlignment::CENTER},
166   {"bottom", VerticalAlignment::BOTTOM}
167 };
168 const unsigned int VERTICAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof(VERTICAL_ALIGNMENT_STRING_TABLE) / sizeof( VERTICAL_ALIGNMENT_STRING_TABLE[0] );
169
170 } // Unnamed namespace
171
172 Toolkit::TableView TableView::New( unsigned int initialRows, unsigned int initialColumns )
173 {
174   // Create the implementation, temporarily owned by this handle on stack
175   IntrusivePtr< TableView > impl = new TableView( initialRows, initialColumns );
176
177   // Pass ownership to CustomActor handle
178   Toolkit::TableView handle( *impl );
179
180   // Second-phase init of the implementation
181   // This can only be done after the CustomActor connection has been made...
182   impl->Initialize();
183
184   return handle;
185 }
186
187 bool TableView::AddChild( Actor& child, const Toolkit::TableView::CellPosition& position )
188 {
189   // check that the child is valid
190   DALI_ASSERT_ALWAYS( child );
191
192   // if child is already parented, we adopt it
193   child.Unparent();
194
195   // check if we need to expand our data array
196   if( position.rowIndex >= mCellData.GetRows() )
197   {
198     // only adding new rows
199     ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
200   }
201
202   if( position.columnIndex >= mCellData.GetColumns() )
203   {
204     // only adding new columns
205     ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
206   }
207
208   // check if there already is something in this cell
209   if( mCellData[ position.rowIndex ][ position.columnIndex ].actor )
210   {
211     return false; // cannot share a cell, it would complicate all logic and not bring much benefit
212   }
213
214   RelayoutingLock lock( *this );
215   // adopt the child
216   Self().Add( child );
217
218   // if child spans multiple rows of columns
219   if( ( position.rowSpan > 1 ) && ( position.rowIndex + position.rowSpan > mCellData.GetRows() ) )
220   {
221     // increase table size for the full span, only increasing rows
222     ResizeContainers( position.rowIndex + position.rowSpan, mCellData.GetColumns() );
223   }
224
225   if( ( position.columnSpan > 1 ) && ( position.columnIndex + position.columnSpan > mCellData.GetColumns() ) )
226   {
227     // increase table size for the full span, only increasing columns
228     ResizeContainers( mCellData.GetRows(), position.columnIndex + position.columnSpan );
229   }
230
231   // Fill in all cells that need the data
232   CellData data;
233   data.actor = child;
234   data.position = position;
235
236   for( unsigned int row = position.rowIndex; row < ( position.rowIndex + position.rowSpan ); ++row )
237   {
238     // store same information to all cells, this way we can identify
239     // if a cell is the prime location of an actor or a spanned one
240     for( unsigned int column = position.columnIndex; column < ( position.columnIndex + position.columnSpan ); ++column )
241     {
242       // store same information to all cells, this way we can identify
243       // if a cell is the prime location of an actor or a spanned one
244       mCellData[ row ][ column ] = data;
245     }
246   }
247
248   // Relayout the whole table
249   if( mRowData[position.rowIndex].sizePolicy == Toolkit::TableView::FIT && position.rowSpan == 1 )
250   {
251     mRowDirty = true;
252   }
253   if( mColumnData[position.columnIndex].sizePolicy == Toolkit::TableView::FIT && position.columnSpan == 1 )
254   {
255     mColumnDirty = true;
256   }
257
258   RelayoutRequest();
259
260   return true;    // Addition successful
261 }
262
263 Actor TableView::GetChildAt( const Toolkit::TableView::CellPosition& position )
264 {
265   if( ( position.rowIndex < mCellData.GetRows() ) && ( position.columnIndex < mCellData.GetColumns() ) )
266   {
267     return mCellData[ position.rowIndex ][ position.columnIndex ].actor;
268   }
269
270   // Return an empty handle
271   return Actor();
272 }
273
274 Actor TableView::RemoveChildAt( const Toolkit::TableView::CellPosition& position )
275 {
276   // get the child handle
277   Actor child = GetChildAt( position );
278   // if no real actor there, nothing else to be done
279   if( child )
280   {
281     RelayoutingLock lock( *this );
282     // Remove the child, this will trigger a call to OnChildRemove
283     Self().Remove( child );
284
285     // relayout the table only if instances were found
286     if( RemoveAllInstances( child ) )
287     {
288       if( mRowData[position.rowIndex].sizePolicy == Toolkit::TableView::FIT )
289       {
290         mRowDirty = true;
291       }
292       if( mColumnData[position.columnIndex].sizePolicy == Toolkit::TableView::FIT )
293       {
294         mColumnDirty = true;
295       }
296       RelayoutRequest();
297     }
298   }
299   // return the child back to caller
300   return child;
301 }
302
303 bool TableView::FindChildPosition( const Actor& child, Toolkit::TableView::CellPosition& positionOut )
304 {
305   // Only find valid child actors
306   if( child )
307   {
308     // Walk through the layout data
309     const unsigned int rowCount = mCellData.GetRows();
310     const unsigned int columnCount = mCellData.GetColumns();
311
312     for( unsigned int row = 0; row < rowCount; ++row )
313     {
314       for( unsigned int column = 0; column < columnCount; ++column )
315       {
316         if( mCellData[ row ][ column ].actor == child )
317         {
318           positionOut = mCellData[ row ][ column ].position;
319           return true;
320         }
321       }
322     }
323   }
324
325   return false;
326 }
327
328 void TableView::InsertRow( unsigned int rowIndex )
329 {
330   RelayoutingLock lock( *this );
331
332   mCellData.InsertRow( rowIndex );
333
334   // Need to update the cell infos for the items that moved
335   const unsigned int rowCount = mCellData.GetRows();
336   const unsigned int columnCount = mCellData.GetColumns();
337
338   for( unsigned int row = 0; row < rowCount; ++row )
339   {
340     for( unsigned int column = 0; column < columnCount; ++column )
341     {
342       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
343
344       // If cell is spanning and above and spans to inserted row
345       if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
346           ( position.rowIndex + position.rowSpan > rowIndex ) )
347       {
348         // Increment span
349         position.rowSpan++;
350
351         // Copy cell to occupy the new column
352         mCellData[ rowIndex ][ column ] = mCellData[ row ][ column ];
353       }
354       else if( row > rowIndex )   // If below of inserted row, increase row index
355       {
356         // Increment index
357         position.rowIndex++;
358       }
359     }
360   }
361
362   // Expand row data array
363   mRowData.Insert( mRowData.Begin() + rowIndex, RowColumnData() );
364
365   // Sizes may have changed, so relayout
366   mRowDirty = true;
367   RelayoutRequest();
368 }
369
370 void TableView::DeleteRow( unsigned int rowIndex )
371 {
372   std::vector< Actor > ignored;
373   DeleteRow( rowIndex, ignored );
374 }
375
376 void TableView::DeleteRow( unsigned int rowIndex, std::vector<Actor>& removed )
377 {
378   RelayoutingLock lock( *this );
379
380   // Delete the row
381   std::vector< CellData > lost;
382   mCellData.DeleteRow( rowIndex, lost );
383
384   // Need to update the cell infos for the items that moved
385   const unsigned int rowCount = mCellData.GetRows();
386   const unsigned int columnCount = mCellData.GetColumns();
387
388   for( unsigned int row = 0; row < rowCount; ++row )
389   {
390     for( unsigned int column = 0; column < columnCount; ++column )
391     {
392       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
393
394       // If cell is spanning and above and spans to deleted row
395       if( ( position.rowSpan > 1 ) && ( position.rowIndex <= rowIndex ) &&
396           ( position.rowIndex + position.rowSpan > rowIndex ) )
397       {
398         // Decrement span
399         if( position.rowSpan > 1 )
400         {
401           position.rowSpan--;
402         }
403       }
404       else if( row >= rowIndex )    // If below of or at the inserted row, decrease row index
405       {
406         // Decrement index
407         if( position.rowIndex > 0 )
408         {
409           position.rowIndex--;
410         }
411       }
412     }
413   }
414
415   // 1 row removed, 0 columns
416   RemoveAndGetLostActors( lost, removed, 1u, 0u );
417
418   // Contract row data array
419   mRowData.Erase( mRowData.Begin() + rowIndex );
420
421   // Sizes may have changed, so relayout
422   mRowDirty = true;
423   // it is possible that the deletion of row leads to remove of child which might further lead to the change of FIT column
424   mColumnDirty = true;
425
426   RelayoutRequest();
427 }
428
429 void TableView::InsertColumn( unsigned int columnIndex )
430 {
431   RelayoutingLock lock( *this );
432
433   // Insert the new column
434   mCellData.InsertColumn( columnIndex );
435
436   // Need to update the cell infos for the items that moved
437   const unsigned int rowCount = mCellData.GetRows();
438   const unsigned int columnCount = mCellData.GetColumns();
439
440   for( unsigned int row = 0; row < rowCount; ++row )
441   {
442     for( unsigned int column = 0; column < columnCount; ++column )
443     {
444       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
445
446       // If cell is spanning and left side and spans to inserted column
447       if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
448           ( position.columnIndex + position.columnSpan > columnIndex ) )
449       {
450         // Increment span
451         position.columnSpan++;
452
453         // Copy cell to occupy the new column
454         mCellData[ row ][ columnIndex ] = mCellData[ row ][ column ];
455       }
456       else if( column > columnIndex )   // If on the right side of inserted column, increase column index
457       {
458         // Increment index
459         position.columnIndex++;
460       }
461     }
462   }
463
464   // Expand column data array
465   mColumnData.Insert( mColumnData.Begin() + columnIndex, RowColumnData() );
466
467   // Sizes may have changed so relayout
468   mColumnDirty = true;
469   RelayoutRequest();
470 }
471
472 void TableView::DeleteColumn( unsigned int columnIndex )
473 {
474   std::vector< Actor > ignored;
475   DeleteColumn( columnIndex, ignored );
476 }
477
478 void TableView::DeleteColumn( unsigned int columnIndex, std::vector<Actor>& removed )
479 {
480   RelayoutingLock lock( *this );
481
482   // Remove the column
483   std::vector< CellData > lost;
484   mCellData.DeleteColumn( columnIndex, lost );
485
486   // Need to update the cell infos for the items that moved
487   const unsigned int rowCount = mCellData.GetRows();
488   const unsigned int columnCount = mCellData.GetColumns();
489
490   for( unsigned int row = 0; row < rowCount; ++row )
491   {
492     for( unsigned int column = 0; column < columnCount; ++column )
493     {
494       Toolkit::TableView::CellPosition& position = mCellData[ row ][ column ].position;
495
496       // If cell is spanning and left side and spans to inserted column
497       if( ( position.columnSpan > 1 ) && ( position.columnIndex <= columnIndex ) &&
498           ( position.columnIndex + position.columnSpan > columnIndex ) )
499       {
500         // Decrement span
501         if( position.columnSpan > 1 )
502         {
503           position.columnSpan--;
504         }
505       }
506       else if( column >= columnIndex )    // If on the right side of or at the inserted column, decrease column index
507       {
508         // Decrement index
509         if( position.columnIndex > 0 )
510         {
511           position.columnIndex--;
512         }
513       }
514     }
515   }
516
517   // 0 rows, 1 column removed
518   RemoveAndGetLostActors( lost, removed, 0u, 1u );
519
520   // Contract column data array
521   mColumnData.Erase( mColumnData.Begin() + columnIndex );
522
523   // Size may have changed so relayout
524   mColumnDirty = true;
525   // it is possible that the deletion of column leads to remove of child which might further lead to the change of FIT row
526   mRowDirty = true;
527
528   RelayoutRequest();
529 }
530
531 void TableView::Resize( unsigned int rows, unsigned int columns )
532 {
533   std::vector< Actor > ignored;
534   Resize( rows, columns, ignored );
535 }
536
537 void TableView::Resize( unsigned int rows, unsigned int columns, std::vector<Actor>& removed )
538 {
539   RelayoutingLock lock( *this );
540
541   unsigned int oldRows = GetRows();
542   unsigned int oldColumns = GetColumns();
543
544   // Resize data array
545   std::vector< CellData > lost;
546   ResizeContainers( rows, columns, lost );
547
548   // Calculate if we lost rows
549   unsigned int rowsRemoved = 0;
550   unsigned int newRows = GetRows();
551
552   if( oldRows < newRows )
553   {
554     rowsRemoved = newRows - oldRows;
555   }
556
557   // Calculate if we lost columns
558   unsigned int columnsRemoved = 0;
559   unsigned int newColumns = GetColumns();
560   if( oldColumns < newColumns )
561   {
562     rowsRemoved = newColumns - oldColumns;
563   }
564
565   RemoveAndGetLostActors( lost, removed, rowsRemoved, columnsRemoved );
566
567   // Sizes may have changed so request a relayout
568   mRowDirty = true;
569   mColumnDirty = true;
570   RelayoutRequest();
571 }
572
573 void TableView::SetCellPadding( Size padding )
574 {
575   // If padding really changed
576   if( padding != mPadding )
577   {
578     mPadding = padding;
579
580     RelayoutRequest();
581   }
582 }
583
584 Size TableView::GetCellPadding()
585 {
586   return mPadding;
587 }
588
589 void TableView::SetFitHeight( unsigned int rowIndex )
590 {
591   DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
592
593   if( mRowData[ rowIndex ].sizePolicy != Toolkit::TableView::FIT )
594   {
595     mRowData[ rowIndex ].sizePolicy = Toolkit::TableView::FIT;
596
597     mRowDirty = true;
598     RelayoutRequest();
599   }
600 }
601
602 bool TableView::IsFitHeight( unsigned int rowIndex ) const
603 {
604   DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
605
606   return mRowData[ rowIndex ].sizePolicy == Toolkit::TableView::FIT;
607 }
608
609 void TableView::SetFitWidth( unsigned int columnIndex )
610 {
611   DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
612
613   if( mColumnData[ columnIndex ].sizePolicy != Toolkit::TableView::FIT )
614   {
615     mColumnData[ columnIndex ].sizePolicy = Toolkit::TableView::FIT;
616
617     mColumnDirty = true;
618     RelayoutRequest();
619   }
620 }
621
622 bool TableView::IsFitWidth( unsigned int columnIndex ) const
623 {
624   DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
625
626   return mColumnData[ columnIndex ].sizePolicy == Toolkit::TableView::FIT;
627 }
628
629 void TableView::SetFixedHeight( unsigned int rowIndex, float height )
630 {
631   DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
632
633   RowColumnData& data = mRowData[ rowIndex ];
634   data.size = height;
635   data.sizePolicy = Toolkit::TableView::FIXED;
636
637   mRowDirty = true;
638   RelayoutRequest();
639 }
640
641 float TableView::GetFixedHeight( unsigned int rowIndex ) const
642 {
643   DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
644
645   return mRowData[ rowIndex ].size;
646 }
647
648 void TableView::SetFixedWidth( unsigned int columnIndex, float width )
649 {
650   DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
651
652   RowColumnData& data = mColumnData[ columnIndex ];
653   data.size = width;
654   data.sizePolicy = Toolkit::TableView::FIXED;
655
656   mColumnDirty = true;
657   RelayoutRequest();
658 }
659
660 float TableView::GetFixedWidth( unsigned int columnIndex ) const
661 {
662   DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
663
664   return mColumnData[ columnIndex ].size;
665 }
666
667 void TableView::SetRelativeHeight( unsigned int rowIndex, float heightPercentage )
668 {
669   DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
670
671   RowColumnData& data = mRowData[ rowIndex ];
672   data.fillRatio = heightPercentage;
673   data.sizePolicy = Toolkit::TableView::RELATIVE;
674
675   mRowDirty = true;
676   RelayoutRequest();
677 }
678
679 float TableView::GetRelativeHeight( unsigned int rowIndex ) const
680 {
681   DALI_ASSERT_ALWAYS( rowIndex < mRowData.Size() );
682
683   return mRowData[ rowIndex ].fillRatio;
684 }
685
686 void TableView::SetRelativeWidth( unsigned int columnIndex, float widthPercentage )
687 {
688   DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
689
690   RowColumnData& data = mColumnData[ columnIndex ];
691   data.fillRatio = widthPercentage;
692   data.sizePolicy = Toolkit::TableView::RELATIVE;
693
694   mColumnDirty = true;
695   RelayoutRequest();
696 }
697
698 float TableView::GetRelativeWidth( unsigned int columnIndex ) const
699 {
700   DALI_ASSERT_ALWAYS( columnIndex < mColumnData.Size() );
701
702   return mColumnData[ columnIndex ].fillRatio;
703 }
704
705 void TableView::OnCalculateRelayoutSize( Dimension::Type dimension )
706 {
707   if( (dimension & Dimension::WIDTH) && mColumnDirty )
708   {
709     /*
710      * FIXED and FIT have size in pixel
711      * Nothing to do with FIXED, as its value is assigned by user and will not get changed
712      *
713      * Need to update the size for FIT column here
714      */
715     CalculateFitSizes( mColumnData, Dimension::WIDTH );
716
717     /* RELATIVE and FILL have size in ratio
718      * Their size in pixel is not available until we get the negotiated size for the whole table
719      * Nothing to do with RELATIVE, as its ratio is assigned by user and will not get changed
720      *
721      * Need to update the ratio for FILL column here
722      */
723     CalculateFillSizes( mColumnData );
724
725     mFixedTotals.width = CalculateTotalFixedSize( mColumnData );
726   }
727
728   if( (dimension & Dimension::HEIGHT) && mRowDirty )
729   {
730     // refer to the comment above
731     CalculateFitSizes( mRowData, Dimension::HEIGHT );
732
733     // refer to the comment above
734     CalculateFillSizes( mRowData );
735
736     mFixedTotals.height = CalculateTotalFixedSize( mRowData );
737   }
738 }
739
740 void TableView::OnLayoutNegotiated( float size, Dimension::Type dimension )
741 {
742   // Update the column sizes
743   if( (dimension & Dimension::WIDTH) && mColumnDirty )
744   {
745     float remainingSize = size - mFixedTotals.width;
746     if( remainingSize < 0.0f )
747     {
748       remainingSize = 0.0f;
749     }
750
751     // update every column position in ColumnData array
752     float cumulatedWidth = 0.0f;
753     for( auto&& element : mColumnData )
754     {
755       if( element.sizePolicy == Toolkit::TableView::FILL || element.sizePolicy == Toolkit::TableView::RELATIVE )
756       {
757         element.size = element.fillRatio * remainingSize;
758       }
759
760       cumulatedWidth += element.size;
761       element.position = cumulatedWidth;
762     }
763
764     mColumnDirty = false;
765   }
766
767   // Update the row sizes
768   if( (dimension & Dimension::HEIGHT) && mRowDirty )
769   {
770     float remainingSize = size - mFixedTotals.height;
771     if( remainingSize < 0.0f )
772     {
773       remainingSize = 0.0f;
774     }
775
776     // update every row position in RowData array
777     float cumulatedHeight = 0.0f;
778     for( unsigned int row = 0, rowCount = mCellData.GetRows(); row < rowCount; ++row )
779     {
780       if( mRowData[ row ].sizePolicy == Toolkit::TableView::FILL ||  mRowData[ row ].sizePolicy == Toolkit::TableView::RELATIVE)
781       {
782         mRowData[ row ].size = mRowData[ row ].fillRatio * remainingSize;
783       }
784
785       cumulatedHeight += mRowData[ row ].size;
786       mRowData[row].position = cumulatedHeight;
787     }
788
789     mRowDirty = false;
790   }
791 }
792
793 void TableView::OnSizeSet( const Vector3& size )
794 {
795   // If this table view is size negotiated by another actor or control, then the
796   // rows and columns must be recalculated or the new size will not take effect.
797   mRowDirty = mColumnDirty = true;
798   RelayoutRequest();
799
800   Control::OnSizeSet( size );
801 }
802
803 void TableView::OnRelayout( const Vector2& size, RelayoutContainer& container )
804 {
805   // Go through the layout data
806   float totalWidth = 0.0;
807
808   Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>( Self().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>() );
809
810   if( Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection )
811   {
812     for (auto&& element : mColumnData)
813     {
814       totalWidth += element.size;
815     }
816   }
817
818   for( unsigned int row = 0, rowCount = mCellData.GetRows(); row < rowCount; ++row )
819   {
820     for( unsigned int column = 0, columnCount = mCellData.GetColumns(); column < columnCount; ++column )
821     {
822       CellData& cellData= mCellData[ row ][ column ];
823       Actor& actor = cellData.actor;
824       const Toolkit::TableView::CellPosition position = cellData.position;
825
826       // If there is an actor and this is the main cell of the actor.
827       // An actor can be in multiple cells if its row or column span is more than 1.
828       // We however must lay out each actor only once.
829       if( actor &&  position.rowIndex == row && position.columnIndex == column )
830       {
831         // Anchor actor to top left of the cell
832         if( actor.GetProperty( Actor::Property::POSITION_USES_ANCHOR_POINT ).Get< bool >() )
833         {
834           actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT );
835         }
836         actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
837
838         Padding padding = actor.GetProperty<Vector4>( Actor::Property::PADDING );
839
840         float left = (column > 0) ? mColumnData[column - 1].position : 0.f;
841         float right;
842
843         if( Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection )
844         {
845           right = totalWidth - left;
846           left = right - mColumnData[column].size;
847         }
848         else
849         {
850           right = left + mColumnData[column].size;
851         }
852
853         float top = row > 0 ? mRowData[row-1].position : 0.f;
854         float bottom = mRowData[row+position.rowSpan-1].position;
855
856         if( cellData.horizontalAlignment == HorizontalAlignment::LEFT )
857         {
858           actor.SetProperty( Actor::Property::POSITION_X,  left + mPadding.width + padding.left );
859         }
860         else if( cellData.horizontalAlignment ==  HorizontalAlignment::RIGHT )
861         {
862           actor.SetProperty( Actor::Property::POSITION_X,  right - mPadding.width - padding.right - actor.GetRelayoutSize( Dimension::WIDTH ) );
863         }
864         else //if( cellData.horizontalAlignment ==  HorizontalAlignment::CENTER )
865         {
866           actor.SetProperty( Actor::Property::POSITION_X,  (left + right + padding.left - padding.right - actor.GetRelayoutSize( Dimension::WIDTH )) * 0.5f );
867         }
868
869         if( cellData.verticalAlignment == VerticalAlignment::TOP )
870         {
871           actor.SetProperty( Actor::Property::POSITION_Y,  top + mPadding.height + padding.top );
872         }
873         else if( cellData.verticalAlignment == VerticalAlignment::BOTTOM )
874         {
875           actor.SetProperty( Actor::Property::POSITION_Y,  bottom - mPadding.height - padding.bottom -  actor.GetRelayoutSize( Dimension::HEIGHT ) );
876         }
877         else //if( cellData.verticalAlignment = VerticalAlignment::CENTER )
878         {
879           actor.SetProperty( Actor::Property::POSITION_Y,  (top + bottom + padding.top - padding.bottom - actor.GetRelayoutSize( Dimension::HEIGHT )) * 0.5f );
880         }
881       }
882     }
883   }
884 }
885
886 unsigned int TableView::GetRows()
887 {
888   return mCellData.GetRows();
889 }
890
891 unsigned int TableView::GetColumns()
892 {
893   return mCellData.GetColumns();
894 }
895
896 void TableView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
897 {
898   Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
899
900   if( tableView )
901   {
902     TableView& tableViewImpl( GetImpl( tableView ) );
903     switch( index )
904     {
905       case Toolkit::TableView::Property::ROWS:
906       {
907         int rows = 0;
908         if( value.Get( rows ) && rows >= 0 )
909         {
910           if( static_cast<unsigned int>(rows) != tableViewImpl.GetRows() )
911           {
912             tableViewImpl.Resize( rows, tableViewImpl.GetColumns() );
913           }
914         }
915         break;
916       }
917       case Toolkit::TableView::Property::COLUMNS:
918       {
919         int columns = 0;
920         if( value.Get( columns ) && columns >= 0 )
921         {
922           if( static_cast<unsigned int>( columns ) != tableViewImpl.GetColumns() )
923           {
924             tableViewImpl.Resize( tableViewImpl.GetRows(), value.Get<int>() );
925           }
926         }
927         break;
928       }
929       case Toolkit::TableView::Property::CELL_PADDING:
930       {
931         tableViewImpl.SetCellPadding( value.Get<Vector2>() );
932         break;
933       }
934       case Toolkit::TableView::Property::LAYOUT_ROWS:
935       {
936         SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedHeight, &TableView::SetRelativeHeight, &TableView::SetFitHeight, value );
937         break;
938       }
939       case Toolkit::TableView::Property::LAYOUT_COLUMNS:
940       {
941         SetHeightOrWidthProperty( tableViewImpl, &TableView::SetFixedWidth, &TableView::SetRelativeWidth, &TableView::SetFitWidth, value );
942         break;
943       }
944     }
945   }
946 }
947
948 Property::Value TableView::GetProperty( BaseObject* object, Property::Index index )
949 {
950   Property::Value value;
951
952   Toolkit::TableView tableView = Toolkit::TableView::DownCast( Dali::BaseHandle( object ) );
953
954   if( tableView )
955   {
956     TableView& tableViewImpl( GetImpl( tableView ) );
957     switch( index )
958     {
959       case Toolkit::TableView::Property::ROWS:
960       {
961         value = static_cast<int>( tableViewImpl.GetRows() );
962         break;
963       }
964       case Toolkit::TableView::Property::COLUMNS:
965       {
966         value = static_cast<int>( tableViewImpl.GetColumns() );
967         break;
968       }
969       case Toolkit::TableView::Property::CELL_PADDING:
970       {
971         value = tableViewImpl.GetCellPadding();
972         break;
973       }
974       case Toolkit::TableView::Property::LAYOUT_ROWS:
975       {
976         value = tableViewImpl.GetRowHeightsPropertyValue();
977         break;
978       }
979       case Toolkit::TableView::Property::LAYOUT_COLUMNS:
980       {
981         value = tableViewImpl.GetColumnWidthsPropertyValue();
982         break;
983       }
984     }
985   }
986
987   return value;
988 }
989
990 void TableView::OnChildAdd( Actor& child )
991 {
992   if( ! mLayoutingChild )
993   {
994     // Ensure we're not in the middle of laying out children
995
996     // Check child properties on actor to decide its position inside the table
997     HorizontalAlignment::Type horizontalAlignment = HorizontalAlignment::LEFT;
998     VerticalAlignment::Type verticalAlignment = VerticalAlignment::TOP;
999
1000     if( child.GetPropertyType( Toolkit::TableView::ChildProperty::CELL_HORIZONTAL_ALIGNMENT ) != Property::NONE )
1001     {
1002       std::string value = child.GetProperty( Toolkit::TableView::ChildProperty::CELL_HORIZONTAL_ALIGNMENT ).Get<std::string >();
1003       Scripting::GetEnumeration< HorizontalAlignment::Type >( value.c_str(),
1004                                                               HORIZONTAL_ALIGNMENT_STRING_TABLE,
1005                                                               HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT,
1006                                                               horizontalAlignment );
1007     }
1008
1009     if( child.GetPropertyType( Toolkit::TableView::ChildProperty::CELL_VERTICAL_ALIGNMENT ) != Property::NONE )
1010     {
1011       std::string value = child.GetProperty( Toolkit::TableView::ChildProperty::CELL_VERTICAL_ALIGNMENT ).Get<std::string >();
1012       Scripting::GetEnumeration< VerticalAlignment::Type >( value.c_str(),
1013                                                             VERTICAL_ALIGNMENT_STRING_TABLE,
1014                                                             VERTICAL_ALIGNMENT_STRING_TABLE_COUNT,
1015                                                             verticalAlignment );
1016     }
1017
1018     Toolkit::TableView::CellPosition cellPosition;
1019     if( child.GetPropertyType( Toolkit::TableView::ChildProperty::ROW_SPAN ) != Property::NONE )
1020     {
1021       cellPosition.rowSpan = child.GetProperty( Toolkit::TableView::ChildProperty::ROW_SPAN ).Get< int >();
1022     }
1023
1024     if( child.GetPropertyType( Toolkit::TableView::ChildProperty::COLUMN_SPAN ) != Property::NONE )
1025     {
1026       cellPosition.columnSpan = child.GetProperty( Toolkit::TableView::ChildProperty::COLUMN_SPAN ).Get< int >();
1027     }
1028
1029     if( child.GetPropertyType( Toolkit::TableView::ChildProperty::CELL_INDEX ) != Property::NONE )
1030     {
1031       Vector2 indices = child.GetProperty( Toolkit::TableView::ChildProperty::CELL_INDEX ).Get<Vector2 >();
1032       cellPosition.rowIndex = static_cast<unsigned int>( indices.x );
1033       cellPosition.columnIndex = static_cast<unsigned int>( indices.y );
1034
1035       AddChild( child, cellPosition );
1036       SetCellAlignment(cellPosition, horizontalAlignment, verticalAlignment);
1037     }
1038     else
1039     {
1040       bool availableCellFound = false;
1041
1042       // Find the first available cell to store the actor in
1043       const unsigned int rowCount = mCellData.GetRows();
1044       const unsigned int columnCount = mCellData.GetColumns();
1045       for( unsigned int row = 0; row < rowCount && !availableCellFound; ++row )
1046       {
1047         for( unsigned int column = 0; column < columnCount && !availableCellFound; ++column )
1048         {
1049           if( !(mCellData[ row ][ column ].actor) )
1050           {
1051             // Put the actor in the cell
1052             CellData data;
1053             data.actor = child;
1054             data.position.columnIndex = column;
1055             data.position.rowIndex = row;
1056             data.horizontalAlignment = horizontalAlignment;
1057             data.verticalAlignment = verticalAlignment;
1058             mCellData[ row ][ column ] = data;
1059
1060             availableCellFound = true;
1061             break;
1062           }
1063         }
1064       }
1065
1066       if( ! availableCellFound )
1067       {
1068         // No empty cells, so increase size of the table
1069         unsigned int newColumnCount = ( columnCount > 0 ) ? columnCount : 1;
1070         ResizeContainers( rowCount + 1, newColumnCount );
1071
1072         // Put the actor in the first cell of the new row
1073         CellData data;
1074         data.actor = child;
1075         data.position.rowIndex = rowCount;
1076         data.position.columnIndex = 0;
1077         data.horizontalAlignment = horizontalAlignment;
1078         data.verticalAlignment = verticalAlignment;
1079         mCellData[ rowCount ][ 0 ] = data;
1080       }
1081
1082       RelayoutRequest();
1083     }
1084   }
1085
1086   Control::OnChildAdd( child );
1087 }
1088
1089 void TableView::OnChildRemove( Actor& child )
1090 {
1091   // dont process if we're in the middle of bigger operation like delete row, column or resize
1092   if( !mLayoutingChild )
1093   {
1094     // relayout the table only if instances were found
1095     if( RemoveAllInstances( child ) )
1096     {
1097       RelayoutRequest();
1098     }
1099   }
1100
1101   Control::OnChildRemove( child );
1102 }
1103
1104 TableView::TableView( unsigned int initialRows, unsigned int initialColumns )
1105 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
1106   mCellData( initialRows, initialColumns ),
1107   mPreviousFocusedActor(),
1108   mLayoutingChild( false ),
1109   mRowDirty( true ),     // Force recalculation first time
1110   mColumnDirty( true )
1111 {
1112   SetKeyboardNavigationSupport( true );
1113   ResizeContainers( initialRows, initialColumns );
1114 }
1115
1116 void TableView::OnInitialize()
1117 {
1118   // Make self as keyboard focusable and focus group
1119   Actor self = Self();
1120   self.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE,true);
1121   SetAsKeyboardFocusGroup(true);
1122 }
1123
1124 void TableView::ResizeContainers( unsigned int rows, unsigned int columns )
1125 {
1126   std::vector<CellData> ignored;
1127   ResizeContainers( rows, columns, ignored );
1128 }
1129
1130 void TableView::ResizeContainers( unsigned int rows, unsigned int columns, std::vector<CellData>& removed )
1131 {
1132   // Resize cell data
1133   mCellData.Resize( rows, columns, removed );
1134
1135   // We don't care if these go smaller, data will be regenerated or is not needed anymore
1136   mRowData.Resize( rows );
1137   mColumnData.Resize( columns );
1138 }
1139
1140 void TableView::RemoveAndGetLostActors( const std::vector<CellData>& lost, std::vector<Actor>& removed,
1141                                         unsigned int rowsRemoved, unsigned int columnsRemoved )
1142 {
1143   // iterate through all lost cells
1144   std::vector< CellData >::const_iterator iter = lost.begin();
1145   for( ; iter != lost.end(); ++iter )
1146   {
1147     // if it is a valid actor
1148     if( (*iter).actor )
1149     {
1150       // is this actor still somewhere else in the table
1151       Toolkit::TableView::CellPosition position;
1152       if( FindChildPosition( (*iter).actor, position ) )
1153       {
1154         // it must be spanning multiple cells, position contains the top left most one
1155         // check if position is left of the removed location
1156         if( position.columnIndex < (*iter).position.columnIndex )
1157         {
1158           // if column span is greater than 1
1159           if( mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan > 1 )
1160           {
1161             // decrease column span
1162             mCellData[ position.rowIndex ][ position.columnIndex ].position.columnSpan -= columnsRemoved;
1163           }
1164         }
1165         // check if position is left of the removed location
1166         if( position.rowIndex < (*iter).position.rowIndex )
1167         {
1168           // if row span is greater than 1
1169           if( mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan > 1 )
1170           {
1171             // decrease row span
1172             mCellData[ position.rowIndex ][ position.columnIndex ].position.rowSpan -= rowsRemoved;
1173           }
1174         }
1175       }
1176       else
1177       {
1178         // this actor is gone for good
1179         // add actor to removed container
1180         removed.push_back( (*iter).actor );
1181         // we dont want the child actor anymore
1182         Self().Remove( (*iter).actor );
1183       }
1184     }
1185   }
1186 }
1187
1188 bool TableView::RemoveAllInstances( const Actor& child )
1189 {
1190   bool found = false;
1191   // walk through the layout data
1192   const unsigned int rowCount = mCellData.GetRows();
1193   const unsigned int columnCount = mCellData.GetColumns();
1194   for( unsigned int row = 0; row < rowCount; ++row )
1195   {
1196     for( unsigned int column = 0; column < columnCount; ++column )
1197     {
1198       if( mCellData[ row ][ column ].actor == child )
1199       {
1200         // clear the cell, NOTE that the cell might be spanning multiple cells
1201         mCellData[ row ][ column ] = CellData();
1202         found = true;
1203       }
1204     }
1205   }
1206   return found;
1207 }
1208
1209 void TableView::SetHeightOrWidthProperty(TableView& tableViewImpl,
1210                                          void(TableView::*funcFixed)(unsigned int, float),
1211                                          void(TableView::*funcRelative)(unsigned int, float),
1212                                          void(TableView::*funcFit)(unsigned int),
1213                                          const Property::Value& value )
1214 {
1215   const Property::Map* map = value.GetMap();
1216   if( map )
1217   {
1218     unsigned int index(0);
1219     for ( unsigned int i = 0, count = map->Count(); i < count; ++i )
1220     {
1221       Property::Value& item = map->GetValue(i);
1222       Property::Map* childMap = item.GetMap();
1223
1224       std::istringstream( map->GetKey(i) ) >> index;
1225       if( childMap )
1226       {
1227         Property::Value* policy = childMap->Find( "policy" );
1228         Property::Value* childMapValue = childMap->Find( "value" );
1229         if( policy && childMapValue )
1230         {
1231           std::string policyValue;
1232           policy->Get( policyValue );
1233           Toolkit::TableView::LayoutPolicy policy;
1234           if( Scripting::GetEnumeration< Toolkit::TableView::LayoutPolicy >( policyValue.c_str(),
1235                                                                              LAYOUT_POLICY_STRING_TABLE,
1236                                                                              LAYOUT_POLICY_STRING_TABLE_COUNT,
1237                                                                              policy ) )
1238           {
1239             if( policy == Toolkit::TableView::FIXED  )
1240             {
1241               (tableViewImpl.*funcFixed)( index, childMapValue->Get<float>() );
1242             }
1243             else if( policy == Toolkit::TableView::RELATIVE )
1244             {
1245               (tableViewImpl.*funcRelative)( index, childMapValue->Get<float>() );
1246             }
1247             else if( policy == Toolkit::TableView::FIT )
1248             {
1249               (tableViewImpl.*funcFit)( index );
1250             }
1251             // do nothing for FILL policy
1252           }
1253         }
1254       }
1255     }
1256   }
1257 }
1258
1259 Property::Value TableView::GetRowHeightsPropertyValue()
1260 {
1261   Property::Map map;
1262   GetMapPropertyValue( mRowData, map);
1263   return Property::Value(map);
1264 }
1265
1266 Property::Value TableView::GetColumnWidthsPropertyValue()
1267 {
1268   Property::Map map;
1269   GetMapPropertyValue( mColumnData, map);
1270   return Property::Value(map);
1271 }
1272
1273 void TableView::GetMapPropertyValue( const RowColumnArray& data, Property::Map& map )
1274 {
1275   const char* fixedPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIXED,
1276                                                                                                LAYOUT_POLICY_STRING_TABLE,
1277                                                                                                LAYOUT_POLICY_STRING_TABLE_COUNT );
1278   const char* relativePolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::RELATIVE,
1279                                                                                                   LAYOUT_POLICY_STRING_TABLE,
1280                                                                                                   LAYOUT_POLICY_STRING_TABLE_COUNT );
1281   const char* fillPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FILL,
1282                                                                                               LAYOUT_POLICY_STRING_TABLE,
1283                                                                                               LAYOUT_POLICY_STRING_TABLE_COUNT );
1284   const char* fitPolicy = Scripting::GetEnumerationName< Toolkit::TableView::LayoutPolicy >( Toolkit::TableView::FIT,
1285                                                                                              LAYOUT_POLICY_STRING_TABLE,
1286                                                                                              LAYOUT_POLICY_STRING_TABLE_COUNT );
1287
1288   const RowColumnArray::SizeType count = data.Size();
1289   for( RowColumnArray::SizeType i = 0; i < count; i++ )
1290   {
1291     const RowColumnData& dataInstance = data[ i ];
1292
1293     Property::Map item;
1294     switch( dataInstance.sizePolicy )
1295     {
1296       case Toolkit::TableView::FIXED:
1297       {
1298         item[ "policy" ] = fixedPolicy;
1299         item[ "value" ] = dataInstance.size;
1300         break;
1301       }
1302       case Toolkit::TableView::RELATIVE:
1303       {
1304         item[ "policy" ] = relativePolicy;
1305         item[ "value" ] = dataInstance.fillRatio;
1306         break;
1307       }
1308       case Toolkit::TableView::FIT:
1309       {
1310         item[ "policy" ] = fitPolicy;
1311         item[ "value" ] = 0.f;
1312         break;
1313       }
1314       case Toolkit::TableView::FILL:
1315       default:
1316       {
1317         item[ "policy" ] = fillPolicy;
1318         item[ "value" ] = 0.f;
1319         break;
1320       }
1321     }
1322     std::ostringstream ss;
1323     ss << i;
1324     map[ ss.str() ] = item;
1325   }
1326 }
1327
1328 TableView::~TableView()
1329 {
1330   // nothing to do
1331 }
1332
1333 Actor TableView::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1334 {
1335   Actor nextFocusableActor;
1336
1337   if ( !currentFocusedActor )
1338   {
1339     // Nothing is currently focused, so the child in the first cell should be focused.
1340     nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1341   }
1342   else
1343   {
1344     Toolkit::TableView::CellPosition position;
1345     if( FindChildPosition( currentFocusedActor, position ) )
1346     {
1347       // The current focused actor is a child of TableView
1348       bool focusLost = false;
1349       int currentRow = position.rowIndex;
1350       int currentColumn = position.columnIndex;
1351       int numberOfColumns = GetColumns();
1352       int numberOfRows = GetRows();
1353
1354       bool lastCell = false;
1355       Actor nextValidActor;
1356
1357       switch ( direction )
1358       {
1359         case Toolkit::Control::KeyboardFocus::LEFT:
1360         {
1361           do
1362           {
1363             if(--currentColumn < 0)
1364             {
1365               currentColumn = numberOfColumns - 1;
1366               if(--currentRow < 0)
1367               {
1368                 lastCell = true;
1369                 currentRow = loopEnabled ? numberOfRows - 1 : 0;
1370                 focusLost = (currentRow == 0);
1371               }
1372             }
1373             nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1374           } while ( !nextValidActor && !lastCell );
1375           break;
1376         }
1377         case Toolkit::Control::KeyboardFocus::RIGHT:
1378         {
1379           do
1380           {
1381             if(++currentColumn > numberOfColumns - 1)
1382             {
1383               currentColumn = 0;
1384               if(++currentRow > numberOfRows - 1)
1385               {
1386                 lastCell = true;
1387                 currentRow = loopEnabled ? 0 : numberOfRows - 1;
1388                 focusLost = (currentRow == numberOfRows - 1);
1389               }
1390             }
1391             nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1392           } while ( !nextValidActor && !lastCell );
1393           break;
1394         }
1395         case Toolkit::Control::KeyboardFocus::UP:
1396         {
1397           do
1398           {
1399             if(--currentRow < 0)
1400             {
1401               lastCell = true;
1402               currentRow = loopEnabled ? numberOfRows - 1 : 0;
1403               focusLost = (currentRow == 0);
1404             }
1405             nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1406           } while ( !nextValidActor && !lastCell );
1407           break;
1408         }
1409         case Toolkit::Control::KeyboardFocus::DOWN:
1410
1411         {
1412           do
1413           {
1414             if(++currentRow > numberOfRows - 1)
1415             {
1416               lastCell = true;
1417               currentRow = loopEnabled ? 0 : numberOfRows - 1;
1418               focusLost = (currentRow == numberOfRows - 1);
1419             }
1420             nextValidActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1421           } while ( !nextValidActor && !lastCell );
1422           break;
1423         }
1424         default:
1425         {
1426           break;
1427         }
1428       }
1429
1430       // Move the focus if we haven't lost it.
1431       if(!focusLost)
1432       {
1433         nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(currentRow, currentColumn));
1434
1435         // Save the focused actor in the TableView.
1436         mPreviousFocusedActor = nextFocusableActor;
1437       }
1438     }
1439     else
1440     {
1441       // The current focused actor is not within this TableView.
1442
1443       unsigned int numberOfColumns = GetColumns();
1444       unsigned int numberOfRows = GetRows();
1445
1446       // Check whether the previous focused actor is a focus group (i.e. a layout container)
1447       bool wasFocusedOnLayoutContainer = false;
1448       Actor previousFocusedActor = mPreviousFocusedActor.GetHandle();
1449       if( previousFocusedActor )
1450       {
1451         Toolkit::Control control = Toolkit::Control::DownCast( previousFocusedActor );
1452         if( control )
1453         {
1454           Internal::Control& controlImpl = static_cast<Internal::Control&>(control.GetImplementation());
1455           wasFocusedOnLayoutContainer = controlImpl.IsKeyboardFocusGroup();
1456         }
1457       }
1458
1459       // Check whether the previous focused actor is a layout container and also a child of this TableView
1460       Toolkit::TableView::CellPosition position;
1461       if( wasFocusedOnLayoutContainer && FindChildPosition( previousFocusedActor, position ) )
1462       {
1463         nextFocusableActor = GetNextKeyboardFocusableActor(previousFocusedActor, direction, loopEnabled);
1464       }
1465       else
1466       {
1467         // Otherwise, move the focus to either the first or the last cell according to the given direction.
1468         if(direction == Toolkit::Control::KeyboardFocus::LEFT || direction == Toolkit::Control::KeyboardFocus::UP)
1469         {
1470           nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(numberOfRows - 1, numberOfColumns - 1));
1471         }
1472         else
1473         {
1474           nextFocusableActor = GetChildAt(Toolkit::TableView::CellPosition(0, 0));
1475         }
1476       }
1477     }
1478   }
1479
1480   return nextFocusableActor;
1481 }
1482
1483 Vector3 TableView::GetNaturalSize()
1484 {
1485   // Natural size is the size of all fixed cell widths or heights. This ignores cells with relative heights.
1486   return Vector3( mFixedTotals.width, mFixedTotals.height, 1.0f );
1487 }
1488
1489 float TableView::CalculateChildSize( const Actor& child, Dimension::Type dimension )
1490 {
1491   Toolkit::TableView::CellPosition position;
1492   if( FindChildPosition( child, position) )
1493   {
1494     switch( dimension )
1495     {
1496       case Dimension::WIDTH:
1497       {
1498         float cellSize = 0.0f;
1499         cellSize = mColumnData[position.columnIndex+position.columnSpan-1].position
1500                  - (position.columnIndex > 0 ? mColumnData[position.columnIndex-1].position : 0.f)
1501                  - mPadding.width * 2.0f;
1502
1503         if( cellSize < 0.0f )
1504         {
1505           cellSize = 0.0f;
1506         }
1507
1508         return cellSize;
1509       }
1510
1511       case Dimension::HEIGHT:
1512       {
1513         float cellSize = 0.0f;
1514
1515         cellSize = mRowData[position.rowIndex+position.rowSpan-1].position
1516                  - (position.rowIndex > 0 ? mRowData[position.rowIndex-1].position : 0.f)
1517                  - mPadding.height * 2.0f;
1518
1519         if( cellSize < 0.0f )
1520         {
1521           cellSize = 0.0f;
1522         }
1523
1524         return cellSize;
1525       }
1526       default:
1527       {
1528         return 0.0f;
1529       }
1530     }
1531   }
1532
1533   return 0.0f;    // Child not found
1534 }
1535
1536 bool TableView::RelayoutDependentOnChildren( Dimension::Type dimension )
1537 {
1538   if ( Control::RelayoutDependentOnChildren( dimension ) )
1539   {
1540     return true;
1541   }
1542
1543   return FindFit( mRowData ) || FindFit( mColumnData );
1544 }
1545
1546 void TableView::SetCellAlignment( Toolkit::TableView::CellPosition position, HorizontalAlignment::Type horizontal, VerticalAlignment::Type vertical )
1547 {
1548   // Check if we need to expand our data array
1549   if( position.rowIndex >= mCellData.GetRows() )
1550   {
1551     // Only adding new rows
1552     ResizeContainers( position.rowIndex + 1, mCellData.GetColumns() );
1553   }
1554
1555   if( position.columnIndex >= mCellData.GetColumns() )
1556   {
1557     // Only adding new columns
1558     ResizeContainers( mCellData.GetRows(), position.columnIndex + 1 );
1559   }
1560
1561   // Set the alignment of the cell
1562   CellData& data = mCellData[ position.rowIndex ][ position.columnIndex ];
1563   data.horizontalAlignment = horizontal;
1564   data.verticalAlignment = vertical;
1565 }
1566
1567 void TableView::CalculateFillSizes( RowColumnArray& data )
1568 {
1569   // First pass: Count number of fill entries and calculate used relative space
1570   Dali::Vector< RowColumnData* > fillData;
1571   float relativeTotal = 0.0f;
1572
1573   const unsigned int dataCount = data.Size();
1574
1575   for( unsigned int i = 0; i < dataCount; ++i )
1576   {
1577     RowColumnData& dataInstance = data[ i ];
1578
1579     if( dataInstance.sizePolicy == Toolkit::TableView::RELATIVE )
1580     {
1581       relativeTotal += dataInstance.fillRatio;
1582     }
1583     else if(dataInstance.sizePolicy == Toolkit::TableView::FILL)
1584     {
1585       fillData.PushBack( &dataInstance );
1586     }
1587   }
1588
1589   // Second pass: Distribute remaining relative space
1590   const unsigned int fillCount = fillData.Size();
1591   if( fillCount > 0 )
1592   {
1593     if( relativeTotal > 1.0f )
1594     {
1595       relativeTotal = 1.0f;
1596     }
1597
1598     const float evenFillRatio = (1.0f - relativeTotal ) / fillCount;
1599
1600     for( unsigned int i = 0; i < fillCount; ++i )
1601     {
1602       fillData[ i ]->fillRatio = evenFillRatio;
1603     }
1604   }
1605 }
1606
1607 float TableView::CalculateTotalFixedSize( const RowColumnArray& data )
1608 {
1609   float totalSize = 0.0f;
1610
1611   const unsigned int dataCount = data.Size();
1612
1613   for( unsigned int i = 0; i < dataCount; ++i )
1614   {
1615     const RowColumnData& dataInstance = data[ i ];
1616
1617     switch( dataInstance.sizePolicy )
1618     {
1619       // we have absolute size to FIXED and FIT column/row and relative size for RELATIVE and FILL column/row
1620       case Toolkit::TableView::FIXED:
1621       case Toolkit::TableView::FIT:
1622       {
1623         totalSize += dataInstance.size;
1624         break;
1625       }
1626
1627       default:
1628       {
1629         break;
1630       }
1631     }
1632   }
1633
1634   return totalSize;
1635 }
1636
1637 Vector2 TableView::GetCellPadding( Dimension::Type dimension )
1638 {
1639   switch( dimension )
1640   {
1641     case Dimension::WIDTH:
1642     {
1643       return Vector2( mPadding.x, mPadding.x );
1644     }
1645     case Dimension::HEIGHT:
1646     {
1647       return Vector2( mPadding.y, mPadding.y );
1648     }
1649     default:
1650     {
1651       break;
1652     }
1653   }
1654
1655   return Vector2();
1656 }
1657
1658 void TableView::CalculateFitSizes( RowColumnArray& data, Dimension::Type dimension )
1659 {
1660   Vector2 cellPadding = GetCellPadding( dimension );
1661
1662   const unsigned int dataCount = data.Size();
1663
1664   for( unsigned int i = 0; i < dataCount; ++i )
1665   {
1666     RowColumnData& dataInstance = data[ i ];
1667
1668     if( dataInstance.sizePolicy == Toolkit::TableView::FIT )
1669     {
1670       // Find the size of the biggest actor in the row or column
1671       float maxActorHeight = 0.0f;
1672
1673       unsigned int fitCount = ( dimension == Dimension::WIDTH ) ? mCellData.GetRows() : mCellData.GetColumns();
1674
1675       for( unsigned int j = 0; j < fitCount; ++j )
1676       {
1677         unsigned int row = ( dimension == Dimension::WIDTH ) ? j : i;
1678         unsigned int column = ( dimension == Dimension::WIDTH ) ? i : j;
1679         DALI_ASSERT_DEBUG( row < mCellData.GetRows() );
1680         DALI_ASSERT_DEBUG( column < mCellData.GetColumns() );
1681
1682         const CellData& cellData = mCellData[ row ][ column ];
1683         const Actor& actor = cellData.actor;
1684         if( actor )
1685         {
1686           if( FitToChild( actor, dimension ) && ( dimension == Dimension::WIDTH ) ? ( cellData.position.columnSpan == 1 ) : ( cellData.position.rowSpan == 1 )  )
1687           {
1688             maxActorHeight = std::max( maxActorHeight, actor.GetRelayoutSize( dimension ) + cellPadding.x + cellPadding.y );
1689           }
1690         }
1691       }
1692
1693       dataInstance.size = maxActorHeight;
1694     }
1695   }
1696 }
1697
1698 bool TableView::FindFit( const RowColumnArray& data )
1699 {
1700   for( unsigned int i = 0, count = data.Size(); i < count; ++i )
1701   {
1702     if( data[ i ].sizePolicy == Toolkit::TableView::FIT )
1703     {
1704       return true;
1705     }
1706   }
1707
1708   return false;
1709 }
1710
1711 } // namespace Internal
1712
1713 } // namespace Toolkit
1714
1715 } // namespace Dali