Back to index

unity  6.0.0
nux-area-accessible.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License version 3 as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Alejandro PiƱeiro Iglesias <apinheiro@igalia.com>
00017  */
00018 
00055 #include "nux-area-accessible.h"
00056 
00057 #include "unitya11y.h"
00058 
00059 /* GObject */
00060 static void nux_area_accessible_class_init(NuxAreaAccessibleClass* klass);
00061 static void nux_area_accessible_init(NuxAreaAccessible* area_accessible);
00062 
00063 /* AtkObject.h */
00064 static void         nux_area_accessible_initialize(AtkObject* accessible,
00065                                                    gpointer   data);
00066 static AtkObject*   nux_area_accessible_get_parent(AtkObject* obj);
00067 static AtkStateSet* nux_area_accessible_ref_state_set(AtkObject* obj);
00068 
00069 /* AtkComponent.h */
00070 static void         atk_component_interface_init(AtkComponentIface* iface);
00071 static void         nux_area_accessible_get_extents(AtkComponent* component,
00072                                                     gint*         x,
00073                                                     gint*         y,
00074                                                     gint*         width,
00075                                                     gint*         height,
00076                                                     AtkCoordType  coord_type);
00077 static gboolean     nux_area_accessible_grab_focus(AtkComponent* component);
00078 static guint        nux_area_accessible_add_focus_handler(AtkComponent* component,
00079                                                           AtkFocusHandler handler);
00080 static void         nux_area_accessible_remove_focus_handler(AtkComponent* component,
00081                                                              guint handler_id);
00082 static void         nux_area_accessible_focus_handler(AtkObject* accessible,
00083                                                       gboolean focus_in);
00084 /* private */
00085 static void          on_focus_changed_cb(nux::Area* area,
00086                                          bool has_focus,
00087                                          nux::KeyNavDirection direction,
00088                                          AtkObject* accessible);
00089 static void          on_parent_window_activate_cb(AtkObject* parent_window,
00090                                                   NuxAreaAccessible* self);
00091 static AtkObject*    search_for_parent_window(AtkObject* object);
00092 static gboolean      nux_area_accessible_real_check_pending_notification(NuxAreaAccessible* self);
00093 static void          check_parent_window_connected(NuxAreaAccessible* self);
00094 static void          check_focus(NuxAreaAccessible* self);
00095 
00096 G_DEFINE_TYPE_WITH_CODE(NuxAreaAccessible,
00097                         nux_area_accessible,
00098                         NUX_TYPE_OBJECT_ACCESSIBLE,
00099                         G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT,
00100                                               atk_component_interface_init))
00101 
00102 #define NUX_AREA_ACCESSIBLE_GET_PRIVATE(obj) \
00103   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NUX_TYPE_AREA_ACCESSIBLE,        \
00104                                 NuxAreaAccessiblePrivate))
00105 
00106 struct _NuxAreaAccessiblePrivate
00107 {
00108   /* focused as Focusable events */
00109   gboolean focused;
00110 
00111   /* if there is any pending notification */
00112   gboolean pending_notification;
00113 
00114   /* Top level parent window, it is not required to be the direct
00115      parent */
00116   AtkObject* parent_window;
00117 };
00118 
00119 
00120 static void
00121 nux_area_accessible_class_init(NuxAreaAccessibleClass* klass)
00122 {
00123   GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
00124   AtkObjectClass* atk_class = ATK_OBJECT_CLASS(klass);
00125   NuxAreaAccessibleClass* area_class = NUX_AREA_ACCESSIBLE_CLASS(klass);
00126 
00127   /* AtkObject */
00128   atk_class->initialize = nux_area_accessible_initialize;
00129   atk_class->get_parent = nux_area_accessible_get_parent;
00130   atk_class->ref_state_set = nux_area_accessible_ref_state_set;
00131 
00132   /* NuxAreaAccessible */
00133   area_class->check_pending_notification = nux_area_accessible_real_check_pending_notification;
00134 
00135   g_type_class_add_private(gobject_class, sizeof(NuxAreaAccessiblePrivate));
00136 }
00137 
00138 static void
00139 nux_area_accessible_init(NuxAreaAccessible* area_accessible)
00140 {
00141   NuxAreaAccessiblePrivate* priv =
00142     NUX_AREA_ACCESSIBLE_GET_PRIVATE(area_accessible);
00143 
00144   area_accessible->priv = priv;
00145 }
00146 
00147 AtkObject*
00148 nux_area_accessible_new(nux::Object* object)
00149 {
00150   AtkObject* accessible = NULL;
00151 
00152   g_return_val_if_fail(dynamic_cast<nux::Area*>(object), NULL);
00153 
00154   accessible = ATK_OBJECT(g_object_new(NUX_TYPE_AREA_ACCESSIBLE, NULL));
00155 
00156   atk_object_initialize(accessible, object);
00157 
00158   return accessible;
00159 }
00160 
00161 /* AtkObject.h */
00162 static void
00163 nux_area_accessible_initialize(AtkObject* accessible,
00164                                gpointer data)
00165 {
00166   nux::Object* nux_object = NULL;
00167   nux::Area* area = NULL;
00168 
00169   ATK_OBJECT_CLASS(nux_area_accessible_parent_class)->initialize(accessible, data);
00170 
00171   accessible->role = ATK_ROLE_UNKNOWN;
00172 
00173   nux_object = nux_object_accessible_get_object(NUX_OBJECT_ACCESSIBLE(accessible));
00174   area = dynamic_cast<nux::Area*>(nux_object);
00175 
00176   /* focus support based on Focusable, used on the Dash */
00177   area->key_nav_focus_change.connect(sigc::bind(sigc::ptr_fun(on_focus_changed_cb), accessible));
00178 
00179   atk_component_add_focus_handler(ATK_COMPONENT(accessible),
00180                                   nux_area_accessible_focus_handler);
00181 
00182   /* NOTE: we can't search for the parent window on initilization as a
00183      general rule, or we could enter on a infinite loop. At area this
00184      is done on the focus event. On the Switcher this is done on their
00185      initialize itself */
00186 }
00187 
00188 static AtkObject*
00189 nux_area_accessible_get_parent(AtkObject* obj)
00190 {
00191   nux::Object* nux_object = NULL;
00192   nux::Area* area = NULL;
00193   nux::Area* parent = NULL;
00194 
00195   g_return_val_if_fail(NUX_IS_AREA_ACCESSIBLE(obj), NULL);
00196 
00197   if (obj->accessible_parent)
00198     return obj->accessible_parent;
00199 
00200   nux_object = nux_object_accessible_get_object(NUX_OBJECT_ACCESSIBLE(obj));
00201 
00202   if (nux_object == NULL) /* defunct */
00203     return NULL;
00204 
00205   area = dynamic_cast<nux::Area*>(nux_object);
00206 
00207   parent = area->GetParentObject();
00208 
00209   return unity_a11y_get_accessible(parent);
00210 }
00211 
00212 /*
00213  * Checks if the parent actor, and his parent, etc is all visible
00214  * Used to check the showing state
00215  */
00216 static gboolean
00217 _check_all_parents_visible(nux::Area* area)
00218 {
00219   nux::Area* iter_parent = NULL;
00220   gboolean result = TRUE;
00221 
00222   for (iter_parent = area; iter_parent;
00223        iter_parent = iter_parent->GetParentObject())
00224   {
00225     if (!iter_parent->IsVisible())
00226     {
00227       result = FALSE;
00228       break;
00229     }
00230   }
00231 
00232   return result;
00233 }
00234 
00235 static AtkStateSet*
00236 nux_area_accessible_ref_state_set(AtkObject* obj)
00237 {
00238   AtkStateSet* state_set = NULL;
00239   nux::Object* nux_object = NULL;
00240   nux::Area* area = NULL;
00241 
00242   g_return_val_if_fail(NUX_IS_AREA_ACCESSIBLE(obj), NULL);
00243 
00244   state_set = ATK_OBJECT_CLASS(nux_area_accessible_parent_class)->ref_state_set(obj);
00245 
00246   nux_object = nux_object_accessible_get_object(NUX_OBJECT_ACCESSIBLE(obj));
00247 
00248   if (nux_object == NULL) /* defunct */
00249     return state_set;
00250 
00251   area = dynamic_cast<nux::Area*>(nux_object);
00252 
00253   if (area->IsSensitive())
00254   {
00255     atk_state_set_add_state(state_set, ATK_STATE_SENSITIVE);
00256     atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
00257   }
00258 
00259   if (area->IsVisible())
00260   {
00261     atk_state_set_add_state(state_set, ATK_STATE_VISIBLE);
00262 
00263     if (_check_all_parents_visible(area))
00264       atk_state_set_add_state(state_set, ATK_STATE_SHOWING);
00265   }
00266 
00267   // FIXME CanFocus is no longer part of Nux API
00268 //  if (area->CanFocus())
00269 //    atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE);
00270 
00271   if (area->HasKeyFocus())
00272     atk_state_set_add_state(state_set, ATK_STATE_FOCUSED);
00273 
00274   return state_set;
00275 }
00276 
00277 /* AtkComponent implementation */
00278 static void
00279 atk_component_interface_init(AtkComponentIface* iface)
00280 {
00281   g_return_if_fail(iface != NULL);
00282 
00283   /* placement */
00284   iface->get_extents    = nux_area_accessible_get_extents;
00285 
00286   /* focus management based on Focusable */
00287   iface->grab_focus           = nux_area_accessible_grab_focus;
00288   iface->add_focus_handler    = nux_area_accessible_add_focus_handler;
00289   iface->remove_focus_handler = nux_area_accessible_remove_focus_handler;
00290 }
00291 
00292 static void
00293 nux_area_accessible_get_extents(AtkComponent* component,
00294                                 gint*         x,
00295                                 gint*         y,
00296                                 gint*         width,
00297                                 gint*         height,
00298                                 AtkCoordType coord_type)
00299 {
00300   gint top_level_x = 0;
00301   gint top_level_y = 0;
00302   nux::Object* nux_object = NULL;
00303   nux::Area* area = NULL;
00304   nux::Geometry geometry;
00305 
00306   g_return_if_fail(NUX_IS_AREA_ACCESSIBLE(component));
00307 
00308   nux_object = nux_object_accessible_get_object(NUX_OBJECT_ACCESSIBLE(component));
00309 
00310   if (nux_object == NULL) /* defunct */
00311     return;
00312 
00313   area = dynamic_cast<nux::Area*>(nux_object);
00314 
00315   geometry = area->GetGeometry();
00316 
00317   *width = geometry.GetWidth();
00318   *height = geometry.GetWidth();
00319   *x = geometry.x;
00320   *y = geometry.y;
00321 
00322   /* In the ATK_XY_WINDOW case
00323    *
00324    * http://library.gnome.org/devel/atk/stable/AtkUtil.html#AtkCoordType
00325    */
00326 
00327   if (coord_type == ATK_XY_SCREEN)
00328   {
00329     /* For the moment Unity is a full-screen app, so ATK_XY_SCREEN
00330        and ATK_XY_WINDOW are the same */
00331     *x += top_level_x;
00332     *y += top_level_y;
00333   }
00334 
00335   return;
00336 }
00337 
00338 static gboolean
00339 nux_area_accessible_grab_focus(AtkComponent* component)
00340 {
00341   nux::Object* nux_object = NULL;
00342   //nux::Area* area = NULL;
00343 
00344   g_return_val_if_fail(NUX_IS_AREA_ACCESSIBLE(component), FALSE);
00345 
00346   nux_object = nux_object_accessible_get_object(NUX_OBJECT_ACCESSIBLE(component));
00347   if (nux_object == NULL) /* defunct */
00348     return FALSE;
00349 
00350   //area = dynamic_cast<nux::Area*>(nux_object);
00351 
00352   /* FIXME: SetFocused doesn't return if the force was succesful or
00353      not, we suppose that this is the case like in cally and gail */
00354 
00355   return TRUE;
00356 }
00357 
00358 /*
00359  * comment C&P from cally-actor:
00360  *
00361  * "These methods are basically taken from gail, as I don't see any
00362  * reason to modify it. It makes me wonder why it is really required
00363  * to be implemented in the toolkit"
00364  */
00365 
00366 static guint
00367 nux_area_accessible_add_focus_handler(AtkComponent* component,
00368                                       AtkFocusHandler handler)
00369 {
00370   GSignalMatchType match_type;
00371   gulong ret;
00372   guint signal_id;
00373 
00374   g_return_val_if_fail(NUX_IS_AREA_ACCESSIBLE(component), 0);
00375 
00376   match_type = (GSignalMatchType)(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC);
00377   signal_id = g_signal_lookup("focus-event", ATK_TYPE_OBJECT);
00378 
00379   ret = g_signal_handler_find(component, match_type, signal_id, 0, NULL,
00380                               (gpointer) handler, NULL);
00381   if (!ret)
00382   {
00383     return g_signal_connect_closure_by_id(component,
00384                                           signal_id, 0,
00385                                           g_cclosure_new(G_CALLBACK(handler), NULL,
00386                                                          (GClosureNotify) NULL),
00387                                           FALSE);
00388   }
00389   else
00390     return 0;
00391 }
00392 
00393 static void
00394 nux_area_accessible_remove_focus_handler(AtkComponent* component,
00395                                          guint handler_id)
00396 {
00397   g_return_if_fail(NUX_IS_AREA_ACCESSIBLE(component));
00398 
00399   g_signal_handler_disconnect(component, handler_id);
00400 }
00401 
00402 static void
00403 nux_area_accessible_focus_handler(AtkObject* accessible,
00404                                   gboolean focus_in)
00405 {
00406   g_return_if_fail(NUX_IS_AREA_ACCESSIBLE(accessible));
00407 
00408   atk_object_notify_state_change(accessible, ATK_STATE_FOCUSED, focus_in);
00409 }
00410 
00411 /* private */
00412 static void
00413 check_parent_window_connected(NuxAreaAccessible* self)
00414 {
00415   AtkObject* window = NULL;
00416 
00417   if (self->priv->parent_window != NULL)
00418     return;
00419 
00420   window = search_for_parent_window(ATK_OBJECT(self));
00421 
00422   if (window != NULL)
00423   {
00424     self->priv->parent_window = window;
00425 
00426     g_signal_connect(self->priv->parent_window,
00427                      "activate",
00428                      G_CALLBACK(on_parent_window_activate_cb),
00429                      self);
00430   }
00431 }
00432 
00433 /*
00434  * nux_area_accessible_parent_window_active
00435  * @self: The accessible to check the focus change
00436  *
00437  * Returns if the top level parent window contains
00438  * the state ATK_STATE_ACTIVE
00439  *
00440  * Returns: TRUE if the parent top level window contains
00441  * ATK_STATE_ACTIVE, FALSE otherwise
00442  */
00443 gboolean
00444 nux_area_accessible_parent_window_active(NuxAreaAccessible* self)
00445 {
00446   gboolean active = FALSE;
00447   AtkStateSet* state_set = NULL;
00448 
00449   check_parent_window_connected(self);
00450 
00451   state_set = atk_object_ref_state_set(ATK_OBJECT(self->priv->parent_window));
00452 
00453   active = atk_state_set_contains_state(state_set, ATK_STATE_ACTIVE);
00454 
00455   g_object_unref(state_set);
00456 
00457   return active;
00458 }
00459 
00460 static void
00461 on_focus_changed_cb(nux::Area* area,
00462                     bool has_focus,
00463                     nux::KeyNavDirection direction,
00464                     AtkObject* accessible)
00465 {
00466   check_focus(NUX_AREA_ACCESSIBLE(accessible));
00467 }
00468 
00469 /* Check to use GetTopLevelViewWindow */
00470 static AtkObject*
00471 search_for_parent_window(AtkObject* object)
00472 {
00473   AtkObject* parent = NULL;
00474 
00475   for (parent = atk_object_get_parent(object);
00476        (parent != NULL) && (atk_object_get_role(parent) != ATK_ROLE_WINDOW);
00477        parent = atk_object_get_parent(parent));
00478 
00479   return parent;
00480 }
00481 
00482 static void
00483 on_parent_window_activate_cb(AtkObject* parent_window,
00484                              NuxAreaAccessible* self)
00485 {
00486   nux_area_accessible_check_pending_notification(self);
00487 }
00488 
00489 
00490 /*
00491  * nux_area_check_pending_notification:
00492  * @self: The accessible
00493  *
00494  * This method checks if there is any pending notification, and emits
00495  * it if it is possible
00496  *
00497  * Returns: TRUE if a atk notification was emitted, FALSE otherwise
00498  */
00499 gboolean
00500 nux_area_accessible_check_pending_notification(NuxAreaAccessible* self)
00501 {
00502   NuxAreaAccessibleClass* klass = NULL;
00503 
00504   klass = NUX_AREA_ACCESSIBLE_GET_CLASS(self);
00505   if (klass->check_pending_notification)
00506     return klass->check_pending_notification(self);
00507   else
00508     return FALSE;
00509 }
00510 
00511 static gboolean
00512 nux_area_accessible_real_check_pending_notification(NuxAreaAccessible* self)
00513 {
00514   nux::Object* nux_object = NULL;
00515 
00516   g_return_val_if_fail(NUX_IS_AREA_ACCESSIBLE(self), FALSE);
00517 
00518   if (self->priv->pending_notification == FALSE)
00519     return FALSE;
00520 
00521   nux_object = nux_object_accessible_get_object(NUX_OBJECT_ACCESSIBLE(self));
00522   if (nux_object == NULL) /* defunct */
00523     return FALSE;
00524 
00525   g_signal_emit_by_name(self, "focus_event", self->priv->focused);
00526   atk_focus_tracker_notify(ATK_OBJECT(self));
00527   self->priv->pending_notification = FALSE;
00528 
00529   return TRUE;
00530 }
00531 
00532 static void
00533 check_focus(NuxAreaAccessible* self)
00534 {
00535   gboolean focus_in = FALSE;
00536   nux::Area* area = NULL;
00537   nux::Object* nux_object = NULL;
00538 
00539   g_return_if_fail(NUX_IS_AREA_ACCESSIBLE(self));
00540 
00541   nux_object = nux_object_accessible_get_object(NUX_OBJECT_ACCESSIBLE(self));
00542   if (nux_object == NULL) /* defunct */
00543     return;
00544 
00545   area = dynamic_cast<nux::Area*>(nux_object);
00546 
00547   if (nux::GetWindowCompositor().GetKeyFocusArea() == area)
00548     focus_in = TRUE;
00549 
00550   if (self->priv->focused != focus_in)
00551   {
00552     gboolean is_parent_window_active = FALSE;
00553 
00554     self->priv->focused = focus_in;
00555     is_parent_window_active = nux_area_accessible_parent_window_active(self);
00556 
00557     /* we don't emit focus_in=TRUE events until the top level window
00558        is active */
00559     if ((focus_in) && (!is_parent_window_active))
00560     {
00561       self->priv->pending_notification = TRUE;
00562     }
00563     else
00564     {
00565       g_signal_emit_by_name(self, "focus_event", focus_in);
00566       atk_focus_tracker_notify(ATK_OBJECT(self));
00567       self->priv->pending_notification = FALSE;
00568     }
00569   }
00570 }
00571 
00572 
00573 /* public */
00574 /*
00575  * nux_area_get_parent_window:
00576  * @self: The accessible
00577  *
00578  * Returns: the top level window that contains this object
00579  */
00580 AtkObject*
00581 nux_area_accessible_get_parent_window(NuxAreaAccessible* self)
00582 {
00583   g_return_val_if_fail(NUX_IS_AREA_ACCESSIBLE(self), NULL);
00584 
00585   /* Ensures that at least whe made a search for it */
00586   check_parent_window_connected(self);
00587 
00588   return self->priv->parent_window;
00589 }