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