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