[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / popup / confirmation-popup-impl.cpp
1 /*
2  * Copyright (c) 2021 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 "confirmation-popup-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/object/type-registry-helper.h>
23 #include <dali/public-api/object/type-registry.h>
24 #include <cstring>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
28
29 namespace Dali
30 {
31 namespace Toolkit
32 {
33 namespace Internal
34 {
35 namespace
36 {
37 /*
38  * This struct is used to define all details required about a dynamically created signal.
39  */
40 struct ControlDetailType
41 {
42   const char* signalName;
43   const char* controlName;
44   const char* connectSignalPropertyName;
45 };
46
47 // clang-format off
48 /* A table of all control details. These details are kept in one place for maintainability.
49  *  Name of the signal     | Name of the control  | Name of the property which lets the
50  *  the app-developer      | which will provide   | app developer choose which signal
51  *  can connect to.        | the signal.          | within the control to connect to.    */
52 const ControlDetailType ControlDetails[] = {
53   {"controlSignalOk",       "controlOk",           "connectSignalOkSelected"    },
54   {"controlSignalCancel",   "controlCancel",       "connectSignalCancelSelected"},
55 };
56 // clang-format on
57 const unsigned int ControlDetailsCount = sizeof(ControlDetails) / sizeof(ControlDetails[0]);
58
59 // To give sensible default behaviour to save the connect signal properties being set.
60 const char* const DEFAULT_CONNECT_SIGNAL_NAME = "clicked";
61
62 BaseHandle Create()
63 {
64   return Toolkit::ConfirmationPopup::New();
65 }
66
67 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ConfirmationPopup, Toolkit::Popup, Create)
68
69 DALI_PROPERTY_REGISTRATION(Toolkit, ConfirmationPopup, ControlDetails[0].connectSignalPropertyName, STRING, CONNECT_SIGNAL_OK_SELECTED)
70 DALI_PROPERTY_REGISTRATION(Toolkit, ConfirmationPopup, ControlDetails[1].connectSignalPropertyName, STRING, CONNECT_SIGNAL_CANCEL_SELECTED)
71
72 // Note: We do not use the macros for signal registration as we do not want to redefine the signal name strings.
73 // We have predefined them for optimal signal name to control name lookup.
74 SignalConnectorType signalConnector1(typeRegistration, ControlDetails[0].signalName, &Toolkit::Internal::ConfirmationPopup::DoConnectSignal);
75 SignalConnectorType signalConnector2(typeRegistration, ControlDetails[1].signalName, &Toolkit::Internal::ConfirmationPopup::DoConnectSignal);
76
77 DALI_TYPE_REGISTRATION_END()
78
79 } // Unnamed namespace
80
81 Dali::Toolkit::ConfirmationPopup ConfirmationPopup::New()
82 {
83   // Create the implementation, temporarily owned on stack.
84   IntrusivePtr<ConfirmationPopup> internalConfirmationPopup = new ConfirmationPopup();
85
86   // Pass ownership to CustomActor
87   Dali::Toolkit::ConfirmationPopup confirmationPopup(*internalConfirmationPopup);
88
89   // Second-phase initialisation of the implementation.
90   // This can only be done after the CustomActor connection has been made...
91   internalConfirmationPopup->Initialize();
92
93   return confirmationPopup;
94 }
95
96 ConfirmationPopup::ConfirmationPopup()
97 : Toolkit::Internal::Popup()
98 {
99   mControlSignals.reserve(MAXIMUM_NUMBER_OF_CONTROLS);
100   mControlSignalNames[Toolkit::ConfirmationPopup::CONTROL_OK]     = DEFAULT_CONNECT_SIGNAL_NAME;
101   mControlSignalNames[Toolkit::ConfirmationPopup::CONTROL_CANCEL] = DEFAULT_CONNECT_SIGNAL_NAME;
102 }
103
104 ConfirmationPopup::~ConfirmationPopup()
105 {
106   for(SignalContainerType::iterator i = mControlSignals.begin(); i != mControlSignals.end(); ++i)
107   {
108     delete(i->second);
109   }
110   mControlSignals.clear();
111 }
112
113 void ConfirmationPopup::SetProperty(BaseObject* object, Property::Index propertyIndex, const Property::Value& value)
114 {
115   Toolkit::ConfirmationPopup popup = Toolkit::ConfirmationPopup::DownCast(Dali::BaseHandle(object));
116
117   if(popup)
118   {
119     ConfirmationPopup& popupImpl(GetDerivedImplementation(popup));
120
121     switch(propertyIndex)
122     {
123       case Toolkit::ConfirmationPopup::Property::CONNECT_SIGNAL_OK_SELECTED:
124       {
125         popupImpl.SetControlSignalName(Toolkit::ConfirmationPopup::CONTROL_OK, value.Get<std::string>());
126         break;
127       }
128       case Toolkit::ConfirmationPopup::Property::CONNECT_SIGNAL_CANCEL_SELECTED:
129       {
130         popupImpl.SetControlSignalName(Toolkit::ConfirmationPopup::CONTROL_CANCEL, value.Get<std::string>());
131         break;
132       }
133     }
134   }
135 }
136
137 Property::Value ConfirmationPopup::GetProperty(BaseObject* object, Property::Index propertyIndex)
138 {
139   Property::Value value;
140
141   Toolkit::ConfirmationPopup popup = Toolkit::ConfirmationPopup::DownCast(Dali::BaseHandle(object));
142
143   if(popup)
144   {
145     ConfirmationPopup& popupImpl(GetDerivedImplementation(popup));
146
147     switch(propertyIndex)
148     {
149       case Toolkit::ConfirmationPopup::Property::CONNECT_SIGNAL_OK_SELECTED:
150       {
151         value = popupImpl.GetControlSignalName(Toolkit::ConfirmationPopup::CONTROL_OK);
152         break;
153       }
154       case Toolkit::ConfirmationPopup::Property::CONNECT_SIGNAL_CANCEL_SELECTED:
155       {
156         value = popupImpl.GetControlSignalName(Toolkit::ConfirmationPopup::CONTROL_CANCEL);
157         break;
158       }
159     }
160   }
161
162   return value;
163 }
164
165 void ConfirmationPopup::SetControlSignalName(const unsigned int controlNumber, const std::string& signalName)
166 {
167   if(controlNumber < ControlDetailsCount)
168   {
169     mControlSignalNames[controlNumber] = signalName;
170   }
171 }
172
173 std::string ConfirmationPopup::GetControlSignalName(unsigned int controlNumber) const
174 {
175   if(controlNumber < ControlDetailsCount)
176   {
177     return mControlSignalNames[controlNumber];
178   }
179
180   return "";
181 }
182
183 bool ConfirmationPopup::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
184 {
185   Dali::BaseHandle           handle(object);
186   Toolkit::ConfirmationPopup popup = Toolkit::ConfirmationPopup::DownCast(handle);
187
188   // Look up the requested signal, attempting to create it dynamically if it doesn't exist.
189   SignalDelegate* signalDelegate = Dali::Toolkit::GetDerivedImplementation(popup).GetControlSignal(signalName);
190   if(signalDelegate)
191   {
192     // The signal delegate was created successfully, attempt to connect it to a callback if specified.
193     // If none is specified, the creation is still successful as the signal delegate can connect at a later time.
194     if(functor)
195     {
196       signalDelegate->Connect(tracker, functor);
197     }
198     return true;
199   }
200
201   // The signal could not be created.
202   return false;
203 }
204
205 SignalDelegate* ConfirmationPopup::GetControlSignal(const std::string& signalName)
206 {
207   // Check if the specified signal name already exists.
208   SignalContainerType::iterator end = mControlSignals.end();
209   for(SignalContainerType::iterator iter = mControlSignals.begin(); iter != end; ++iter)
210   {
211     // Find the first non-connected signal by matching signal name.
212     if((signalName == iter->first) && (!iter->second->IsConnected()))
213     {
214       // The requested signal (delegate) already exists, just return it.
215       return iter->second;
216     }
217   }
218
219   // The signal doesn't exist, or it does but it's already connected to something else.
220   // To make a new connection to an existing signal, we need a new delegate,
221   // as delegates house a signal connection functor each.
222   // Check the signal name is valid and if so create the signal dynamically.
223   for(unsigned int i = 0; i < ControlDetailsCount; ++i)
224   {
225     if(0 == strcmp(signalName.c_str(), ControlDetails[i].signalName))
226     {
227       // The signal name is valid, check the respective actor to connect to exists.
228       Actor connectActor = Self().FindChildByName(ControlDetails[i].controlName);
229       if(connectActor)
230       {
231         // The actor exists, set up a signal delegate that will allow the application developer
232         // to connect the actor signal directly to their callback.
233         // Note: We don't use the GetControlSignalName() here for speedup, as we know the array bound is capped.
234         SignalDelegate* signalDelegate = new SignalDelegate(connectActor, mControlSignalNames[i]);
235
236         // Store the delegate with the signal name so we know what signals have been dynamically created so far.
237         mControlSignals.push_back(std::make_pair(signalName, signalDelegate));
238
239         // Return the delegate to allow connection to the newly created signal.
240         return signalDelegate;
241       }
242
243       // Signal name valid but could not connect to the control,
244       return NULL;
245     }
246   }
247
248   // Signal name was not found (invalid).
249   return NULL;
250 }
251
252 } // namespace Internal
253
254 } // namespace Toolkit
255
256 } // namespace Dali