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