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