[dasher: 104/217] Add moves and speak around cursor to the Linux + Gtk2 with AT/SPI dasher.



commit 4e69d26a054d23ab85e32a034893038757b52bcd
Author: lbaudoin <lbaudoin google com>
Date:   Tue Nov 3 20:45:11 2015 -0800

    Add moves and speak around cursor to the Linux + Gtk2 with AT/SPI dasher.

 Src/Gtk2/GtkDasherControl.cpp             |    2 +-
 Src/Gtk2/GtkDasherControl.h               |    2 +-
 Src/Gtk2/SpeechDispatcher.cpp             |    4 +-
 Src/Gtk2/dasher_editor.cpp                |   10 ++-
 Src/Gtk2/dasher_editor.h                  |    2 +-
 Src/Gtk2/dasher_editor_external.h         |    4 +-
 Src/Gtk2/dasher_editor_external_atspi.cpp |  165 ++++++++++++++++++++++++++++-
 Src/Gtk2/dasher_editor_external_cspi.cpp  |   10 ++
 Src/Gtk2/dasher_editor_external_xtest.cpp |   10 ++
 9 files changed, 199 insertions(+), 10 deletions(-)
---
diff --git a/Src/Gtk2/GtkDasherControl.cpp b/Src/Gtk2/GtkDasherControl.cpp
index 50c0e39..1740736 100644
--- a/Src/Gtk2/GtkDasherControl.cpp
+++ b/Src/Gtk2/GtkDasherControl.cpp
@@ -231,7 +231,7 @@ gtk_dasher_control_get_all_text(GtkDasherControl *pControl) {
   return dasher_editor_get_all_text(pPrivate->pEditor);
 }
 
