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);
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);
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);
Window
};
+ enum class Granularity {
+ CHAR = 1,
+ WORD,
+ LINE,
+ PARAGRAPH
+ };
+
Atspi();
~Atspi();
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;
+++ /dev/null
-#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();
- }
-};
--- /dev/null
+#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;
+};
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 {},
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 {},
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;
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");