[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-controls / text-selection-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 <dali-toolkit/internal/controls/text-controls/text-selection-popup-impl.h>
20
21 // EXTERNAL INCLUDES
22 #if defined(__GLIBC__)
23 #include <libintl.h>
24 #endif
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/animation/animation.h>
27 #include <dali/public-api/math/vector2.h>
28 #include <dali/public-api/math/vector4.h>
29 #include <dali/public-api/object/type-registry-helper.h>
30 #include <string.h>
31 #include <cfloat>
32
33 // INTERNAL INCLUDES
34 #include <dali-toolkit/devel-api/controls/buttons/button-devel.h>
35 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
36 #include <dali-toolkit/devel-api/controls/control-devel.h>
37 #include <dali-toolkit/devel-api/controls/text-controls/text-selection-popup-callback-interface.h>
38 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
39 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
40 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
41 #include <dali-toolkit/internal/controls/text-controls/text-selection-popup-property-handler.h>
42 #include <dali-toolkit/internal/helpers/color-conversion.h>
43 #include <dali-toolkit/public-api/controls/text-controls/text-label.h>
44 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
45 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
46 #include <dali-toolkit/public-api/visuals/visual-properties.h>
47
48 namespace Dali
49 {
50 namespace Toolkit
51 {
52 namespace Internal
53 {
54 namespace
55 {
56 #if defined(__GLIBC__)
57 #define GET_LOCALE_TEXT(string) dgettext("dali-toolkit", string)
58 #endif
59
60 const std::string   TEXT_SELECTION_POPUP_BUTTON_STYLE_NAME("TextSelectionPopupButton");
61 const Dali::Vector4 DEFAULT_OPTION_PRESSED_COLOR(Dali::Vector4(0.24f, 0.72f, 0.8f, 1.0f));
62 const float         DEFAULT_OPTION_PRESSED_CORNER_RADIUS = 0.0f;
63 const Dali::Vector4 DEFAULT_LABEL_PADDING(Dali::Vector4(24.0f, 24.0f, 14.0f, 14.0f));
64
65 #if defined(DEBUG_ENABLED)
66 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
67 #endif
68
69 #ifdef DGETTEXT_ENABLED
70
71 #define POPUP_CUT_STRING GET_LOCALE_TEXT("IDS_COM_BODY_CUT")
72 #define POPUP_COPY_STRING GET_LOCALE_TEXT("IDS_COM_BODY_COPY")
73 #define POPUP_PASTE_STRING GET_LOCALE_TEXT("IDS_COM_BODY_PASTE")
74 #define POPUP_SELECT_STRING GET_LOCALE_TEXT("IDS_COM_SK_SELECT")
75 #define POPUP_SELECT_ALL_STRING GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL")
76 #define POPUP_CLIPBOARD_STRING GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD")
77
78 #else
79
80 #define POPUP_CUT_STRING "Cut"
81 #define POPUP_COPY_STRING "Copy"
82 #define POPUP_PASTE_STRING "Paste"
83 #define POPUP_SELECT_STRING "Select"
84 #define POPUP_SELECT_ALL_STRING "Select All"
85 #define POPUP_CLIPBOARD_STRING "Clipboard"
86
87 #endif
88
89 const char* const OPTION_SELECT_WORD = "option-select_word"; // "Select Word" popup option.
90 const char* const OPTION_SELECT_ALL("option-select_all");    // "Select All" popup option.
91 const char* const OPTION_CUT("optionCut");                   // "Cut" popup option.
92 const char* const OPTION_COPY("optionCopy");                 // "Copy" popup option.
93 const char* const OPTION_PASTE("optionPaste");               // "Paste" popup option.
94 const char* const OPTION_CLIPBOARD("optionClipboard");       // "Clipboard" popup option.
95
96 const std::string IDS_LTR("IDS_LTR");
97 const std::string RTL_DIRECTION("RTL");
98
99 BaseHandle Create()
100 {
101   return Toolkit::TextSelectionPopup::New(NULL);
102 }
103
104 // Setup properties, signals and actions using the type-registry.
105
106 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::TextSelectionPopup, Toolkit::Control, Create);
107
108 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupMaxSize",              VECTOR2, POPUP_MAX_SIZE)
109 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupMinSize",              VECTOR2, POPUP_MIN_SIZE)
110 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionMaxSize",             VECTOR2, OPTION_MAX_SIZE)
111 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionMinSize",             VECTOR2, OPTION_MIN_SIZE)
112 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionDividerSize",         VECTOR2, OPTION_DIVIDER_SIZE)
113 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "optionDividerPadding",      VECTOR4, OPTION_DIVIDER_PADDING)
114 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupClipboardButtonImage", STRING,  POPUP_CLIPBOARD_BUTTON_ICON_IMAGE)
115 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupCutButtonImage",       STRING,  POPUP_CUT_BUTTON_ICON_IMAGE)
116 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupCopyButtonImage",      STRING,  POPUP_COPY_BUTTON_ICON_IMAGE)
117 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPasteButtonImage",     STRING,  POPUP_PASTE_BUTTON_ICON_IMAGE)
118 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupSelectButtonImage",    STRING,  POPUP_SELECT_BUTTON_ICON_IMAGE)
119 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupSelectAllButtonImage", STRING,  POPUP_SELECT_ALL_BUTTON_ICON_IMAGE)
120 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupDividerColor",         VECTOR4, POPUP_DIVIDER_COLOR)
121 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupIconColor",            VECTOR4, POPUP_ICON_COLOR)
122 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedColor",         VECTOR4, POPUP_PRESSED_COLOR)
123 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedCornerRadius",  FLOAT,   POPUP_PRESSED_CORNER_RADIUS)
124 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupPressedImage",         STRING,  POPUP_PRESSED_IMAGE)
125 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupFadeInDuration",       FLOAT,   POPUP_FADE_IN_DURATION)
126 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "popupFadeOutDuration",      FLOAT,   POPUP_FADE_OUT_DURATION)
127 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "backgroundBorder",          MAP,     BACKGROUND_BORDER)
128 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "background",                MAP,     BACKGROUND)
129 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelMinimumSize",          VECTOR2, LABEL_MINIMUM_SIZE)
130 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelPadding",              VECTOR4, LABEL_PADDING)
131 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "labelTextVisual",           MAP,     LABEL_TEXT_VISUAL)
132 DALI_PROPERTY_REGISTRATION(Toolkit, TextSelectionPopup, "enableScrollBar",           BOOLEAN, ENABLE_SCROLL_BAR)
133
134 DALI_TYPE_REGISTRATION_END()
135
136 } // namespace
137
138 Dali::Toolkit::TextSelectionPopup TextSelectionPopup::New(TextSelectionPopupCallbackInterface* callbackInterface)
139 {
140   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextSelectionPopup::New\n");
141
142   // Create the implementation, temporarily owned by this handle on stack
143   IntrusivePtr<TextSelectionPopup> impl = new TextSelectionPopup(callbackInterface);
144
145   // Pass ownership to CustomActor handle
146   Dali::Toolkit::TextSelectionPopup handle(*impl);
147
148   // Second-phase init of the implementation
149   // This can only be done after the CustomActor connection has been made...
150   impl->Initialize();
151
152   return handle;
153 }
154
155 void TextSelectionPopup::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
156 {
157   Toolkit::TextSelectionPopup selectionPopup = Toolkit::TextSelectionPopup::DownCast(Dali::BaseHandle(object));
158
159   if(selectionPopup)
160   {
161     PropertyHandler::SetProperty(selectionPopup, index, value);
162   }
163 }
164
165 Property::Value TextSelectionPopup::GetProperty(BaseObject* object, Property::Index index)
166 {
167   Property::Value value;
168   Toolkit::TextSelectionPopup selectionPopup = Toolkit::TextSelectionPopup::DownCast(Dali::BaseHandle(object));
169
170   if(selectionPopup)
171   {
172     value = PropertyHandler::GetProperty(selectionPopup, index);
173   }
174   return value;
175 }
176
177 void TextSelectionPopup::SetProperties(const Property::Map& properties)
178 {
179   Toolkit::TextSelectionPopup selectionPopup = Toolkit::TextSelectionPopup::DownCast(Self());
180
181   if(selectionPopup)
182   {
183     const Property::Map::SizeType count = properties.Count();
184     for(Property::Map::SizeType position = 0; position < count; ++position)
185     {
186       KeyValuePair     keyValue = properties.GetKeyValue(position);
187       Property::Key&   key      = keyValue.first;
188       Property::Value& value    = keyValue.second;
189       PropertyHandler::SetProperty(selectionPopup, key.indexKey, value);
190     }
191   }
192 }
193
194 void TextSelectionPopup::GetProperties(Property::Map& properties)
195 {
196   Property::Map map;
197
198   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_MAX_SIZE, GetDimensionToCustomise(POPUP_MAXIMUM_SIZE));
199   map.Insert(Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_SIZE, GetDimensionToCustomise(OPTION_DIVIDER_SIZE));
200   map.Insert(Toolkit::TextSelectionPopup::Property::OPTION_DIVIDER_PADDING, Vector4(mOptionDividerPadding.left, mOptionDividerPadding.right, mOptionDividerPadding.top, mOptionDividerPadding.bottom));
201   map.Insert(Toolkit::TextSelectionPopup::Property::LABEL_MINIMUM_SIZE, mLabelMinimumSize);
202   map.Insert(Toolkit::TextSelectionPopup::Property::LABEL_PADDING, Vector4(mLabelPadding.left, mLabelPadding.right, mLabelPadding.top, mLabelPadding.bottom));
203   map.Insert(Toolkit::TextSelectionPopup::Property::LABEL_TEXT_VISUAL, mLabelTextVisual);
204   map.Insert(Toolkit::TextSelectionPopup::Property::ENABLE_SCROLL_BAR, mEnableScrollBar);
205   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_DIVIDER_COLOR, mDividerColor);
206   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_FADE_IN_DURATION, mFadeInDuration);
207   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_FADE_OUT_DURATION, mFadeOutDuration);
208   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_COLOR, mPressedColor);
209   map.Insert(Toolkit::TextSelectionPopup::Property::POPUP_PRESSED_CORNER_RADIUS, mPressedCornerRadius);
210
211   Property::Map backgroundMap;
212   Toolkit::Visual::Base backgroundVisual = DevelControl::GetVisual(*this, Toolkit::Control::Property::BACKGROUND);
213   if(backgroundVisual)
214   {
215     backgroundVisual.CreatePropertyMap(backgroundMap);
216   }
217   map.Insert(Toolkit::TextSelectionPopup::Property::BACKGROUND, backgroundMap);
218
219   Property::Map borderMap;
220   Toolkit::Visual::Base borderVisual = DevelControl::GetVisual(*this, Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER);
221   if(borderVisual)
222   {
223     borderVisual.CreatePropertyMap(borderMap);
224   }
225   map.Insert(Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER, borderMap);
226
227   properties = map;
228 }
229
230 void TextSelectionPopup::EnableButtons(Toolkit::TextSelectionPopup::Buttons buttonsToEnable)
231 {
232   mEnabledButtons = buttonsToEnable;
233   mButtonsChanged = true;
234 }
235
236 void TextSelectionPopup::RaiseAbove(Actor target)
237 {
238   if(mToolbar)
239   {
240     mToolbar.RaiseAbove(target);
241   }
242 }
243
244 void TextSelectionPopup::ShowPopup()
245 {
246   if((!mPopupShowing || mButtonsChanged) &&
247      (Toolkit::TextSelectionPopup::NONE != mEnabledButtons))
248   {
249     Actor self = Self();
250     AddPopupOptionsToToolbar(mShowIcons, mShowCaptions);
251
252     Animation animation = Animation::New(mFadeInDuration);
253     animation.AnimateTo(Property(self, Actor::Property::COLOR_ALPHA), 1.0f);
254     animation.Play();
255     mPopupShowing = true;
256   }
257 }
258
259 void TextSelectionPopup::HidePopup()
260 {
261   if(mPopupShowing)
262   {
263     mPopupShowing       = false;
264     Actor     self      = Self();
265     Animation animation = Animation::New(mFadeOutDuration);
266     animation.AnimateTo(Property(self, Actor::Property::COLOR_ALPHA), 0.0f);
267     animation.FinishedSignal().Connect(this, &TextSelectionPopup::HideAnimationFinished);
268     animation.Play();
269   }
270 }
271
272 void TextSelectionPopup::OnInitialize()
273 {
274   DALI_LOG_INFO(gLogFilter, Debug::General, "TextSelectionPopup::OnInitialize\n");
275   Actor self = Self();
276   self.SetResizePolicy(ResizePolicy::FIT_TO_CHILDREN, Dimension::ALL_DIMENSIONS);
277   self.SetProperty(Actor::Property::COLOR_ALPHA, 0.0f);
278
279   // Accessibility
280   self.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, true);
281   self.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::DIALOG);
282 }
283
284 DevelControl::ControlAccessible* TextSelectionPopup::CreateAccessibleObject()
285 {
286   return new TextSelectionPopupAccessible(Self());
287 }
288
289 Dali::Accessibility::States TextSelectionPopup::TextSelectionPopupAccessible::CalculateStates()
290 {
291   auto states = ControlAccessible::CalculateStates();
292
293   states[Dali::Accessibility::State::MODAL] = true;
294
295   return states;
296 }
297
298 void TextSelectionPopup::HideAnimationFinished(Animation& animation)
299 {
300   Actor self = Self();
301   if(!mPopupShowing) // During the Hide/Fade animation there could be a call to Show the Popup again, mPopupShowing will be true in this case.
302   {
303     DALI_LOG_INFO(gLogFilter, Debug::General, "TextSelectionPopup::HideAnimationFinished\n");
304     UnparentAndReset(mToolbar);
305   }
306 }
307
308 bool TextSelectionPopup::OnCutButtonPressed(Toolkit::Button button)
309 {
310   if(mCallbackInterface)
311   {
312     mCallbackInterface->TextPopupButtonTouched(Toolkit::TextSelectionPopup::CUT);
313   }
314
315   return true;
316 }
317
318 bool TextSelectionPopup::OnCopyButtonPressed(Toolkit::Button button)
319 {
320   if(mCallbackInterface)
321   {
322     mCallbackInterface->TextPopupButtonTouched(Dali::Toolkit::TextSelectionPopup::COPY);
323   }
324
325   return true;
326 }
327
328 bool TextSelectionPopup::OnPasteButtonPressed(Toolkit::Button button)
329 {
330   if(mCallbackInterface)
331   {
332     mCallbackInterface->TextPopupButtonTouched(Dali::Toolkit::TextSelectionPopup::PASTE);
333   }
334
335   return true;
336 }
337
338 bool TextSelectionPopup::OnSelectButtonPressed(Toolkit::Button button)
339 {
340   if(mCallbackInterface)
341   {
342     mCallbackInterface->TextPopupButtonTouched(Dali::Toolkit::TextSelectionPopup::SELECT);
343   }
344
345   return true;
346 }
347
348 bool TextSelectionPopup::OnSelectAllButtonPressed(Toolkit::Button button)
349 {
350   if(mCallbackInterface)
351   {
352     mCallbackInterface->TextPopupButtonTouched(Dali::Toolkit::TextSelectionPopup::SELECT_ALL);
353   }
354
355   return true;
356 }
357
358 bool TextSelectionPopup::OnClipboardButtonPressed(Toolkit::Button button)
359 {
360   if(mCallbackInterface)
361   {
362     mCallbackInterface->TextPopupButtonTouched(Dali::Toolkit::TextSelectionPopup::CLIPBOARD);
363   }
364
365   return true;
366 }
367
368 void TextSelectionPopup::SetDimensionToCustomise(const PopupCustomisations& settingToCustomise, const Size& dimension)
369 {
370   switch(settingToCustomise)
371   {
372     case POPUP_MAXIMUM_SIZE:
373     {
374       mPopupMaxSize = dimension;
375       if(mToolbar)
376       {
377         mToolbar.SetProperty(Toolkit::TextSelectionToolbar::Property::MAX_SIZE, dimension);
378       }
379       break;
380     }
381     case OPTION_MAXIMUM_SIZE:
382     {
383       mOptionMaxSize = dimension;
384       // Option max size not currently currently supported
385       break;
386     }
387     case OPTION_MINIMUM_SIZE:
388     {
389       mOptionMinSize = dimension;
390       // Option min size not currently currently supported
391       break;
392     }
393     case OPTION_DIVIDER_SIZE:
394     {
395       mOptionDividerSize = dimension;
396       if(mToolbar)
397       {
398         // Resize Dividers not currently supported
399       }
400       break;
401     }
402   } // switch
403 }
404
405 Size TextSelectionPopup::GetDimensionToCustomise(const PopupCustomisations& settingToCustomise) const
406 {
407   switch(settingToCustomise)
408   {
409     case POPUP_MAXIMUM_SIZE:
410     {
411       if(mToolbar)
412       {
413         return mToolbar.GetProperty(Toolkit::TextSelectionToolbar::Property::MAX_SIZE).Get<Vector2>();
414       }
415       else
416       {
417         return mPopupMaxSize;
418       }
419     }
420     case OPTION_MAXIMUM_SIZE:
421     {
422       return mOptionMaxSize;
423     }
424     case OPTION_MINIMUM_SIZE:
425     {
426       return mOptionMinSize;
427     }
428     case OPTION_DIVIDER_SIZE:
429     {
430       return mOptionDividerSize;
431     }
432   } // switch
433
434   return Size::ZERO;
435 }
436
437 void TextSelectionPopup::SetButtonImage(Toolkit::TextSelectionPopup::Buttons button, const std::string& image)
438 {
439   switch(button)
440   {
441     case Toolkit::TextSelectionPopup::CLIPBOARD:
442     {
443       mClipboardIconImage = image;
444       break;
445     }
446     case Toolkit::TextSelectionPopup::CUT:
447     {
448       mCutIconImage = image;
449       break;
450     }
451     case Toolkit::TextSelectionPopup::COPY:
452     {
453       mCopyIconImage = image;
454       break;
455     }
456     case Toolkit::TextSelectionPopup::PASTE:
457     {
458       mPasteIconImage = image;
459       break;
460     }
461     case Toolkit::TextSelectionPopup::SELECT:
462     {
463       mSelectIconImage = image;
464       break;
465     }
466     case Toolkit::TextSelectionPopup::SELECT_ALL:
467     {
468       mSelectAllIconImage = image;
469       break;
470     }
471     default:
472     {
473       DALI_ASSERT_DEBUG("TextSelectionPopup SetPopupImage Unknown Button");
474     }
475   } // switch
476 }
477
478 const std::string& TextSelectionPopup::GetButtonImage(Toolkit::TextSelectionPopup::Buttons button) const
479 {
480   switch(button)
481   {
482     case Toolkit::TextSelectionPopup::CLIPBOARD:
483     {
484       return mClipboardIconImage;
485       break;
486     }
487     case Toolkit::TextSelectionPopup::CUT:
488     {
489       return mCutIconImage;
490       break;
491     }
492     case Toolkit::TextSelectionPopup::COPY:
493     {
494       return mCopyIconImage;
495       break;
496     }
497     case Toolkit::TextSelectionPopup::PASTE:
498     {
499       return mPasteIconImage;
500       break;
501     }
502     case Toolkit::TextSelectionPopup::SELECT:
503     {
504       return mSelectIconImage;
505       break;
506     }
507     case Toolkit::TextSelectionPopup::SELECT_ALL:
508     {
509       return mSelectAllIconImage;
510       break;
511     }
512     case Toolkit::TextSelectionPopup::NONE:
513     {
514       break;
515     }
516   } // switch
517
518   DALI_ASSERT_DEBUG("TextSelectionPopup GetPopupImage Unknown Button");
519   static std::string empty;
520   return empty;
521 }
522
523 void TextSelectionPopup::SetPressedImage(const std::string& filename)
524 {
525   mPressedImage = filename;
526 }
527
528 std::string TextSelectionPopup::GetPressedImage() const
529 {
530   return mPressedImage;
531 }
532
533 void TextSelectionPopup::SetOptionDividerPadding(const Padding& padding)
534 {
535   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextSelectionPopup::SetOptionDividerPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.top, padding.bottom);
536   mOptionDividerPadding = Padding(padding.left, padding.right, padding.top, padding.bottom);
537 }
538
539 Padding TextSelectionPopup::GetOptionDividerPadding() const
540 {
541   return mOptionDividerPadding;
542 }
543
544 void TextSelectionPopup::SetLabelPadding(const Padding& padding)
545 {
546   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextSelectionPopup::SetLabelPadding padding(%f,%f,%f,%f)\n", padding.left, padding.right, padding.top, padding.bottom);
547   mLabelPadding = Padding(padding.left, padding.right, padding.top, padding.bottom);
548 }
549
550 Padding TextSelectionPopup::GetLabelPadding() const
551 {
552   return mLabelPadding;
553 }
554
555 void TextSelectionPopup::CreateOrderedListOfPopupOptions()
556 {
557   mOrderListOfButtons.clear();
558   mOrderListOfButtons.reserve(8u);
559
560   // Create button for each possible option using Option priority
561   mOrderListOfButtons.push_back(ButtonRequirement(Toolkit::TextSelectionPopup::CUT, mCutOptionPriority, OPTION_CUT, POPUP_CUT_STRING, 0 != (mEnabledButtons & Toolkit::TextSelectionPopup::CUT)));
562   mOrderListOfButtons.push_back(ButtonRequirement(Toolkit::TextSelectionPopup::COPY, mCopyOptionPriority, OPTION_COPY, POPUP_COPY_STRING, 0 != (mEnabledButtons & Toolkit::TextSelectionPopup::COPY)));
563   mOrderListOfButtons.push_back(ButtonRequirement(Toolkit::TextSelectionPopup::PASTE, mPasteOptionPriority, OPTION_PASTE, POPUP_PASTE_STRING, 0 != (mEnabledButtons & Toolkit::TextSelectionPopup::PASTE)));
564   mOrderListOfButtons.push_back(ButtonRequirement(Toolkit::TextSelectionPopup::SELECT, mSelectOptionPriority, OPTION_SELECT_WORD, POPUP_SELECT_STRING, 0 != (mEnabledButtons & Toolkit::TextSelectionPopup::SELECT)));
565   mOrderListOfButtons.push_back(ButtonRequirement(Toolkit::TextSelectionPopup::SELECT_ALL, mSelectAllOptionPriority, OPTION_SELECT_ALL, POPUP_SELECT_ALL_STRING, 0 != (mEnabledButtons & Toolkit::TextSelectionPopup::SELECT_ALL)));
566   mOrderListOfButtons.push_back(ButtonRequirement(Toolkit::TextSelectionPopup::CLIPBOARD, mClipboardOptionPriority, OPTION_CLIPBOARD, POPUP_CLIPBOARD_STRING, 0 != (mEnabledButtons & Toolkit::TextSelectionPopup::CLIPBOARD)));
567
568   // Sort the buttons according their priorities.
569   std::sort(mOrderListOfButtons.begin(), mOrderListOfButtons.end(), TextSelectionPopup::ButtonPriorityCompare());
570 }
571
572 void TextSelectionPopup::AddOption(const ButtonRequirement& button, bool showDivider, bool showIcons, bool showCaption)
573 {
574   // 1. Create a option.
575   DALI_LOG_INFO(gLogFilter, Debug::General, "TextSelectionPopup::AddOption\n");
576
577   Toolkit::PushButton option = Toolkit::PushButton::New();
578   option.SetProperty(Dali::Actor::Property::NAME, button.name);
579   option.SetResizePolicy(ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS);
580   option.SetProperty(Actor::Property::MINIMUM_SIZE, mLabelMinimumSize);
581
582   switch(button.id)
583   {
584     case Toolkit::TextSelectionPopup::CUT:
585     {
586       option.ClickedSignal().Connect(this, &TextSelectionPopup::OnCutButtonPressed);
587       break;
588     }
589     case Toolkit::TextSelectionPopup::COPY:
590     {
591       option.ClickedSignal().Connect(this, &TextSelectionPopup::OnCopyButtonPressed);
592       break;
593     }
594     case Toolkit::TextSelectionPopup::PASTE:
595     {
596       option.ClickedSignal().Connect(this, &TextSelectionPopup::OnPasteButtonPressed);
597       break;
598     }
599     case Toolkit::TextSelectionPopup::SELECT:
600     {
601       option.ClickedSignal().Connect(this, &TextSelectionPopup::OnSelectButtonPressed);
602       break;
603     }
604     case Toolkit::TextSelectionPopup::SELECT_ALL:
605     {
606       option.ClickedSignal().Connect(this, &TextSelectionPopup::OnSelectAllButtonPressed);
607       break;
608     }
609     case Toolkit::TextSelectionPopup::CLIPBOARD:
610     {
611       option.ClickedSignal().Connect(this, &TextSelectionPopup::OnClipboardButtonPressed);
612       break;
613     }
614     case Toolkit::TextSelectionPopup::NONE:
615     {
616       // Nothing to do:
617       break;
618     }
619   }
620
621   // 2. Set the options contents.
622   if(showCaption)
623   {
624     // PushButton layout properties.
625     const Padding padding(mLabelPadding);
626     option.SetProperty(Toolkit::PushButton::Property::LABEL_PADDING, padding);
627
628     // Label properties.
629     Property::Map buttonLabelProperties;
630     buttonLabelProperties.Insert(Toolkit::TextVisual::Property::TEXT, button.caption);
631     buttonLabelProperties.Merge(mLabelTextVisual);
632
633     option.SetProperty(Toolkit::Button::Property::LABEL, buttonLabelProperties);
634   }
635   if(showIcons)
636   {
637     option.SetProperty(Toolkit::PushButton::Property::ICON_PADDING, Vector4(10.0f, 10.0f, 10.0f, 10.0f));
638     option.SetProperty(Toolkit::DevelButton::Property::LABEL_RELATIVE_ALIGNMENT, "BOTTOM");
639
640     // TODO: This is temporarily disabled until the text-selection-popup image API is changed to strings.
641     //option.SetProperty(  Toolkit::Button::Property::SELECTED_VISUAL, button.icon );
642     //option.SetProperty(  Toolkit::Button::Property::UNSELECTED_VISUAL, button.icon );
643   }
644
645   // 3. Set the normal option image (blank / Transparent).
646   option.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, "");
647
648   // 4. Set the pressed option image.
649   Property::Value selectedBackgroundValue(mPressedImage);
650   if(mPressedImage.empty())
651   {
652     // The image can be blank, the color can be used in that case.
653     selectedBackgroundValue = Property::Value{{Toolkit::Visual::Property::TYPE, Toolkit::Visual::COLOR},
654                                               {Toolkit::ColorVisual::Property::MIX_COLOR, mPressedColor},
655                                               {Toolkit::DevelVisual::Property::CORNER_RADIUS, mPressedCornerRadius},
656                                               {Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, Toolkit::Visual::Transform::Policy::RELATIVE}};
657   }
658   option.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, selectedBackgroundValue);
659   // The value set by user takes precedence over the theme value.
660   if(mLabelTextVisual.Count() == 0)
661   {
662     option.SetProperty(Toolkit::Control::Property::STYLE_NAME, TEXT_SELECTION_POPUP_BUTTON_STYLE_NAME);
663   }
664
665   // 5 Add option to tool bar
666   mToolbar.AddOption(option);
667
668   // 6. Add the divider
669   if(showDivider)
670   {
671     const Size    size(mOptionDividerSize.width, 0.0f); // Height FILL_TO_PARENT
672     const Padding padding(mOptionDividerPadding);
673
674     Toolkit::Control divider = Toolkit::Control::New();
675 #ifdef DECORATOR_DEBUG
676     divider.SetProperty(Dali::Actor::Property::NAME, "Text's popup divider");
677 #endif
678     divider.SetProperty(Actor::Property::SIZE, size);
679     divider.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT);
680     divider.SetProperty(Actor::Property::PADDING, padding);
681     divider.SetBackgroundColor(mDividerColor);
682     mToolbar.AddDivider(divider);
683   }
684 }
685
686 std::size_t TextSelectionPopup::GetNumberOfEnabledOptions() const
687 {
688   std::size_t numberOfOptions = 0u;
689   for(std::vector<ButtonRequirement>::const_iterator it = mOrderListOfButtons.begin(), endIt = mOrderListOfButtons.end(); (it != endIt); ++it)
690   {
691     const ButtonRequirement& button(*it);
692     if(button.enabled)
693     {
694       ++numberOfOptions;
695     }
696   }
697
698   return numberOfOptions;
699 }
700
701 void TextSelectionPopup::AddPopupOptionsToToolbar(bool showIcons, bool showCaptions)
702 {
703   DALI_LOG_INFO(gLogFilter, Debug::General, "TextSelectionPopup::AddPopupOptionsToToolbar\n");
704
705   CreateOrderedListOfPopupOptions();
706
707   mButtonsChanged = false;
708   UnparentAndReset(mToolbar);
709
710   if(!mToolbar)
711   {
712     Actor self = Self();
713     mToolbar   = Toolkit::TextSelectionToolbar::New();
714     if(mPopupMaxSize != Vector2::ZERO) // If PopupMaxSize property set then apply to Toolbar. Toolbar currently is not retriving this from json
715     {
716       mToolbar.SetProperty(Toolkit::TextSelectionToolbar::Property::MAX_SIZE, mPopupMaxSize);
717     }
718     mToolbar.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
719 #ifdef DECORATOR_DEBUG
720     mToolbar.SetProperty(Dali::Actor::Property::NAME, "TextSelectionToolbar");
721 #endif
722     mToolbar.SetProperty(Toolkit::TextSelectionToolbar::Property::ENABLE_SCROLL_BAR, mEnableScrollBar);
723     self.Add(mToolbar);
724   }
725
726   // Whether to mirror the list of buttons (for right to left languages)
727   bool mirror = false;
728 #if defined(__GLIBC__)
729   char* idsLtr = GET_LOCALE_TEXT(IDS_LTR.c_str());
730   if(NULL != idsLtr)
731   {
732     mirror = (0 == strcmp(idsLtr, RTL_DIRECTION.c_str()));
733
734     if(mirror)
735     {
736       std::reverse(mOrderListOfButtons.begin(), mOrderListOfButtons.end());
737     }
738   }
739 #endif
740
741   // Iterate list of buttons and add active ones to Toolbar
742   std::size_t numberOfOptionsRequired = GetNumberOfEnabledOptions();
743   std::size_t numberOfOptionsAdded    = 0u;
744   for(std::vector<ButtonRequirement>::const_iterator it = mOrderListOfButtons.begin(), endIt = mOrderListOfButtons.end(); (it != endIt); ++it)
745   {
746     const ButtonRequirement& button(*it);
747     if(button.enabled)
748     {
749       numberOfOptionsAdded++;
750       AddOption(button, (numberOfOptionsAdded < numberOfOptionsRequired), showIcons, showCaptions);
751     }
752   }
753
754   if(mirror)
755   {
756     mToolbar.ScrollTo(Vector2(mPopupMaxSize.x, 0.f));
757   }
758 }
759
760 void TextSelectionPopup::CreateBackgroundBorder(Property::Map& propertyMap)
761 {
762   // Removes previous image if necessary
763   DevelControl::UnregisterVisual(*this, Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER);
764
765   if(!propertyMap.Empty())
766   {
767     Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(propertyMap);
768
769     if(visual)
770     {
771       DevelControl::RegisterVisual(*this, Toolkit::TextSelectionPopup::Property::BACKGROUND_BORDER, visual, DepthIndex::CONTENT);
772     }
773   }
774 }
775
776 void TextSelectionPopup::CreateBackground(Property::Map& propertyMap)
777 {
778   // Removes previous image if necessary
779   DevelControl::UnregisterVisual(*this, Toolkit::Control::Property::BACKGROUND);
780
781   if(!propertyMap.Empty())
782   {
783     Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(propertyMap);
784
785     if(visual)
786     {
787       DevelControl::RegisterVisual(*this, Toolkit::Control::Property::BACKGROUND, visual, DepthIndex::BACKGROUND);
788     }
789   }
790 }
791
792 TextSelectionPopup::TextSelectionPopup(TextSelectionPopupCallbackInterface* callbackInterface)
793 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
794   mToolbar(),
795   mPopupMaxSize(),
796   mOptionMaxSize(),
797   mOptionMinSize(),
798   mOptionDividerSize(),
799   mOptionDividerPadding(),
800   mLabelMinimumSize(),
801   mLabelPadding(DEFAULT_LABEL_PADDING),
802   mLabelTextVisual(),
803   mEnableScrollBar(true),
804   mEnabledButtons(Toolkit::TextSelectionPopup::NONE),
805   mCallbackInterface(callbackInterface),
806   mPressedColor(DEFAULT_OPTION_PRESSED_COLOR),
807   mPressedCornerRadius(DEFAULT_OPTION_PRESSED_CORNER_RADIUS),
808   mDividerColor(Color::WHITE),
809   mIconColor(Color::WHITE),
810   mSelectOptionPriority(1),
811   mSelectAllOptionPriority(2),
812   mCutOptionPriority(4),
813   mCopyOptionPriority(3),
814   mPasteOptionPriority(5),
815   mClipboardOptionPriority(6),
816   mFadeInDuration(0.0f),
817   mFadeOutDuration(0.0f),
818   mShowIcons(false),
819   mShowCaptions(true),
820   mPopupShowing(false),
821   mButtonsChanged(false)
822 {
823 }
824
825 TextSelectionPopup::~TextSelectionPopup()
826 {
827 }
828
829 } // namespace Internal
830
831 } // namespace Toolkit
832
833 } // namespace Dali