-const gchar* gtk_dasher_control_get_text_around_cursor(GtkDasherControl *pControl, 
CControlManager::EditDistance dist) {
+std::string gtk_dasher_control_get_text_around_cursor(GtkDasherControl *pControl, 
CControlManager::EditDistance dist) {
   GtkDasherControlPrivate *pPrivate = GTK_DASHER_CONTROL_GET_PRIVATE(pControl);
   return dasher_editor_get_text_around_cursor(pPrivate->pEditor, dist);
 }
diff --git a/Src/Gtk2/GtkDasherControl.h b/Src/Gtk2/GtkDasherControl.h
index c93bbaa..4b71bb3 100644
--- a/Src/Gtk2/GtkDasherControl.h
+++ b/Src/Gtk2/GtkDasherControl.h
@@ -73,7 +73,7 @@ void gtk_dasher_control_set_editor(GtkDasherControl *pControl, DasherEditor *pEd
 void gtk_dasher_control_clear_all_context(GtkDasherControl *pControl);
 const gchar* gtk_dasher_control_get_all_text(GtkDasherControl *pControl);
 const gchar* gtk_dasher_control_get_context(GtkDasherControl *pControl, unsigned int iOffset, unsigned int 
iLength);
-const gchar* gtk_dasher_control_get_text_around_cursor(GtkDasherControl *pControl, 
Dasher::CControlManager::EditDistance dist);
+std::string gtk_dasher_control_get_text_around_cursor(GtkDasherControl *pControl, 
Dasher::CControlManager::EditDistance dist);
 //void gtk_dasher_control_invalidate_context(GtkDasherControl *pControl, bool bForceStart);
 void gtk_dasher_control_set_buffer(GtkDasherControl *pControl, int iOffset);
 void gtk_dasher_control_set_offset(GtkDasherControl *pControl, int iOffset);
diff --git a/Src/Gtk2/SpeechDispatcher.cpp b/Src/Gtk2/SpeechDispatcher.cpp
index 81948bc..7be1e83 100644
--- a/Src/Gtk2/SpeechDispatcher.cpp
+++ b/Src/Gtk2/SpeechDispatcher.cpp
@@ -26,7 +26,9 @@ void CSpeech::Speak(const std::string &strText, bool bInterrupt) {
                return;
        }
        if (!bInterrupt) {
-               spd_say(m_speaker, SPD_TEXT, strText.c_str());
+               if (!strText.empty()) {
+                       spd_say(m_speaker, SPD_TEXT, strText.c_str());
+               }
        } else {
                spd_stop(m_speaker);
        }       
diff --git a/Src/Gtk2/dasher_editor.cpp b/Src/Gtk2/dasher_editor.cpp
index eef3ba8..1382a19 100644
--- a/Src/Gtk2/dasher_editor.cpp
+++ b/Src/Gtk2/dasher_editor.cpp
@@ -438,8 +438,11 @@ dasher_editor_get_context(DasherEditor *pSelf, int iOffset, int iLength) {
   return gtk_text_buffer_get_text( pPrivate->pBuffer, &start, &end, false );
 }
 
-const gchar* dasher_editor_get_text_around_cursor(DasherEditor *pSelf, Dasher::CControlManager::EditDistance 
distance) {
+std::string dasher_editor_get_text_around_cursor(DasherEditor *pSelf, Dasher::CControlManager::EditDistance 
distance) {
   DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+  if (isdirect(pPrivate->pAppSettings))
+    return dasher_editor_external_get_text_around_cursor(pSelf, distance);
+
   // TODO: handle the external editor.
   GtkTextIter end_pos, start_pos;
   gtk_text_buffer_get_iter_at_mark(pPrivate->pBuffer, &end_pos, 
gtk_text_buffer_get_insert(pPrivate->pBuffer));
@@ -1219,6 +1222,11 @@ dasher_editor_ctrl_delete(DasherEditor *pSelf, bool bForwards, Dasher::CControlM
 gint
 dasher_editor_ctrl_move(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance iDist) {
   DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+
+  if (isdirect(pPrivate->pAppSettings)) {
+    dasher_editor_external_move(pSelf, bForwards, iDist);
+    return 0;
+  }
   GtkTextIter sPos;
 
   gtk_text_buffer_get_iter_at_mark(pPrivate->pBuffer, &sPos, gtk_text_buffer_get_insert(pPrivate->pBuffer));
diff --git a/Src/Gtk2/dasher_editor.h b/Src/Gtk2/dasher_editor.h
index 3efdc25..c63f288 100644
--- a/Src/Gtk2/dasher_editor.h
+++ b/Src/Gtk2/dasher_editor.h
@@ -77,7 +77,7 @@ void dasher_editor_end_compose(DasherEditor *pSelf, bool bKeep);
 
 /* Function for reading the active buffer */
 const gchar *dasher_editor_get_context(DasherEditor *pSelf, int iOffset, int iLength);
-const gchar* dasher_editor_get_text_around_cursor(DasherEditor *pSelf, Dasher::CControlManager::EditDistance 
dist);
+std::string dasher_editor_get_text_around_cursor(DasherEditor *pSelf, Dasher::CControlManager::EditDistance 
dist);
 gint dasher_editor_get_offset(DasherEditor *pSelf);
 
 gint dasher_editor_ctrl_move(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance 
dist);
diff --git a/Src/Gtk2/dasher_editor_external.h b/Src/Gtk2/dasher_editor_external.h
index 1067743..e3180bf 100644
--- a/Src/Gtk2/dasher_editor_external.h
+++ b/Src/Gtk2/dasher_editor_external.h
@@ -10,5 +10,7 @@ void dasher_editor_external_delete(DasherEditor *pSelf, int iLength, int iOffset
 const char * dasher_editor_external_get_context(DasherEditor *pSelf, int iOffset, int iLength);
 int dasher_editor_external_get_offset(DasherEditor *pSelf);
 void dasher_editor_external_toggle_direct_mode(DasherEditor *, bool);
-
+void dasher_editor_external_move(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance 
iDist);
+std::string dasher_editor_external_get_text_around_cursor(
+    DasherEditor *pSelf, Dasher::CControlManager::EditDistance distance);
 #endif
diff --git a/Src/Gtk2/dasher_editor_external_atspi.cpp b/Src/Gtk2/dasher_editor_external_atspi.cpp
index 176e2f2..09d0d92 100644
--- a/Src/Gtk2/dasher_editor_external_atspi.cpp
+++ b/Src/Gtk2/dasher_editor_external_atspi.cpp
@@ -1,4 +1,5 @@
 #include <X11/keysym.h>
+#include <string.h>
 
 extern "C" {
   #include <atspi/atspi.h>
@@ -13,6 +14,10 @@ struct _DasherEditorExternalPrivate {
   AtspiEventListener *pFocusListener;
   AtspiEventListener *pCaretListener;
   AtspiText *pAccessibleText;
+  // Used to distinguish dasher initiated cursor moves from external moves so that the
+  // interface stays in control mode. When dasher moves the caret it will also set
+  // current_caret_position.
+  glong current_caret_position;
 };
 
 void dasher_editor_external_handle_focus(DasherEditor *pSelf, const AtspiEvent *pEvent);
@@ -69,6 +74,7 @@ dasher_editor_external_create_buffer(DasherEditor *pSelf) {
   p->pFocusListener = atspi_event_listener_new(focus_listener, pSelf, NULL);
   p->pCaretListener = atspi_event_listener_new(caret_listener, pSelf, NULL);
   p->pAccessibleText = NULL;
+  p->current_caret_position = -1;
   pPrivate->pExtPrivate = p;
 
 #ifdef DEBUG_ATSPI
@@ -79,19 +85,23 @@ dasher_editor_external_create_buffer(DasherEditor *pSelf) {
 static void
 listen_to_bus(DasherEditor *pSelf) {
   DasherEditorPrivate *p = DASHER_EDITOR_GET_PRIVATE(pSelf);
-  atspi_event_listener_register(p->pExtPrivate->pFocusListener, "focus:", NULL);
+  //atspi_event_listener_register(p->pExtPrivate->pFocusListener, "focus:", NULL);
+  atspi_event_listener_register(p->pExtPrivate->pFocusListener, "object:state-changed:focused", NULL);
   atspi_event_listener_register(p->pExtPrivate->pCaretListener, "object:text-caret-moved", NULL);
 }
 
 static void
 unlisten_to_bus(DasherEditor *pSelf) {
   DasherEditorPrivate *p = DASHER_EDITOR_GET_PRIVATE(pSelf);
-  atspi_event_listener_deregister(p->pExtPrivate->pFocusListener, "focus:", NULL);
+//  atspi_event_listener_deregister(p->pExtPrivate->pFocusListener, "focus:", NULL);
+  atspi_event_listener_deregister(p->pExtPrivate->pFocusListener, "object:state-changed:focused", NULL);
   atspi_event_listener_deregister(p->pExtPrivate->pCaretListener, "object:text-caret-moved", NULL);
 }
 
 void
 dasher_editor_external_toggle_direct_mode(DasherEditor *pSelf, bool direct) {
+  DasherEditorPrivate *p = DASHER_EDITOR_GET_PRIVATE(pSelf);
+  p->pExtPrivate->current_caret_position = -1;
   if (direct)
     listen_to_bus(pSelf);
   else
@@ -101,7 +111,6 @@ dasher_editor_external_toggle_direct_mode(DasherEditor *pSelf, bool direct) {
 void
 dasher_editor_external_output(DasherEditor *pSelf, const char *szText, int iOffset /* unused */) {
   if (!initSPI()) return;
-
   atspi_generate_keyboard_event(0, szText, ATSPI_KEY_STRING, NULL);
 }
 
@@ -168,6 +177,7 @@ dasher_editor_external_handle_focus(DasherEditor *pSelf, const AtspiEvent *pEven
 #endif
 
   DASHER_ASSERT(pPrivate->pExtPrivate != NULL);
+  pPrivate->pExtPrivate->current_caret_position = -1;
   AtspiText *textobj = pPrivate->pExtPrivate->pAccessibleText;
 
   if (textobj) {
@@ -186,7 +196,7 @@ dasher_editor_external_handle_focus(DasherEditor *pSelf, const AtspiEvent *pEven
     atspi_accessible_get_description(acc, NULL));
 #endif
 
-  textobj = atspi_accessible_get_text(acc);
+  textobj = atspi_accessible_get_text_iface(acc);
   pPrivate->pExtPrivate->pAccessibleText = textobj;
   if (textobj) {
     g_object_ref(textobj);
@@ -234,6 +244,14 @@ dasher_editor_external_handle_caret(DasherEditor *pSelf, const AtspiEvent *pEven
   textobj = atspi_accessible_get_text(acc);
   pPrivate->pExtPrivate->pAccessibleText = textobj;
   if (textobj) {
+    // If dasher moved the caret don't send a notification to the control.
+    glong caret = atspi_text_get_caret_offset(textobj, NULL);
+    bool in_control_action = false;
+    if (caret == pPrivate->pExtPrivate->current_caret_position) {
+      return;
+    }
+    pPrivate->pExtPrivate->current_caret_position = -1;
+
     g_object_ref(textobj);
 
     //ACL: in old code, external_buffer emitted signal, for which the editor_external had
@@ -244,6 +262,7 @@ dasher_editor_external_handle_caret(DasherEditor *pSelf, const AtspiEvent *pEven
     // ...if that makes any sense?
 
     g_signal_emit_by_name(G_OBJECT(pSelf), "context_changed", G_OBJECT(pSelf), NULL, NULL);
+    pPrivate->bInControlAction = false;
   }
 #ifdef DEBUG_ATSPI
   else {
@@ -263,3 +282,141 @@ void
 caret_listener(AtspiEvent *pEvent, void *pUserData) {
   dasher_editor_external_handle_caret(DASHER_EDITOR(pUserData), pEvent);
 }
+
+void dasher_editor_external_move(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance 
iDist) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+  DASHER_ASSERT(pPrivate->pExtPrivate != nullptr);
+  AtspiText *textobj = pPrivate->pExtPrivate->pAccessibleText;
+  if (textobj == nullptr) {
+    return;
+  }
+
+  GError *err = nullptr;
+  auto caret_pos = atspi_text_get_caret_offset(textobj, &err);
+  if (err != nullptr) {
+    fprintf(stderr, "Failed to get the caret: %s\n", err->message);
+    g_error_free(err);
+    return;
+  }
+
+  auto length = atspi_text_get_character_count(textobj, &err);
+  if (err != nullptr) {
+    fprintf(stderr, "Failed to get the character count: %s\n", err->message);
+    g_error_free(err);
+    return;
+  }
+  if (length == 0) {
+    return;
+  }
+
+  AtspiTextRange* range = nullptr;
+  glong new_position = caret_pos;
+
+  AtspiTextBoundaryType boundary = ATSPI_TEXT_BOUNDARY_CHAR;
+  switch (iDist) {
+    case Dasher::CControlManager::EDIT_CHAR:
+      if (bForwards) {
+        new_position = std::min<glong>(caret_pos + 1, length - 1);
+      } else {
+        new_position = std::max<glong>(caret_pos - 1, 0);
+      }
+      break;
+    case Dasher::CControlManager::EDIT_FILE:
+      if (bForwards) {
+        new_position = length - 1;
+      } else {
+        new_position = 0;
+      }
+      break;
+    case Dasher::CControlManager::EDIT_WORD:
+      boundary = ATSPI_TEXT_BOUNDARY_WORD_START;
+      break;
+    case Dasher::CControlManager::EDIT_LINE:
+      boundary = ATSPI_TEXT_BOUNDARY_LINE_START;
+      break;
+    case Dasher::CControlManager::EDIT_SENTENCE:
+      boundary = ATSPI_TEXT_BOUNDARY_SENTENCE_START;
+      break;
+    default:
+      break;
+  }
+  if (boundary != ATSPI_TEXT_BOUNDARY_CHAR) {
+    if (bForwards) {
+      range = atspi_text_get_text_after_offset(textobj, caret_pos, boundary, &err);
+    } else {
+      range = atspi_text_get_text_before_offset(textobj, caret_pos, boundary, &err);
+    }
+    if (err != nullptr) {
+      fprintf(stderr, "Failed to get the text after/befor the offset: %s\n", err->message);
+      g_error_free(err);
+      return;
+    }
+    if (range != nullptr) {
+      new_position = range->start_offset;
+    }
+    g_free(range);
+  }
+  atspi_text_set_caret_offset(textobj, new_position, &err);
+  if (err != nullptr) {
+    fprintf(stderr, "Failed to set the caret offset: %s\n", err->message);
+    g_error_free(err);
+    return;
+  }
+  pPrivate->pExtPrivate->current_caret_position = new_position;
+}
+
+std::string dasher_editor_external_get_text_around_cursor(
+    DasherEditor *pSelf, Dasher::CControlManager::EditDistance distance) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+  DASHER_ASSERT(pPrivate->pExtPrivate != nullptr);
+  AtspiText *textobj = pPrivate->pExtPrivate->pAccessibleText;
+  if (textobj == nullptr) {
+    return "";
+  }
+  GError *err = nullptr;
+  auto caret_pos = atspi_text_get_caret_offset(textobj, &err);
+  if (err != nullptr) {
+    fprintf(stderr, "Failed to get the caret offset: %s\n", err->message);
+    g_error_free(err);
+    return "";
+  }
+
+  std::string text;
+  AtspiTextGranularity granularity;
+  switch (distance) {
+    case Dasher::CControlManager::EDIT_FILE: {
+      auto length = atspi_text_get_character_count(textobj, nullptr);
+      auto gtext = atspi_text_get_text(textobj, 0, length, nullptr);
+      text = gtext;
+      g_free(gtext);
+      return text;
+    }
+      break;
+    case Dasher::CControlManager::EDIT_WORD:
+      granularity = ATSPI_TEXT_GRANULARITY_WORD;
+      break;
+    case Dasher::CControlManager::EDIT_LINE:
+      granularity = ATSPI_TEXT_GRANULARITY_LINE;
+      break;
+    case Dasher::CControlManager::EDIT_SENTENCE:
+      granularity = ATSPI_TEXT_GRANULARITY_SENTENCE;
+      break;
+    case Dasher::CControlManager::EDIT_PARAGRAPH:
+      // TODO: figure out why ATSPI_TEXT_GRANULARITY_PARAGRAPH doesn't work.
+      granularity = ATSPI_TEXT_GRANULARITY_PARAGRAPH;
+      break;
+    default:
+      return "";
+  }
+  auto* range = atspi_text_get_string_at_offset(textobj, caret_pos, granularity, &err);
+  if (err != nullptr) {
+    fprintf(stderr, "Failed to get the caret offset: %s\n", err->message);
+    g_error_free(err);
+    return "";
+  }
+  if (range != nullptr) {
+    text = range->content;
+    g_free(range);
+  }
+  return text;
+}
diff --git a/Src/Gtk2/dasher_editor_external_cspi.cpp b/Src/Gtk2/dasher_editor_external_cspi.cpp
index 8b8c72b..479eea6 100644
--- a/Src/Gtk2/dasher_editor_external_cspi.cpp
+++ b/Src/Gtk2/dasher_editor_external_cspi.cpp
@@ -240,3 +240,13 @@ void focus_listener(const AccessibleEvent *pEvent, void *pUserData) {
 void caret_listener(const AccessibleEvent *pEvent, void *pUserData) {
   dasher_editor_external_handle_caret((DasherEditor *)pUserData, pEvent);
 }
+
+void dasher_editor_external_move(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance 
iDist) {
+  // TODO: implement.
+}
+
+std::string dasher_editor_external_get_text_around_cursor(
+    DasherEditor *pSelf, Dasher::CControlManager::EditDistance distance) {
+  // TODO: implement.
+  return "";
+}
diff --git a/Src/Gtk2/dasher_editor_external_xtest.cpp b/Src/Gtk2/dasher_editor_external_xtest.cpp
index a342cd9..361d3b3 100644
--- a/Src/Gtk2/dasher_editor_external_xtest.cpp
+++ b/Src/Gtk2/dasher_editor_external_xtest.cpp
@@ -147,3 +147,13 @@ gint
 dasher_editor_external_get_offset(DasherEditor *pSelf) {
   return 0;
 }
+
+void dasher_editor_external_move(DasherEditor *pSelf, bool bForwards, Dasher::CControlManager::EditDistance 
iDist) {
+  // TODO: implement.
+}
+
+std::string dasher_editor_external_get_text_around_cursor(
+    DasherEditor *pSelf, Dasher::CControlManager::EditDistance distance) {
+  // TODO: implement.
+  return "";
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]