NextGranularityUnitActivity & PrevGranularityUnitActivity 10/159510/6
authorPawel Kurowski <p.kurowski2@samsung.com>
Thu, 9 Nov 2017 09:59:00 +0000 (10:59 +0100)
committerPawel Kurowski <p.kurowski2@samsung.com>
Fri, 10 Nov 2017 13:15:25 +0000 (14:15 +0100)
Change-Id: I43875cf5d7f4f96d6af2b20289e09b2dad47d566

src/Atspi.cpp
src/Atspi.hpp
src/ClipboardActivity.cpp [deleted file]
src/EditTextActivity.cpp [new file with mode: 0644]
src/MenuBuilder.cpp
src/SelectActivity.cpp

index bb67d31..b37bc0a 100644 (file)
@@ -1057,6 +1057,52 @@ Optional<TextRange> Atspi::getTextSelection(const AtspiTextPtr &textInterface, i
        return ret;
 }
 
+namespace   //TODO change when atspi_text_get_string_at_offset will be supported by elementary
+{
+       Elm_Atspi_Text_Granularity convertGranularity(Atspi::Granularity granularity)
+       {
+               switch (granularity) {
+               case Atspi::Granularity::CHAR:
+                       return ELM_ATSPI_TEXT_GRANULARITY_CHAR;
+               case Atspi::Granularity::WORD:
+                       return ELM_ATSPI_TEXT_GRANULARITY_WORD;
+               case Atspi::Granularity::LINE:
+                       return ELM_ATSPI_TEXT_GRANULARITY_LINE;
+               case Atspi::Granularity::PARAGRAPH:
+                       return ELM_ATSPI_TEXT_GRANULARITY_PARAGRAPH;
+               }
+               return ELM_ATSPI_TEXT_GRANULARITY_CHAR;
+       }
+}
+
+Optional<TextRange> Atspi::getTextRangeAtOffset(const AtspiTextPtr &textInterface, size_t offset, Granularity granularity) const
+{
+       EXIT_IF_NULLPTR(textInterface);
+       GError *error = nullptr;
+       auto range = std::unique_ptr<AtspiTextRange, void(*)(void *)>(
+                                        //TODO change when atspi_text_get_string_at_offset will be supported by elementary
+                                        atspi_text_get_text_at_offset(textInterface.get(), static_cast<int>(offset), static_cast<AtspiTextBoundaryType>(convertGranularity(granularity)), &error), g_free);
+       PRINT_ERROR_AND_FREE(error);
+       if (range && range->start_offset >= 0 && range->end_offset >= 0)
+               return TextRange {static_cast<size_t>(range->start_offset), static_cast<size_t>(range->end_offset)};
+       else
+               return {};
+}
+
+std::string Atspi::getTextAtOffset(const AtspiTextPtr &textInterface, size_t offset, Granularity granularity) const
+{
+       EXIT_IF_NULLPTR(textInterface);
+       GError *error = nullptr;
+       auto range = std::unique_ptr<AtspiTextRange, void(*)(void *)>(
+                                        //TODO change when atspi_text_get_string_at_offset will be supported by elementary
+                                        atspi_text_get_text_at_offset(textInterface.get(), static_cast<int>(offset), static_cast<AtspiTextBoundaryType>(convertGranularity(granularity)), &error), g_free);
+       PRINT_ERROR_AND_FREE(error);
+       if (range && range->start_offset >= 0 && range->end_offset >= 0)
+               return std::string {range->content};
+       else
+               return {};
+}
+
 void Atspi::setTextSelection(const AtspiTextPtr &textInterface, TextRange range, int selectionNum) const
 {
        RETURN_ON_NULLPTR(textInterface);
@@ -1070,6 +1116,15 @@ void Atspi::setTextSelection(const AtspiTextPtr &textInterface, TextRange range,
        PRINT_ERROR_AND_FREE(error);
 }
 
+void Atspi::removeTextSelection(const AtspiTextPtr &textInterface, int selectionNum) const
+{
+       RETURN_ON_NULLPTR(textInterface);
+       GError *error = nullptr;
+       if (!atspi_text_remove_selection(textInterface.get(), selectionNum, &error))
+               ERROR("atspi_text_remove_selection failed");
+       PRINT_ERROR_AND_FREE(error);
+}
+
 Optional<size_t> Atspi::getTextCaretOffset(const AtspiTextPtr &textInterface) const
 {
        EXIT_IF_NULLPTR(textInterface);
@@ -1082,6 +1137,15 @@ Optional<size_t> Atspi::getTextCaretOffset(const AtspiTextPtr &textInterface) co
        return ret;
 }
 
+void Atspi::setTextCaretOffset(const AtspiTextPtr &textInterface, size_t offset) const
+{
+       RETURN_ON_NULLPTR(textInterface);
+       GError *error = nullptr;
+       if (!atspi_text_set_caret_offset(textInterface.get(), static_cast<int>(offset), &error))
+               ERROR("atspi_text_set_selection failed");
+       PRINT_ERROR_AND_FREE(error);
+}
+
 Optional<size_t> Atspi::countTextCharacters(const AtspiTextPtr &textInterface) const
 {
        EXIT_IF_NULLPTR(textInterface);
index 756acad..f8dc984 100644 (file)
@@ -39,6 +39,13 @@ public:
                Window
        };
 
+       enum class Granularity {
+               CHAR = 1,
+               WORD,
+               LINE,
+               PARAGRAPH
+       };
+
        Atspi();
        ~Atspi();
 
@@ -75,8 +82,12 @@ public:
 
        Optional<TextRange> getTextSelection(const AtspiTextPtr &textInterface, int selectionNum = 0) const;
        void setTextSelection(const AtspiTextPtr &textInterface, TextRange range, int selectionNum = 0) const;
+       void removeTextSelection(const AtspiTextPtr &textInterface, int selectionNum = 0) const;
        Optional<size_t> getTextCaretOffset(const AtspiTextPtr &textInterface) const;
+       void setTextCaretOffset(const AtspiTextPtr &textInterface, size_t offset) const;
        Optional<size_t> countTextCharacters(const AtspiTextPtr &textInterface) const;
+       Optional<TextRange> getTextRangeAtOffset(const AtspiTextPtr &textInterface, size_t offset, Granularity granularity) const;
+       std::string getTextAtOffset(const AtspiTextPtr &textInterface, size_t offset, Granularity granularity) const;
        void copyEditableText(const AtspiEditableTextPtr &editableTextInterface, TextRange) const;
        void cutEditableText(const AtspiEditableTextPtr &editableTextInterface, TextRange) const;
        void pasteEditableText(const AtspiEditableTextPtr &editableTextInterface, size_t offset) const;
diff --git a/src/ClipboardActivity.cpp b/src/ClipboardActivity.cpp
deleted file mode 100644 (file)
index 5069bfa..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#include "UIActivity.hpp"
-#include "ActivityFactory.hpp"
-#include "UniversalSwitchLog.hpp"
-#include "Atspi.hpp"
-#include "Singleton.hpp"
-#include "UniversalSwitch.hpp"
-
-#include <memory>
-
-template <typename DerivedType>
-class ClipboardActivity : public UIActivity, private RegisterActivity<DerivedType>
-{
-public:
-       constexpr static const char *activityType = DerivedType::activityType;
-
-       ClipboardActivity() : UIActivity(activityType), atspi(Singleton<UniversalSwitch>::instance().getAtspi()) {}
-
-protected:
-       void getAtspiInterfaces()
-       {
-               ASSERT(atspi, "Atspi should exist");
-
-               if (!uiElement) {
-                       ERROR("Process invoked with incorrect UIElement");
-                       return;
-               }
-
-               atspiText = atspi->getTextInterface(uiElement->getObject());
-               atspiEditableText = atspi->getEditableTextInterface(uiElement->getObject());
-       }
-
-       std::shared_ptr<Atspi> atspi;
-       AtspiTextPtr atspiText;
-       AtspiEditableTextPtr atspiEditableText;
-};
-
-
-class CopyActivity : public ClipboardActivity<CopyActivity>
-{
-public:
-       constexpr static const char *activityType = "COPY";
-
-       void process() override
-       {
-               getAtspiInterfaces();
-
-               auto range = atspi->getTextSelection(atspiText);
-               if (range)
-                       atspi->copyEditableText(atspiEditableText, *range);
-
-               markAsCompleted();
-       }
-};
-
-class CutActivity : public ClipboardActivity<CutActivity>
-{
-public:
-       constexpr static const char *activityType = "CUT";
-
-       void process() override
-       {
-               getAtspiInterfaces();
-
-               auto range = atspi->getTextSelection(atspiText);
-               if (range)
-                       atspi->cutEditableText(atspiEditableText, *range);
-
-               markAsCompleted();
-       }
-};
-
-
-class PasteActivity : public ClipboardActivity<PasteActivity>
-{
-public:
-       constexpr static const char *activityType = "PASTE";
-
-       void process() override
-       {
-               getAtspiInterfaces();
-
-               auto caretOffset = atspi->getTextCaretOffset(atspiText);
-               if (caretOffset)
-                       atspi->pasteEditableText(atspiEditableText, *caretOffset);
-
-               markAsCompleted();
-       }
-};
-
-class SelectAllActivity : public ClipboardActivity<SelectAllActivity>
-{
-public:
-       constexpr static const char *activityType = "SELECT_ALL";
-
-       void process() override
-       {
-               getAtspiInterfaces();
-
-               auto offsetEnd = atspi->countTextCharacters(atspiText);
-               if (offsetEnd) {
-                       auto range = TextRange {0, *offsetEnd};
-                       atspi->setTextSelection(atspiText, range);
-               }
-
-               markAsCompleted();
-       }
-};
diff --git a/src/EditTextActivity.cpp b/src/EditTextActivity.cpp
new file mode 100644 (file)
index 0000000..50583e9
--- /dev/null
@@ -0,0 +1,222 @@
+#include "UIActivity.hpp"
+#include "ActivityFactory.hpp"
+#include "UniversalSwitchLog.hpp"
+#include "Atspi.hpp"
+#include "Singleton.hpp"
+#include "UniversalSwitch.hpp"
+#include "VConfKeys.hpp"
+
+#include <memory>
+
+template <typename DerivedType>
+class EditTextActivity : public UIActivity, private RegisterActivity<DerivedType>
+{
+public:
+       constexpr static const char *activityType = DerivedType::activityType;
+
+       EditTextActivity() : UIActivity(activityType), atspi(Singleton<UniversalSwitch>::instance().getAtspi()) {}
+
+protected:
+       void getAtspiInterfaces()
+       {
+               ASSERT(atspi, "No Atspi object");
+
+               if (!uiElement) {
+                       ERROR("Process invoked with no UIElement");
+                       return;
+               }
+
+               atspiText = atspi->getTextInterface(uiElement->getObject());
+               atspiEditableText = atspi->getEditableTextInterface(uiElement->getObject());
+       }
+
+       std::shared_ptr<Atspi> atspi;
+       AtspiTextPtr atspiText;
+       AtspiEditableTextPtr atspiEditableText;
+};
+
+
+class CopyActivity : public EditTextActivity<CopyActivity>
+{
+public:
+       constexpr static const char *activityType = "COPY";
+
+       void process() override
+       {
+               getAtspiInterfaces();
+
+               auto range = atspi->getTextSelection(atspiText);
+               if (range)
+                       atspi->copyEditableText(atspiEditableText, *range);
+
+               markAsCompleted();
+       }
+};
+
+class CutActivity : public EditTextActivity<CutActivity>
+{
+public:
+       constexpr static const char *activityType = "CUT";
+
+       void process() override
+       {
+               getAtspiInterfaces();
+
+               auto range = atspi->getTextSelection(atspiText);
+               if (range)
+                       atspi->cutEditableText(atspiEditableText, *range);
+
+               markAsCompleted();
+       }
+};
+
+
+class PasteActivity : public EditTextActivity<PasteActivity>
+{
+public:
+       constexpr static const char *activityType = "PASTE";
+
+       void process() override
+       {
+               getAtspiInterfaces();
+
+               auto caretOffset = atspi->getTextCaretOffset(atspiText);
+               if (caretOffset)
+                       atspi->pasteEditableText(atspiEditableText, *caretOffset);
+
+               markAsCompleted();
+       }
+};
+
+class SelectAllActivity : public EditTextActivity<SelectAllActivity>
+{
+public:
+       constexpr static const char *activityType = "SELECT_ALL";
+
+       void process() override
+       {
+               getAtspiInterfaces();
+
+               auto offsetEnd = atspi->countTextCharacters(atspiText);
+               if (offsetEnd)
+                       atspi->setTextSelection(atspiText, {0, *offsetEnd});
+
+               markAsCompleted();
+       }
+};
+
+
+/*
+ * TODO:
+ * 1. in AT-SPI2 bridge for EFL, add support to atspi_text_get_string_after_offset,
+ * atspi_text_get_string_at_offset, atspi_text_get_string_before_offset
+ * 2. in Universal Switch use dedicated after/before functions mentioned above,
+ * instead of simulating them with atspi_text_get_at_offset
+ */
+
+template<typename DerivedType>
+class MoveByGranularityUnitActivity : public EditTextActivity<DerivedType>
+{
+public:
+       using EditTextActivity<DerivedType>::atspi;
+       using EditTextActivity<DerivedType>::atspiText;
+       using EditTextActivity<DerivedType>::getAtspiInterfaces;
+       using EditTextActivity<DerivedType>::markAsCompleted;
+       void process() override
+       {
+               //TODO SelectionMode
+               getAtspiInterfaces();
+               move(DerivedType::DIRECTION, getGranularity());
+               markAsCompleted();
+       }
+
+protected:
+       enum class Direction : bool {
+               PREV = false,
+               NEXT = true
+       };
+
+       Atspi::Granularity getGranularity()
+       {
+               auto vconfValue = Singleton<VConfInterface>::instance().get(VCONF_KEY_GRANULARITY_UNIT, 1);
+               auto ret = static_cast<Atspi::Granularity>(vconfValue);
+               if (ret >= Atspi::Granularity::CHAR && ret <= Atspi::Granularity::PARAGRAPH)
+                       return ret;
+               else
+                       return Atspi::Granularity::CHAR;
+       }
+
+       void move(const Direction direction, const Atspi::Granularity granularity)
+       {
+               const auto currentSelectionRange = atspi->getTextSelection(atspiText);
+               if (currentSelectionRange)
+                       moveToSelectionLimit(direction, granularity, *currentSelectionRange);
+               else
+                       moveByGranularityUnit(direction, granularity);
+       }
+
+private:
+       void moveToSelectionLimit(const Direction direction, const Atspi::Granularity granularity, const TextRange currentSelectionRange)
+       {
+               auto selectionMarker = (direction == Direction::NEXT) ? currentSelectionRange.end : currentSelectionRange.start;
+               DEBUG("Removing selection, moving caret to: %d", selectionMarker);
+               atspi->setTextCaretOffset(atspiText, selectionMarker);
+               atspi->removeTextSelection(atspiText);
+       }
+
+       void moveByGranularityUnit(const Direction direction, const Atspi::Granularity granularity)
+       {
+               const auto offset = atspi->getTextCaretOffset(atspiText);
+               const auto limit = atspi->countTextCharacters(atspiText);
+               if (!offset || !limit)
+                       return;
+
+               auto currentOffset = *offset;
+               auto range = atspi->getTextRangeAtOffset(atspiText, currentOffset, granularity);
+               if (!range)
+                       return;
+
+               auto finalOffset = range->end;
+
+               if (direction == Direction::NEXT && currentOffset == finalOffset) {
+                       do {
+                               if (++currentOffset >= *limit) {
+                                       finalOffset = *limit;
+                                       break;
+                               }
+                               range = atspi->getTextRangeAtOffset(atspiText, currentOffset, granularity);
+                               if (!range)
+                                       return;
+
+                               finalOffset = range->end;
+                       } while (currentOffset > finalOffset);
+               } else if (direction == Direction::PREV) {
+                       if (currentOffset > 0)
+                               currentOffset--;
+
+                       range = atspi->getTextRangeAtOffset(atspiText, currentOffset, granularity);
+                       if (!range)
+                               return;
+
+                       finalOffset = range->start;
+               }
+
+               DEBUG("Moving caret from: %d to: %d", *offset, finalOffset);
+               atspi->setTextCaretOffset(atspiText, finalOffset);
+       }
+};
+
+class NextGranularityUnitActivity : public MoveByGranularityUnitActivity<NextGranularityUnitActivity>
+{
+public:
+       static constexpr const char *activityType = "NEXT_GRANLUARITY_UNIT";
+       static constexpr Direction DIRECTION = Direction::NEXT;
+};
+
+
+class PrevGranularityUnitActivity : public MoveByGranularityUnitActivity<PrevGranularityUnitActivity>
+{
+public:
+       static constexpr const char *activityType = "PREV_GRANLUARITY_UNIT";
+       static constexpr Direction DIRECTION = Direction::PREV;
+};
index 0e047c9..a9a03ab 100644 (file)
@@ -324,7 +324,7 @@ MenuBuilderImplementation::MenuBuilderImplementation()
        auto previous                           =       std::make_shared<VconfIntTypeMenuItem>(
                                                                                std::vector<std::string> { "IDS_PREVIOUS_CHARACTER", "IDS_PREVIOUS_WORD", "IDS_PREVIOUS_LINE", "IDS_PREVIOUS_PARAGRAPH"},
                                                                                defaultImg,
-                                                                               std::string {},/*TODO add activity*/
+                                                                               "PREV_GRANLUARITY_UNIT",
                                                                                std::string {VCONF_KEY_GRANULARITY_UNIT},
                                                                                std::string {},
                                                                                std::string {},
@@ -332,7 +332,7 @@ MenuBuilderImplementation::MenuBuilderImplementation()
        auto next                                       =       std::make_shared<VconfIntTypeMenuItem>(
                                                                                std::vector<std::string> { "IDS_NEXT_CHARACTER", "IDS_NEXT_WORD", "IDS_NEXT_LINE", "IDS_NEXT_PARAGRAPH"},
                                                                                defaultImg,
-                                                                               std::string {},/*TODO add activity*/
+                                                                               "NEXT_GRANLUARITY_UNIT",
                                                                                std::string {VCONF_KEY_GRANULARITY_UNIT},
                                                                                std::string {},
                                                                                std::string {},
index af04944..c7cba6c 100644 (file)
@@ -26,7 +26,6 @@ static const double    AUTO_TAP_WAITING_PERIOD_DEFAULT_TIME = 3.0;
 
 static const std::string AUTO_SCAN_KEY                      = VCONF_KEY_AUTO_SCAN_ENABLED;
 static const std::string AUTO_SCROLL_KEY                    = VCONF_KEY_AUTO_SCROLL_ENABLED;
-static const std::string TEXT_EDITION_MODE                  = VCONF_KEY_TEXT_EDITION_MODE;
 static const std::string AUTO_TAP_KEY                       = VCONF_KEY_AUTO_TAP_ENABLED;
 static const std::string AUTO_TAP_WAITING_PERIOD_KEY        = VCONF_KEY_AUTO_TAP_WAITING_TIME;
 
@@ -326,9 +325,6 @@ void SelectActivity::navigateThroughSubMenu(const std::string &subMenuLabel)
        if (subMenuLabel == "PREVIOUS_MENU") {
                navigateBack();
        } else { //TODO this part should be refactored, select shouldn't perform additional logic depending on menu name
-               if (subMenuLabel == "IDS_MENU_EDIT_TEXT")
-                       Singleton<VConfInterface>::instance().set(TEXT_EDITION_MODE, 1);
-
                nestedMenusLabels.push_back(subMenuLabel);
                if (subMenuLabel == "IDS_MENU_AUTO_SCROLL") {
                        DEBUG("creating scroll activities data");