1 /******************************************************************************* 2 * Copyright (c) 2000, 2007 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Port to the D programming language: 11 * Frank Benoit <benoit@tionex.de> 12 *******************************************************************************/ 13 module org.eclipse.swt.accessibility.AccessibleObject; 14 15 import org.eclipse.swt.internal.accessibility.gtk.ATK; 16 import org.eclipse.swt.internal.gtk.OS; 17 import org.eclipse.swt.internal.LONG; 18 import org.eclipse.swt.accessibility.Accessible; 19 import org.eclipse.swt.accessibility.AccessibleListener; 20 import org.eclipse.swt.accessibility.AccessibleControlListener; 21 import org.eclipse.swt.accessibility.AccessibleTextListener; 22 import org.eclipse.swt.accessibility.AccessibleEvent; 23 import org.eclipse.swt.accessibility.AccessibleControlEvent; 24 import org.eclipse.swt.accessibility.AccessibleTextEvent; 25 import org.eclipse.swt.accessibility.ACC; 26 import org.eclipse.swt.accessibility.AccessibleFactory; 27 import org.eclipse.swt.widgets.Display; 28 import java.lang.all; 29 import java.util.Vector; 30 import java.util.Hashtable; 31 import java.util.Enumeration; 32 version(Tango){ 33 //import tango.text.Util; 34 } else { // Phobos 35 } 36 37 class AccessibleObject { 38 AtkObject* handle; 39 int parentType; 40 int index = -1, id = ACC.CHILDID_SELF; 41 Accessible accessible; 42 AccessibleObject parent; 43 Hashtable children; 44 /* 45 * a lightweight object does not correspond to a concrete gtk widget, but 46 * to a logical child of a widget (eg.- a CTabItem, which is simply drawn) 47 */ 48 bool isLightweight = false; 49 50 static String actionNamePtr; 51 static String descriptionPtr; 52 static String keybindingPtr; 53 static String namePtr; 54 static AccessibleObject[AtkObject*] AccessibleObjects; 55 static /*const*/ ptrdiff_t ATK_ACTION_TYPE; 56 static /*const*/ ptrdiff_t ATK_COMPONENT_TYPE; 57 static /*const*/ ptrdiff_t ATK_HYPERTEXT_TYPE; 58 static /*const*/ ptrdiff_t ATK_SELECTION_TYPE; 59 static /*const*/ ptrdiff_t ATK_TEXT_TYPE; 60 static /*const*/ bool DEBUG; 61 static bool static_this_completed = false; 62 63 package static void static_this() { 64 if( static_this_completed ) return; 65 DEBUG = Display.DEBUG; 66 ATK_ACTION_TYPE = ATK.g_type_from_name ("AtkAction"); 67 ATK_COMPONENT_TYPE = ATK.g_type_from_name ("AtkComponent"); 68 ATK_HYPERTEXT_TYPE = ATK.g_type_from_name ("AtkHypertext"); 69 ATK_SELECTION_TYPE = ATK.g_type_from_name ("AtkSelection"); 70 ATK_TEXT_TYPE = ATK.g_type_from_name ("AtkText"); 71 static_this_completed = true; 72 } 73 74 this (int type, GtkWidget* widget, Accessible accessible, int parentType, bool isLightweight) { 75 children = new Hashtable(9); 76 handle = cast(AtkObject*)ATK.g_object_new (type, null); 77 this.parentType = parentType; 78 ATK.atk_object_initialize (handle, widget); 79 this.accessible = accessible; 80 this.isLightweight = isLightweight; 81 AccessibleObjects[handle] = this; 82 if (DEBUG) getDwtLogger().info( __FILE__, __LINE__, "new AccessibleObject: {}", handle); 83 } 84 85 void addChild (AccessibleObject child) { 86 children.put(new LONG(cast(int)child.handle), child); 87 child.setParent (this); 88 } 89 90 package static extern(C) char* atkAction_get_keybinding (void* obj, int index) { 91 auto atkObject = cast(AtkObject*)obj; 92 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkAction_get_keybinding"); 93 AccessibleObject object = getAccessibleObject (atkObject); 94 if (object is null) return null; 95 char* parentResult; 96 if (ATK.g_type_is_a (object.parentType, ATK_ACTION_TYPE)) { 97 auto superType = cast(AtkActionIface*)ATK.g_type_interface_peek_parent (ATK.ATK_ACTION_GET_IFACE (object.handle)); 98 AtkActionIface* actionIface = superType; 99 if (actionIface.get_keybinding !is null) { 100 parentResult = actionIface.get_keybinding( object.handle, index ); 101 } 102 } 103 AccessibleListener[] listeners = object.getAccessibleListeners (); 104 if (listeners.length is 0) return parentResult; 105 106 AccessibleEvent event = new AccessibleEvent (object); 107 event.childID = object.id; 108 if (parentResult !is null) { 109 String res = fromStringz( parentResult )._idup(); 110 event.result = res; 111 } 112 for (int i = 0; i < listeners.length; i++) { 113 listeners [i].getKeyboardShortcut (event); 114 } 115 if (event.result is null) return parentResult; 116 if (keybindingPtr !is null ) OS.g_free (keybindingPtr.ptr); 117 String name = event.result._idup() ~ '\0'; 118 char* p = cast(char*) OS.g_malloc (name.length); 119 keybindingPtr = p ? cast(String)p[ 0 .. name.length ] : null; 120 return cast(char*)keybindingPtr.ptr; 121 } 122 123 package static extern(C) char* atkAction_get_name (void* obj, int index) { 124 auto atkObject = cast(AtkObject*)obj; 125 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkAction_get_name"); 126 AccessibleObject object = getAccessibleObject (atkObject); 127 if (object is null) return null; 128 char* parentResult; 129 if (ATK.g_type_is_a (object.parentType, ATK_ACTION_TYPE)) { 130 auto actionIface = cast(AtkActionIface*)ATK.g_type_interface_peek_parent (ATK.ATK_ACTION_GET_IFACE (object.handle)); 131 if (actionIface.get_name !is null) { 132 parentResult = actionIface.get_name( object.handle, index); 133 } 134 } 135 AccessibleControlListener[] listeners = object.getControlListeners (); 136 if (listeners.length is 0) return parentResult; 137 138 AccessibleControlEvent event = new AccessibleControlEvent (object); 139 event.childID = object.id; 140 if (parentResult !is null) { 141 auto res = fromStringz( parentResult ); 142 event.result = res._idup(); 143 } 144 for (int i = 0; i < listeners.length; i++) { 145 listeners [i].getDefaultAction (event); 146 } 147 if (event.result is null) return parentResult; 148 if (actionNamePtr !is null) OS.g_free (actionNamePtr.ptr); 149 150 String name = event.result._idup() ~ '\0'; 151 auto p = cast(char*)OS.g_malloc (name.length); 152 actionNamePtr = p ? cast(String)p[ 0 .. name.length ] : null; 153 return cast(char*)actionNamePtr.ptr; 154 } 155 156 package static extern(C) void atkComponent_get_extents (void* obj, int* x, int* y, int* width, int* height, int coord_type) { 157 auto atkObject = cast(AtkObject*)obj; 158 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkComponent_get_extents"); 159 AccessibleObject object = getAccessibleObject (atkObject); 160 if (object is null) return; 161 *x = 0; 162 *y = 0; 163 *width = 0; 164 *height = 0; 165 if (ATK.g_type_is_a (object.parentType, ATK_COMPONENT_TYPE)) { 166 auto componentIface = cast(AtkComponentIface*) ATK.g_type_interface_peek_parent (ATK.ATK_COMPONENT_GET_IFACE (object.handle)); 167 if (componentIface.get_extents !is null) { 168 componentIface.get_extents( object.handle, x, y, width, height, coord_type); 169 } 170 } 171 AccessibleControlListener[] listeners = object.getControlListeners (); 172 if (listeners.length is 0) return; 173 174 int parentX = *x, parentY = *y; 175 int parentWidth = *width, parentHeight = *height; 176 AccessibleControlEvent event = new AccessibleControlEvent (object); 177 event.childID = object.id; 178 event.x = parentX; event.y = parentY; 179 event.width = parentWidth; event.height = parentHeight; 180 if (coord_type is ATK.ATK_XY_WINDOW) { 181 /* translate control -> display, for filling in event to be dispatched */ 182 auto gtkAccessible = ATK.GTK_ACCESSIBLE (object.handle); 183 auto topLevel = ATK.gtk_widget_get_toplevel (gtkAccessible.widget); 184 auto window = OS.GTK_WIDGET_WINDOW (topLevel); 185 int topWindowX, topWindowY; 186 OS.gdk_window_get_origin (window, &topWindowX, &topWindowY); 187 event.x += topWindowX; 188 event.y += topWindowY; 189 } 190 for (int i = 0; i < listeners.length; i++) { 191 listeners [i].getLocation (event); 192 } 193 if (coord_type is ATK.ATK_XY_WINDOW) { 194 /* translate display -> control, for answering to the OS */ 195 auto gtkAccessible = ATK.GTK_ACCESSIBLE (object.handle); 196 auto topLevel = ATK.gtk_widget_get_toplevel (gtkAccessible.widget); 197 auto window = OS.GTK_WIDGET_WINDOW (topLevel); 198 int topWindowX, topWindowY; 199 OS.gdk_window_get_origin (window, &topWindowX, &topWindowY); 200 event.x -= topWindowX; 201 event.y -= topWindowY; 202 } 203 *x = event.x; 204 *y = event.y; 205 *width = event.width; 206 *height = event.height; 207 //return 0; 208 } 209 210 package static extern(C) void atkComponent_get_position (void* obj, int* x, int* y, int coord_type) { 211 auto atkObject = cast(AtkObject*)obj; 212 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkComponent_get_position, object: {} x:{} y:{} coord:{}", atkObject, x, y, coord_type); 213 AccessibleObject object = getAccessibleObject (atkObject); 214 if (object is null) return; 215 *x=0; 216 *y=0; 217 if (ATK.g_type_is_a (object.parentType, ATK_COMPONENT_TYPE)) { 218 auto componentIface = cast(AtkComponentIface*)ATK.g_type_interface_peek_parent (ATK.ATK_COMPONENT_GET_IFACE (object.handle)); 219 if (componentIface.get_extents !is null) { 220 componentIface.get_position( object.handle, x, y, coord_type); 221 } 222 } 223 AccessibleControlListener[] listeners = object.getControlListeners (); 224 if (listeners.length is 0) return; 225 226 int parentX, parentY; 227 parentX = *x; 228 parentY = *y; 229 AccessibleControlEvent event = new AccessibleControlEvent (object); 230 event.childID = object.id; 231 event.x = parentX; event.y = parentY; 232 if (coord_type is ATK.ATK_XY_WINDOW) { 233 /* translate control -> display, for filling in event to be dispatched */ 234 auto gtkAccessible = ATK.GTK_ACCESSIBLE (object.handle); 235 auto topLevel = ATK.gtk_widget_get_toplevel (gtkAccessible.widget); 236 auto window = OS.GTK_WIDGET_WINDOW (topLevel); 237 int topWindowX, topWindowY; 238 OS.gdk_window_get_origin (window, &topWindowX, &topWindowY); 239 event.x += topWindowX; 240 event.y += topWindowY; 241 } 242 for (int i = 0; i < listeners.length; i++) { 243 listeners [i].getLocation (event); 244 } 245 if (coord_type is ATK.ATK_XY_WINDOW) { 246 /* translate display -> control, for answering to the OS */ 247 auto gtkAccessible = ATK.GTK_ACCESSIBLE (object.handle); 248 auto topLevel = ATK.gtk_widget_get_toplevel (gtkAccessible.widget); 249 auto window = OS.GTK_WIDGET_WINDOW (topLevel); 250 int topWindowX, topWindowY; 251 OS.gdk_window_get_origin (window, &topWindowX, &topWindowY); 252 event.x -= topWindowX; 253 event.y -= topWindowY; 254 } 255 *x=event.x; 256 *y=event.y; 257 //return 0; 258 } 259 260 //PORTING_FIXME: what about the coord_type? componentIface.get_size( object.handle, width, height, coord_type); 261 //package static extern(C) void atkComponent_get_size (void* obj, int* width, int* height, int coord_type) { 262 package static extern(C) void atkComponent_get_size (void* obj, int* width, int* height) { 263 auto atkObject = cast(AtkObject*)obj; 264 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkComponent_get_size"); 265 AccessibleObject object = getAccessibleObject (atkObject); 266 if (object is null) return; 267 *width=0; 268 *height=0; 269 if (ATK.g_type_is_a (object.parentType, ATK_COMPONENT_TYPE)) { 270 auto componentIface = cast(AtkComponentIface*)ATK.g_type_interface_peek_parent (ATK.ATK_COMPONENT_GET_IFACE (object.handle)); 271 if (componentIface.get_extents !is null) { 272 //PORTING_FIXME: what about the coord_type? componentIface.get_size( object.handle, width, height, coord_type); 273 componentIface.get_size( object.handle, width, height); 274 } 275 } 276 AccessibleControlListener[] listeners = object.getControlListeners (); 277 if (listeners.length is 0) return; 278 279 int parentWidth, parentHeight; 280 parentWidth= *width; 281 parentHeight= *height; 282 AccessibleControlEvent event = new AccessibleControlEvent (object); 283 event.childID = object.id; 284 event.width = parentWidth; event.height = parentHeight; 285 for (int i = 0; i < listeners.length; i++) { 286 listeners [i].getLocation (event); 287 } 288 *width=event.width; 289 *height=event.height; 290 //return 0; 291 } 292 293 package static extern(C) AtkObject* atkComponent_ref_accessible_at_point (void* obj, int x, int y, int coord_type) { 294 auto atkObject = cast(AtkObject*)obj; 295 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkComponent_ref_accessible_at_point"); 296 AccessibleObject object = getAccessibleObject (atkObject); 297 if (object is null) return null; 298 AtkObject* parentResult; 299 if (ATK.g_type_is_a (object.parentType, ATK_COMPONENT_TYPE)) { 300 auto componentIface = cast(AtkComponentIface*)ATK.g_type_interface_peek_parent (ATK.ATK_COMPONENT_GET_IFACE (object.handle)); 301 if (componentIface.ref_accessible_at_point !is null) { 302 parentResult = componentIface.ref_accessible_at_point( object.handle, x, y, coord_type); 303 } 304 } 305 AccessibleControlListener[] listeners = object.getControlListeners (); 306 if (listeners.length is 0) return parentResult; 307 308 AccessibleControlEvent event = new AccessibleControlEvent (object); 309 event.childID = object.id; 310 event.x = x; event.y = y; 311 if (coord_type is ATK.ATK_XY_WINDOW) { 312 /* translate control -> display, for filling in the event to be dispatched */ 313 auto gtkAccessible = ATK.GTK_ACCESSIBLE (object.handle); 314 auto topLevel = ATK.gtk_widget_get_toplevel (gtkAccessible.widget); 315 auto window = OS.GTK_WIDGET_WINDOW (topLevel); 316 int topWindowX, topWindowY; 317 OS.gdk_window_get_origin (window, &topWindowX, &topWindowY); 318 event.x += topWindowX; 319 event.y += topWindowY; 320 } 321 for (int i = 0; i < listeners.length; i++) { 322 listeners [i].getChildAtPoint (event); 323 } 324 if (event.childID is object.id) event.childID = ACC.CHILDID_SELF; 325 AccessibleObject accObj = object.getChildByID (event.childID); 326 if (accObj !is null) { 327 if (parentResult !is null) OS.g_object_unref (parentResult); 328 OS.g_object_ref (accObj.handle); 329 return accObj.handle; 330 } 331 return parentResult; 332 } 333 334 package static extern(C) AtkHyperlink* atkHypertext_get_link (void* obj, int link_index) { 335 auto atkObject = cast(AtkObject*)obj; 336 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkHypertext_get_link"); 337 return null; 338 } 339 340 package static extern(C) int atkHypertext_get_n_links (void* obj) { 341 auto atkObject = cast(AtkObject*)obj; 342 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkHypertext_get_n_links"); 343 return 0; /* read hyperlink's name */ 344 } 345 346 package static extern(C) int atkHypertext_get_link_index (void* obj, int char_index) { 347 auto atkObject = cast(AtkObject*)obj; 348 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkHypertext_get_link_index"); 349 return 0; 350 } 351 352 package static extern(C) char* atkObject_get_description (AtkObject* atkObject) { 353 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObject_get_description"); 354 AccessibleObject object = getAccessibleObject (atkObject); 355 if (object is null) return null; 356 char* parentResult; 357 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 358 if (objectClass.get_description !is null) { 359 parentResult = objectClass.get_description(object.handle); 360 } 361 AccessibleListener[] listeners = object.getAccessibleListeners (); 362 if (listeners.length is 0) return parentResult; 363 364 AccessibleEvent event = new AccessibleEvent (object); 365 event.childID = object.id; 366 if (parentResult !is null) { 367 event.result = fromStringz( parentResult )._idup(); 368 } 369 for (int i = 0; i < listeners.length; i++) { 370 listeners [i].getDescription (event); 371 } 372 if (event.result is null) return parentResult; 373 if (descriptionPtr !is null) OS.g_free (descriptionPtr.ptr); 374 375 String name = event.result._idup() ~ '\0'; 376 char* p = cast(char*)OS.g_malloc (name.length); 377 descriptionPtr = p ? cast(String)p[ 0 .. name.length ] : null; 378 return cast(char*)descriptionPtr.ptr; 379 } 380 381 package static extern(C) char* atkObject_get_name (AtkObject* atkObject) { 382 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObject_get_name: {}", atkObject); 383 AccessibleObject object = getAccessibleObject (atkObject); 384 if (object is null) return null; 385 char* parentResult; 386 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 387 if (objectClass.get_name !is null) { 388 parentResult = objectClass.get_name( object.handle); 389 } 390 AccessibleListener[] listeners = object.getAccessibleListeners (); 391 if (listeners.length is 0) return parentResult; 392 393 AccessibleEvent event = new AccessibleEvent (object); 394 event.childID = object.id; 395 if (parentResult !is null) { 396 event.result = fromStringz( parentResult )._idup(); 397 } 398 for (int i = 0; i < listeners.length; i++) { 399 listeners [i].getName (event); 400 } 401 if (event.result is null) return parentResult; 402 if (namePtr !is null) OS.g_free (namePtr.ptr); 403 String name = event.result._idup() ~ '\0'; 404 char* p = cast(char*)OS.g_malloc (name.length); 405 namePtr = p ? cast(String)p[ 0 .. name.length ] : null; 406 return cast(char*)namePtr.ptr; 407 } 408 409 package static extern(C) int atkObject_get_n_children (AtkObject* atkObject) { 410 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObject_get_n_children: {}", atkObject); 411 AccessibleObject object = getAccessibleObject (atkObject); 412 if (object is null) return 0; 413 int parentResult = 0; 414 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 415 if (objectClass.get_n_children !is null) { 416 parentResult = objectClass.get_n_children( object.handle); 417 } 418 AccessibleControlListener[] listeners = object.getControlListeners (); 419 if (listeners.length is 0) return parentResult; 420 421 AccessibleControlEvent event = new AccessibleControlEvent (object); 422 event.childID = object.id; 423 event.detail = parentResult; 424 for (int i = 0; i < listeners.length; i++) { 425 listeners [i].getChildCount (event); 426 } 427 return event.detail; 428 } 429 430 package static extern(C) int atkObject_get_index_in_parent (AtkObject* atkObject) { 431 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObjectCB_get_index_in_parent. "); 432 AccessibleObject object = getAccessibleObject (atkObject); 433 if (object is null) return 0; 434 if (object.index !is -1) return object.index; 435 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 436 if (objectClass.get_index_in_parent is null) return 0; 437 return objectClass.get_index_in_parent(object. handle); 438 } 439 440 package static extern(C) AtkObject* atkObject_get_parent (AtkObject* atkObject) { 441 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObject_get_parent: {}", atkObject); 442 AccessibleObject object = getAccessibleObject (atkObject); 443 if (object is null) return null; 444 if (object.parent !is null) return object.parent.handle; 445 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 446 if (objectClass.get_parent is null) return null; 447 return objectClass.get_parent( object.handle); 448 } 449 450 package static extern(C) int atkObject_get_role (AtkObject* atkObject) { 451 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObject_get_role: {}", atkObject); 452 AccessibleObject object = getAccessibleObject (atkObject); 453 if (object is null) return 0; 454 if (object.getAccessibleListeners ().length !is 0) { 455 AccessibleControlListener[] listeners = object.getControlListeners (); 456 AccessibleControlEvent event = new AccessibleControlEvent (object); 457 event.childID = object.id; 458 event.detail = -1; 459 for (int i = 0; i < listeners.length; i++) { 460 listeners [i].getRole (event); 461 } 462 if (event.detail !is -1) { 463 switch (event.detail) { 464 /* Convert from win32 role values to atk role values */ 465 case ACC.ROLE_CHECKBUTTON: return ATK.ATK_ROLE_CHECK_BOX; 466 case ACC.ROLE_CLIENT_AREA: return ATK.ATK_ROLE_DRAWING_AREA; 467 case ACC.ROLE_COMBOBOX: return ATK.ATK_ROLE_COMBO_BOX; 468 case ACC.ROLE_DIALOG: return ATK.ATK_ROLE_DIALOG; 469 case ACC.ROLE_LABEL: return ATK.ATK_ROLE_LABEL; 470 case ACC.ROLE_LINK: return ATK.ATK_ROLE_TEXT; 471 case ACC.ROLE_LIST: return ATK.ATK_ROLE_LIST; 472 case ACC.ROLE_LISTITEM: return ATK.ATK_ROLE_LIST_ITEM; 473 case ACC.ROLE_MENU: return ATK.ATK_ROLE_MENU; 474 case ACC.ROLE_MENUBAR: return ATK.ATK_ROLE_MENU_BAR; 475 case ACC.ROLE_MENUITEM: return ATK.ATK_ROLE_MENU_ITEM; 476 case ACC.ROLE_PROGRESSBAR: return ATK.ATK_ROLE_PROGRESS_BAR; 477 case ACC.ROLE_PUSHBUTTON: return ATK.ATK_ROLE_PUSH_BUTTON; 478 case ACC.ROLE_SCROLLBAR: return ATK.ATK_ROLE_SCROLL_BAR; 479 case ACC.ROLE_SEPARATOR: return ATK.ATK_ROLE_SEPARATOR; 480 case ACC.ROLE_SLIDER: return ATK.ATK_ROLE_SLIDER; 481 case ACC.ROLE_TABLE: return ATK.ATK_ROLE_LIST; 482 case ACC.ROLE_TABLECELL: return ATK.ATK_ROLE_LIST_ITEM; 483 case ACC.ROLE_TABLECOLUMNHEADER: return ATK.ATK_ROLE_TABLE_COLUMN_HEADER; 484 case ACC.ROLE_TABLEROWHEADER: return ATK.ATK_ROLE_TABLE_ROW_HEADER; 485 case ACC.ROLE_TABFOLDER: return ATK.ATK_ROLE_PAGE_TAB_LIST; 486 case ACC.ROLE_TABITEM: return ATK.ATK_ROLE_PAGE_TAB; 487 case ACC.ROLE_TEXT: return ATK.ATK_ROLE_TEXT; 488 case ACC.ROLE_TOOLBAR: return ATK.ATK_ROLE_TOOL_BAR; 489 case ACC.ROLE_TOOLTIP: return ATK.ATK_ROLE_TOOL_TIP; 490 case ACC.ROLE_TREE: return ATK.ATK_ROLE_TREE; 491 case ACC.ROLE_TREEITEM: return ATK.ATK_ROLE_LIST_ITEM; 492 case ACC.ROLE_RADIOBUTTON: return ATK.ATK_ROLE_RADIO_BUTTON; 493 case ACC.ROLE_WINDOW: return ATK.ATK_ROLE_WINDOW; 494 default: 495 } 496 } 497 } 498 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 499 if (objectClass.get_role is null) return 0; 500 return objectClass.get_role( object.handle); 501 } 502 503 package static extern(C) AtkObject* atkObject_ref_child (AtkObject* atkObject, int index) { 504 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObject_ref_child: {} of: {}", index, atkObject); 505 AccessibleObject object = getAccessibleObject (atkObject); 506 if (object is null) return null; 507 object.updateChildren (); 508 AccessibleObject accObject = object.getChildByIndex (index); 509 if (accObject !is null) { 510 OS.g_object_ref (accObject.handle); 511 return accObject.handle; 512 } 513 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 514 if (objectClass.ref_child is null) return null; 515 return objectClass.ref_child( object.handle, index); 516 } 517 518 package static extern(C) AtkStateSet * atkObject_ref_state_set (AtkObject* atkObject) { 519 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkObject_ref_state_set"); 520 AccessibleObject object = getAccessibleObject (atkObject); 521 if (object is null) return null; 522 AtkStateSet* parentResult; 523 auto objectClass = cast(AtkObjectClass*)ATK.g_type_class_peek (object.parentType); 524 if (objectClass.ref_state_set !is null) { 525 parentResult = objectClass.ref_state_set( object.handle); 526 } 527 AccessibleControlListener[] listeners = object.getControlListeners (); 528 if (listeners.length is 0) return parentResult; 529 530 auto set = parentResult; 531 AccessibleControlEvent event = new AccessibleControlEvent (object); 532 event.childID = object.id; 533 event.detail = -1; 534 for (int i = 0; i < listeners.length; i++) { 535 listeners [i].getState (event); 536 } 537 if (event.detail !is -1) { 538 /* Convert from win32 state values to atk state values */ 539 int state = event.detail; 540 if ((state & ACC.STATE_BUSY) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_BUSY); 541 if ((state & ACC.STATE_CHECKED) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_CHECKED); 542 if ((state & ACC.STATE_EXPANDED) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_EXPANDED); 543 if ((state & ACC.STATE_FOCUSABLE) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_FOCUSABLE); 544 if ((state & ACC.STATE_FOCUSED) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_FOCUSED); 545 if ((state & ACC.STATE_HOTTRACKED) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_ARMED); 546 if ((state & ACC.STATE_INVISIBLE) is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_VISIBLE); 547 if ((state & ACC.STATE_MULTISELECTABLE) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_MULTISELECTABLE); 548 if ((state & ACC.STATE_OFFSCREEN) is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SHOWING); 549 if ((state & ACC.STATE_PRESSED) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_PRESSED); 550 if ((state & ACC.STATE_READONLY) is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_EDITABLE); 551 if ((state & ACC.STATE_SELECTABLE) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SELECTABLE); 552 if ((state & ACC.STATE_SELECTED) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SELECTED); 553 if ((state & ACC.STATE_SIZEABLE) !is 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_RESIZABLE); 554 /* Note: STATE_COLLAPSED, STATE_LINKED and STATE_NORMAL have no ATK equivalents */ 555 } 556 return set; 557 } 558 559 package static extern(C) int atkSelection_is_child_selected (void* obj, int index) { 560 auto atkObject = cast(AtkObject*)obj; 561 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkSelection_is_child_selected"); 562 AccessibleObject object = getAccessibleObject (atkObject); 563 if (object is null) return 0; 564 int parentResult = 0; 565 if (ATK.g_type_is_a (object.parentType, ATK_SELECTION_TYPE)) { 566 auto selectionIface = cast(AtkSelectionIface*)ATK.g_type_interface_peek_parent (ATK.ATK_SELECTION_GET_IFACE (object.handle)); 567 if (selectionIface.is_child_selected !is null) { 568 parentResult = selectionIface.is_child_selected( object.handle, index); 569 } 570 } 571 AccessibleControlListener[] listeners = object.getControlListeners (); 572 if (listeners.length is 0) return parentResult; 573 574 AccessibleControlEvent event = new AccessibleControlEvent (object); 575 event.childID = object.id; 576 for (int i = 0; i < listeners.length; i++) { 577 listeners [i].getSelection (event); 578 } 579 AccessibleObject accessibleObject = object.getChildByID (event.childID); 580 if (accessibleObject !is null) { 581 return accessibleObject.index is index ? 1 : 0; 582 } 583 return parentResult; 584 } 585 586 package static extern(C) AtkObject* atkSelection_ref_selection (void* obj, int index) { 587 auto atkObject = cast(AtkObject*)obj; 588 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkSelection_ref_selection"); 589 AccessibleObject object = getAccessibleObject (atkObject); 590 if (object is null) return null; 591 AtkObject* parentResult; 592 if (ATK.g_type_is_a (object.parentType, ATK_SELECTION_TYPE)) { 593 auto selectionIface = cast(AtkSelectionIface*)ATK.g_type_interface_peek_parent (ATK.ATK_SELECTION_GET_IFACE (object.handle)); 594 if (selectionIface.ref_selection !is null) { 595 parentResult = selectionIface.ref_selection( object.handle, index); 596 } 597 } 598 AccessibleControlListener[] listeners = object.getControlListeners (); 599 if (listeners.length is 0) return parentResult; 600 601 AccessibleControlEvent event = new AccessibleControlEvent (object); 602 event.childID = object.id; 603 for (int i = 0; i < listeners.length; i++) { 604 listeners [i].getSelection (event); 605 } 606 AccessibleObject accObj = object.getChildByID (event.childID); 607 if (accObj !is null) { 608 if (parentResult !is null) OS.g_object_unref (parentResult); 609 OS.g_object_ref (accObj.handle); 610 return accObj.handle; 611 } 612 return parentResult; 613 } 614 615 package static extern(C) int atkText_get_caret_offset (void* obj) { 616 auto atkObject = cast(AtkObject*)obj; 617 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_caret_offset"); 618 AccessibleObject object = getAccessibleObject (atkObject); 619 if (object is null) return 0; 620 int parentResult = 0; 621 if (ATK.g_type_is_a (object.parentType, ATK_TEXT_TYPE)) { 622 auto textIface = cast(AtkTextIface*)ATK.g_type_interface_peek_parent (ATK.ATK_TEXT_GET_IFACE (object.handle)); 623 if (textIface.get_caret_offset !is null) { 624 parentResult = textIface.get_caret_offset( object.handle); 625 } 626 } 627 AccessibleTextListener[] listeners = object.getTextListeners (); 628 if (listeners.length is 0) return parentResult; 629 630 AccessibleTextEvent event = new AccessibleTextEvent (object); 631 event.childID = object.id; 632 event.offset = parentResult; 633 for (int i = 0; i < listeners.length; i++) { 634 listeners [i].getCaretOffset (event); 635 } 636 return event.offset; 637 } 638 639 package static extern(C) uint atkText_get_character_at_offset (void* obj, int offset) { 640 auto atkObject = cast(AtkObject*)obj; 641 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_character_at_offset"); 642 AccessibleObject object = getAccessibleObject (atkObject); 643 if (object is null) return 0; 644 String text = object.getText (); 645 if (text !is null) return text[ offset ]; // TODO 646 if (ATK.g_type_is_a (object.parentType, ATK_TEXT_TYPE)) { 647 auto textIface = cast(AtkTextIface*)ATK.g_type_class_peek (object.parentType); 648 if (textIface.get_character_at_offset !is null) { 649 return textIface.get_character_at_offset( object.handle, offset); 650 } 651 } 652 return 0; 653 } 654 655 package static extern(C) int atkText_get_character_count (void* obj) { 656 auto atkObject = cast(AtkObject*)obj; 657 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_character_count"); 658 AccessibleObject object = getAccessibleObject (atkObject); 659 if (object is null) return 0; 660 String text = object.getText (); 661 if (text !is null) return cast(int)/*64bit*/text.length; 662 if (ATK.g_type_is_a (object.parentType, ATK_TEXT_TYPE)) { 663 auto textIface = cast(AtkTextIface*)ATK.g_type_class_peek (object.parentType); 664 if (textIface.get_character_count !is null) { 665 return textIface.get_character_count( object.handle); 666 } 667 } 668 return 0; 669 } 670 671 package static extern(C) int atkText_get_n_selections (void* obj) { 672 auto atkObject = cast(AtkObject*)obj; 673 if (DEBUG) getDwtLogger().info( __FILE__, __LINE__, "-->atkText_get_n_selections"); 674 AccessibleObject object = getAccessibleObject (atkObject); 675 if (object is null) return 0; 676 int parentResult = 0; 677 if (ATK.g_type_is_a (object.parentType, ATK_TEXT_TYPE)) { 678 auto textIface = cast(AtkTextIface*)ATK.g_type_interface_peek_parent (ATK.ATK_TEXT_GET_IFACE (object.handle)); 679 if (textIface.get_n_selections !is null) { 680 parentResult = textIface.get_n_selections( object.handle); 681 } 682 } 683 AccessibleTextListener[] listeners = object.getTextListeners (); 684 if (listeners.length is 0) return parentResult; 685 686 AccessibleTextEvent event = new AccessibleTextEvent (object); 687 event.childID = object.id; 688 for (int i = 0; i < listeners.length; i++) { 689 listeners [i].getSelectionRange (event); 690 } 691 return event.length is 0 ? parentResult : 1; 692 } 693 694 package static extern(C) char* atkText_get_selection (void* obj, int selection_num, int* start_offset, int* end_offset) { 695 auto atkObject = cast(AtkObject*)obj; 696 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_selection"); 697 AccessibleObject object = getAccessibleObject (atkObject); 698 if (object is null) return null; 699 *start_offset=0; 700 *end_offset=0; 701 if (ATK.g_type_is_a (object.parentType, ATK_TEXT_TYPE)) { 702 auto textIface = cast(AtkTextIface*)ATK.g_type_interface_peek_parent (ATK.ATK_TEXT_GET_IFACE (object.handle)); 703 if (textIface.get_selection !is null) { 704 textIface.get_selection( object.handle, selection_num, start_offset, end_offset ); 705 } 706 } 707 AccessibleTextListener[] listeners = object.getTextListeners (); 708 if (listeners.length is 0) return null; 709 710 AccessibleTextEvent event = new AccessibleTextEvent (object); 711 event.childID = object.id; 712 int parentStart; 713 int parentEnd; 714 parentStart= *start_offset; 715 parentEnd= *end_offset; 716 event.offset = parentStart; 717 event.length = (parentEnd - parentStart); 718 for (int i = 0; i < listeners.length; i++) { 719 listeners [i].getSelectionRange (event); 720 } 721 *start_offset = event.offset; 722 *end_offset = event.offset + event.length; 723 return null; 724 } 725 726 package static extern(C) char* atkText_get_text (void* obj, int start_offset, int end_offset) { 727 auto atkObject = cast(AtkObject*)obj; 728 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_text: {},{}", start_offset, end_offset); 729 AccessibleObject object = getAccessibleObject (atkObject); 730 if (object is null) return null; 731 String text = object.getText (); 732 if (text.length > 0) { 733 if (end_offset is -1) { 734 end_offset = cast(int)/*64bit*/text.length; 735 } else { 736 end_offset = cast(int)/*64bit*/Math.min (end_offset, text.length ); 737 } 738 start_offset = Math.min (start_offset, end_offset); 739 text = text[ start_offset .. end_offset ]; 740 auto result = cast(char*)OS.g_malloc (text.length+1); 741 result[ 0 .. text.length ] = text; 742 result[ text.length ] = '\0'; 743 return result; 744 } 745 return null; 746 } 747 748 package static extern(C) char* atkText_get_text_after_offset (void* obj, int offset_value, int boundary_type, int* start_offset, int* end_offset) { 749 auto atkObject = cast(AtkObject*)obj; 750 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_text_after_offset"); 751 AccessibleObject object = getAccessibleObject (atkObject); 752 if (object is null) return null; 753 auto offset = offset_value; 754 String text = object.getText (); 755 if (text.length > 0) { 756 int length = cast(int)/*64bit*/text.length ; 757 offset = Math.min (offset, length - 1); 758 int startBounds = offset; 759 int endBounds = offset; 760 switch (boundary_type) { 761 case ATK.ATK_TEXT_BOUNDARY_CHAR: { 762 if (length > offset) endBounds++; 763 break; 764 } 765 case ATK.ATK_TEXT_BOUNDARY_WORD_START: { 766 auto wordStart1 = nextIndexOfChar (text, " !?.\n", offset - 1); 767 if (wordStart1 is -1) { 768 startBounds = endBounds = length; 769 break; 770 } 771 wordStart1 = nextIndexOfNotChar (text, " !?.\n", wordStart1); 772 if (wordStart1 is length) { 773 startBounds = endBounds = length; 774 break; 775 } 776 startBounds = wordStart1; 777 auto wordStart2 = nextIndexOfChar (text, " !?.\n", wordStart1); 778 if (wordStart2 is -1) { 779 endBounds = length; 780 break; 781 } 782 endBounds = nextIndexOfNotChar (text, " !?.\n", wordStart2); 783 break; 784 } 785 case ATK.ATK_TEXT_BOUNDARY_WORD_END: { 786 auto previousWordEnd = previousIndexOfNotChar (text, " \n", offset); 787 if (previousWordEnd is -1 || previousWordEnd !is offset - 1) { 788 offset = nextIndexOfNotChar (text, " \n", offset); 789 } 790 if (offset is -1) { 791 startBounds = endBounds = length; 792 break; 793 } 794 auto wordEnd1 = nextIndexOfChar (text, " !?.\n", offset); 795 if (wordEnd1 is -1) { 796 startBounds = endBounds = length; 797 break; 798 } 799 wordEnd1 = nextIndexOfNotChar (text, "!?.", wordEnd1); 800 if (wordEnd1 is length) { 801 startBounds = endBounds = length; 802 break; 803 } 804 startBounds = wordEnd1; 805 auto wordEnd2 = nextIndexOfNotChar (text, " \n", wordEnd1); 806 if (wordEnd2 is length) { 807 startBounds = endBounds = length; 808 break; 809 } 810 wordEnd2 = nextIndexOfChar (text, " !?.\n", wordEnd2); 811 if (wordEnd2 is -1) { 812 endBounds = length; 813 break; 814 } 815 endBounds = nextIndexOfNotChar (text, "!?.", wordEnd2); 816 break; 817 } 818 case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: { 819 auto previousSentenceEnd = previousIndexOfChar (text, "!?.", offset); 820 auto previousText = previousIndexOfNotChar (text, " !?.\n", offset); 821 auto sentenceStart1 = 0; 822 if (previousSentenceEnd >= previousText) { 823 sentenceStart1 = nextIndexOfNotChar (text, " !?.\n", offset); 824 } else { 825 sentenceStart1 = nextIndexOfChar (text, "!?.", offset); 826 if (sentenceStart1 is -1) { 827 startBounds = endBounds = length; 828 break; 829 } 830 sentenceStart1 = nextIndexOfNotChar (text, " !?.\n", sentenceStart1); 831 } 832 if (sentenceStart1 is length) { 833 startBounds = endBounds = length; 834 break; 835 } 836 startBounds = sentenceStart1; 837 auto sentenceStart2 = nextIndexOfChar (text, "!?.", sentenceStart1); 838 if (sentenceStart2 is -1) { 839 endBounds = length; 840 break; 841 } 842 endBounds = nextIndexOfNotChar (text, " !?.\n", sentenceStart2); 843 break; 844 } 845 case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: { 846 auto sentenceEnd1 = nextIndexOfChar (text, "!?.", offset); 847 if (sentenceEnd1 is -1) { 848 startBounds = endBounds = length; 849 break; 850 } 851 sentenceEnd1 = nextIndexOfNotChar (text, "!?.", sentenceEnd1); 852 if (sentenceEnd1 is length) { 853 startBounds = endBounds = length; 854 break; 855 } 856 startBounds = sentenceEnd1; 857 auto sentenceEnd2 = nextIndexOfNotChar (text, " \n", sentenceEnd1); 858 if (sentenceEnd2 is length) { 859 startBounds = endBounds = length; 860 break; 861 } 862 sentenceEnd2 = nextIndexOfChar (text, "!?.", sentenceEnd2); 863 if (sentenceEnd2 is -1) { 864 endBounds = length; 865 break; 866 } 867 endBounds = nextIndexOfNotChar (text, "!?.", sentenceEnd2); 868 break; 869 } 870 case ATK.ATK_TEXT_BOUNDARY_LINE_START: { 871 auto lineStart1 = text.indexOf( '\n' ); 872 if (lineStart1 is -1) { 873 startBounds = endBounds = length; 874 break; 875 } 876 lineStart1 = nextIndexOfNotChar (text, "\n", lineStart1); 877 if (lineStart1 is length) { 878 startBounds = endBounds = length; 879 break; 880 } 881 startBounds = lineStart1; 882 auto lineStart2 = text.indexOf( '\n' ); 883 if (lineStart2 is -1) { 884 endBounds = length; 885 break; 886 } 887 lineStart2 = nextIndexOfNotChar (text, "\n", lineStart2); 888 endBounds = lineStart2; 889 break; 890 } 891 case ATK.ATK_TEXT_BOUNDARY_LINE_END: { 892 auto lineEnd1 = nextIndexOfChar (text, "\n", offset); 893 if (lineEnd1 is -1) { 894 startBounds = endBounds = length; 895 break; 896 } 897 startBounds = lineEnd1; 898 if (startBounds is length) { 899 endBounds = length; 900 break; 901 } 902 auto lineEnd2 = nextIndexOfChar (text, "\n", lineEnd1 + 1); 903 if (lineEnd2 is -1) { 904 endBounds = length; 905 break; 906 } 907 endBounds = lineEnd2; 908 break; 909 } 910 default: 911 } 912 *start_offset=startBounds; 913 *end_offset=endBounds; 914 text = text[startBounds .. endBounds ]; 915 auto result = cast(char*)OS.g_malloc (text.length+1); 916 result[ 0 .. text.length ] = text; 917 result[ text.length ] = '\0'; 918 return result; 919 } 920 return null; 921 } 922 923 package static extern(C) char* atkText_get_text_at_offset (void* obj, int offset_value, int boundary_type, int* start_offset, int* end_offset) { 924 auto atkObject = cast(AtkObject*)obj; 925 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_text_at_offset: {} start: {} end: {}", offset_value, start_offset, end_offset); 926 AccessibleObject object = getAccessibleObject (atkObject); 927 if (object is null) return null; 928 auto offset = offset_value; 929 String text = object.getText (); 930 if (text.length > 0) { 931 auto length = text.length; 932 offset = cast(int)/*64bit*/Math.min (offset, length - 1); 933 auto startBounds = offset; 934 auto endBounds = offset; 935 switch (boundary_type) { 936 case ATK.ATK_TEXT_BOUNDARY_CHAR: { 937 if (length > offset) endBounds++; 938 break; 939 } 940 case ATK.ATK_TEXT_BOUNDARY_WORD_START: { 941 auto wordStart1 = previousIndexOfNotChar (text, " !?.\n", offset); 942 if (wordStart1 is -1) { 943 startBounds = endBounds = 0; 944 break; 945 } 946 wordStart1 = previousIndexOfChar (text, " !?.\n", wordStart1) + 1; 947 if (wordStart1 is -1) { 948 startBounds = 0; 949 break; 950 } 951 startBounds = wordStart1; 952 auto wordStart2 = nextIndexOfChar (text, " !?.\n", wordStart1); 953 endBounds = nextIndexOfNotChar (text, " !?.\n", wordStart2); 954 break; 955 } 956 case ATK.ATK_TEXT_BOUNDARY_WORD_END: { 957 auto wordEnd1 = previousIndexOfNotChar (text, "!?.", offset + 1); 958 wordEnd1 = previousIndexOfChar (text, " !?.\n", wordEnd1); 959 wordEnd1 = previousIndexOfNotChar (text, " \n", wordEnd1 + 1); 960 if (wordEnd1 is -1) { 961 startBounds = endBounds = 0; 962 break; 963 } 964 startBounds = wordEnd1 + 1; 965 auto wordEnd2 = nextIndexOfNotChar (text, " \n", startBounds); 966 if (wordEnd2 is length) { 967 endBounds = startBounds; 968 break; 969 } 970 wordEnd2 = nextIndexOfChar (text, " !?.\n", wordEnd2); 971 if (wordEnd2 is -1) { 972 endBounds = startBounds; 973 break; 974 } 975 endBounds = nextIndexOfNotChar (text, "!?.", wordEnd2); 976 break; 977 } 978 case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: { 979 auto sentenceStart1 = previousIndexOfNotChar (text, " !?.\n", offset + 1); 980 if (sentenceStart1 is -1) { 981 startBounds = endBounds = 0; 982 break; 983 } 984 sentenceStart1 = previousIndexOfChar (text, "!?.", sentenceStart1) + 1; 985 startBounds = nextIndexOfNotChar (text, " \n", sentenceStart1); 986 auto sentenceStart2 = nextIndexOfChar (text, "!?.", startBounds); 987 endBounds = nextIndexOfNotChar (text, " !?.\n", sentenceStart2); 988 break; 989 } 990 case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: { 991 auto sentenceEnd1 = previousIndexOfNotChar (text, "!?.", offset + 1); 992 sentenceEnd1 = previousIndexOfChar (text, "!?.", sentenceEnd1); 993 sentenceEnd1 = previousIndexOfNotChar (text, " \n", sentenceEnd1 + 1); 994 if (sentenceEnd1 is -1) { 995 startBounds = endBounds = 0; 996 break; 997 } 998 startBounds = sentenceEnd1 + 1; 999 auto sentenceEnd2 = nextIndexOfNotChar (text, " \n", startBounds); 1000 if (sentenceEnd2 is length) { 1001 endBounds = startBounds; 1002 break; 1003 } 1004 sentenceEnd2 = nextIndexOfChar (text, "!?.", sentenceEnd2); 1005 if (sentenceEnd2 is -1) { 1006 endBounds = startBounds; 1007 break; 1008 } 1009 endBounds = nextIndexOfNotChar (text, "!?.", sentenceEnd2); 1010 break; 1011 } 1012 case ATK.ATK_TEXT_BOUNDARY_LINE_START: { 1013 startBounds = previousIndexOfChar (text, "\n", offset) + 1; 1014 auto lineEnd2 = nextIndexOfChar (text, "\n", startBounds); 1015 if (lineEnd2 < length) lineEnd2++; 1016 endBounds = lineEnd2; 1017 break; 1018 } 1019 case ATK.ATK_TEXT_BOUNDARY_LINE_END: { 1020 auto lineEnd1 = previousIndexOfChar (text, "\n", offset); 1021 if (lineEnd1 is -1) { 1022 startBounds = endBounds = 0; 1023 break; 1024 } 1025 startBounds = lineEnd1; 1026 endBounds = nextIndexOfChar (text, "\n", lineEnd1 + 1); 1027 break; 1028 } 1029 default: 1030 break; 1031 } 1032 *start_offset=startBounds; 1033 *end_offset=endBounds; 1034 text = text[startBounds .. endBounds]; 1035 auto result = cast(char*) OS.g_malloc (text.length+1); 1036 result[ 0 .. text.length ] = text; 1037 result[ text.length ] = '\0'; 1038 return result; 1039 } 1040 return null; 1041 } 1042 1043 package static extern(C) char* atkText_get_text_before_offset (void* obj, int offset_value, int boundary_type, int* start_offset, int* end_offset) { 1044 auto atkObject = cast(AtkObject*)obj; 1045 if (DEBUG) getDwtLogger().info (__FILE__, __LINE__, "-->atkText_get_text_before_offset"); 1046 AccessibleObject object = getAccessibleObject (atkObject); 1047 if (object is null) return null; 1048 int offset = offset_value; 1049 String text = object.getText (); 1050 if (text.length > 0) { 1051 auto length = text.length; 1052 offset = cast(int)/*64bit*/Math.min (offset, length - 1); 1053 auto startBounds = offset; 1054 auto endBounds = offset; 1055 switch (boundary_type) { 1056 case ATK.ATK_TEXT_BOUNDARY_CHAR: { 1057 if (length >= offset && offset > 0) startBounds--; 1058 break; 1059 } 1060 case ATK.ATK_TEXT_BOUNDARY_WORD_START: { 1061 auto wordStart1 = previousIndexOfChar (text, " !?.\n", offset - 1); 1062 if (wordStart1 is -1) { 1063 startBounds = endBounds = 0; 1064 break; 1065 } 1066 auto wordStart2 = previousIndexOfNotChar (text, " !?.\n", wordStart1); 1067 if (wordStart2 is -1) { 1068 startBounds = endBounds = 0; 1069 break; 1070 } 1071 endBounds = wordStart1 + 1; 1072 startBounds = previousIndexOfChar (text, " !?.\n", wordStart2) + 1; 1073 break; 1074 } 1075 case ATK.ATK_TEXT_BOUNDARY_WORD_END: { 1076 auto wordEnd1 =previousIndexOfChar (text, " !?.\n", offset); 1077 if (wordEnd1 is -1) { 1078 startBounds = endBounds = 0; 1079 break; 1080 } 1081 wordEnd1 = previousIndexOfNotChar (text, " \n", wordEnd1 + 1); 1082 if (wordEnd1 is -1) { 1083 startBounds = endBounds = 0; 1084 break; 1085 } 1086 endBounds = wordEnd1 + 1; 1087 auto wordEnd2 = previousIndexOfNotChar (text, " !?.\n", endBounds); 1088 wordEnd2 = previousIndexOfChar (text, " !?.\n", wordEnd2); 1089 if (wordEnd2 is -1) { 1090 startBounds = 0; 1091 break; 1092 } 1093 startBounds = previousIndexOfNotChar (text, " \n", wordEnd2 + 1) + 1; 1094 break; 1095 } 1096 case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: { 1097 auto sentenceStart1 = previousIndexOfChar (text, "!?.", offset); 1098 if (sentenceStart1 is -1) { 1099 startBounds = endBounds = 0; 1100 break; 1101 } 1102 auto sentenceStart2 = previousIndexOfNotChar (text, "!?.", sentenceStart1); 1103 if (sentenceStart2 is -1) { 1104 startBounds = endBounds = 0; 1105 break; 1106 } 1107 endBounds = sentenceStart1 + 1; 1108 startBounds = previousIndexOfChar (text, "!?.", sentenceStart2) + 1; 1109 break; 1110 } 1111 case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: { 1112 auto sentenceEnd1 = previousIndexOfChar (text, "!?.", offset); 1113 if (sentenceEnd1 is -1) { 1114 startBounds = endBounds = 0; 1115 break; 1116 } 1117 sentenceEnd1 = previousIndexOfNotChar (text, " \n", sentenceEnd1 + 1); 1118 if (sentenceEnd1 is -1) { 1119 startBounds = endBounds = 0; 1120 break; 1121 } 1122 endBounds = sentenceEnd1 + 1; 1123 auto sentenceEnd2 = previousIndexOfNotChar (text, "!?.", endBounds); 1124 sentenceEnd2 = previousIndexOfChar (text, "!?.", sentenceEnd2); 1125 if (sentenceEnd2 is -1) { 1126 startBounds = 0; 1127 break; 1128 } 1129 startBounds = previousIndexOfNotChar (text, " \n", sentenceEnd2 + 1) + 1; 1130 break; 1131 } 1132 case ATK.ATK_TEXT_BOUNDARY_LINE_START: { 1133 auto lineStart1 = previousIndexOfChar (text, "\n", offset); 1134 if (lineStart1 is -1) { 1135 startBounds = endBounds = 0; 1136 break; 1137 } 1138 endBounds = lineStart1 + 1; 1139 startBounds = previousIndexOfChar (text, "\n", lineStart1) + 1; 1140 break; 1141 } 1142 case ATK.ATK_TEXT_BOUNDARY_LINE_END: { 1143 auto lineEnd1 = previousIndexOfChar (text, "\n", offset); 1144 if (lineEnd1 is -1) { 1145 startBounds = endBounds = 0; 1146 break; 1147 } 1148 endBounds = lineEnd1; 1149 startBounds = previousIndexOfChar (text, "\n", lineEnd1); 1150 if (startBounds is -1) startBounds = 0; 1151 break; 1152 } 1153 default: 1154 } 1155 *start_offset=startBounds; 1156 *end_offset=endBounds; 1157 text = text[startBounds .. endBounds]; 1158 auto result = cast(char*)OS.g_malloc (text.length+1); 1159 result[ 0 .. text.length ] = text; 1160 result[ text.length ] = '\0'; 1161 return result; 1162 } 1163 return null; 1164 } 1165 1166 AccessibleListener[] getAccessibleListeners () { 1167 if (accessible is null) return new AccessibleListener [0]; 1168 AccessibleListener[] result = accessible.getAccessibleListeners (); 1169 return result !is null ? result : new AccessibleListener [0]; 1170 } 1171 1172 static AccessibleObject getAccessibleObject (AtkObject* atkObject) { 1173 return AccessibleObjects[atkObject]; 1174 } 1175 1176 AccessibleObject getChildByHandle (AtkObject* handle) { 1177 return cast(AccessibleObject) children.get( new LONG(handle) ); 1178 } 1179 1180 AccessibleObject getChildByID (int childId) { 1181 if (childId is ACC.CHILDID_SELF) return this; 1182 Enumeration elements = children.elements (); 1183 while (elements.hasMoreElements ()) { 1184 AccessibleObject object = cast(AccessibleObject) elements.nextElement (); 1185 if (object.id is childId) return object; 1186 } 1187 return null; 1188 } 1189 1190 AccessibleObject getChildByIndex (int childIndex) { 1191 Enumeration elements = children.elements (); 1192 while (elements.hasMoreElements ()) { 1193 AccessibleObject object = cast(AccessibleObject) elements.nextElement (); 1194 if (object.index is childIndex) return object; 1195 } 1196 return null; 1197 } 1198 1199 AccessibleControlListener[] getControlListeners () { 1200 if (accessible is null) return new AccessibleControlListener [0]; 1201 AccessibleControlListener[] result = accessible.getControlListeners (); 1202 return result !is null ? result : new AccessibleControlListener [0]; 1203 } 1204 1205 String getText () { 1206 char* parentResult; 1207 String parentText = ""; //$NON-NLS-1$ 1208 if (ATK.g_type_is_a (parentType, ATK_TEXT_TYPE)) { 1209 auto textIface = cast(AtkTextIface*)ATK.g_type_interface_peek_parent (ATK.ATK_TEXT_GET_IFACE (handle)); 1210 int characterCount = 0; 1211 if (textIface.get_character_count !is null) { 1212 characterCount = textIface.get_character_count( handle); 1213 } 1214 if (characterCount > 0 && textIface.get_text !is null) { 1215 parentResult = textIface.get_text( handle, 0, characterCount); 1216 if (parentResult !is null) { 1217 parentText = fromStringz( parentResult )._idup(); 1218 } 1219 } 1220 } 1221 AccessibleControlListener[] controlListeners = getControlListeners (); 1222 if (controlListeners.length is 0) return parentText; 1223 AccessibleControlEvent event = new AccessibleControlEvent (this); 1224 event.childID = id; 1225 event.result = parentText; 1226 for (int i = 0; i < controlListeners.length; i++) { 1227 controlListeners [i].getValue (event); 1228 } 1229 return event.result; 1230 } 1231 1232 AccessibleTextListener[] getTextListeners () { 1233 if (accessible is null) return new AccessibleTextListener [0]; 1234 AccessibleTextListener[] result = accessible.getTextListeners (); 1235 return result !is null ? result : new AccessibleTextListener [0]; 1236 } 1237 1238 package static extern(C) void gObjectClass_finalize (GObject* atkObject) { 1239 auto superType = ATK.g_type_class_peek_parent (ATK.G_OBJECT_GET_CLASS (cast(GTypeInstance*)atkObject)); 1240 auto objectClassStruct = cast(GObjectClass*)ATK.G_OBJECT_CLASS (cast(GTypeClass*)superType); 1241 objectClassStruct.finalize(atkObject); 1242 AccessibleObject object = getAccessibleObject (cast(AtkObject*)atkObject); 1243 if (object !is null) { 1244 AccessibleObjects.remove (cast(AtkObject*)atkObject); 1245 object.release (); 1246 } 1247 } 1248 1249 static int nextIndexOfChar (String str, String searchChars, int startIndex) { 1250 auto result = cast(int)/*64bit*/str.length; 1251 for (int i = 0; i < searchChars.length; i++) { 1252 char current = searchChars[i]; 1253 auto index = str.indexOf( current, startIndex ); 1254 if (index !is -1 ) result = Math.min (result, index); 1255 } 1256 return result; 1257 } 1258 1259 static int nextIndexOfNotChar (String str, String searchChars, int startIndex) { 1260 size_t length = str.length; 1261 auto index = startIndex; 1262 while (index < length) { 1263 char current = str[index]; 1264 if ( searchChars.indexOf( current) is -1) break; 1265 index++; 1266 } 1267 return index; 1268 } 1269 1270 static int previousIndexOfChar (String str, String searchChars, int startIndex) { 1271 int result = -1; 1272 if (startIndex < 0) return result; 1273 str = str[0 .. startIndex]; 1274 for (int i = 0; i < searchChars.length ; i++) { 1275 char current = searchChars[i]; 1276 auto index = str.lastIndexOf( current); 1277 if (index !is -1 ) result = Math.max (result, index); 1278 } 1279 return result; 1280 } 1281 1282 static int previousIndexOfNotChar (String str, String searchChars, int startIndex) { 1283 if (startIndex < 0) return -1; 1284 int index = startIndex - 1; 1285 while (index >= 0) { 1286 char current = str[index]; 1287 if ( searchChars.indexOf( current) is -1 ) break; 1288 index--; 1289 } 1290 return index; 1291 } 1292 1293 void release () { 1294 if (DEBUG) getDwtLogger().info( __FILE__, __LINE__, "AccessibleObject.release: {}", handle); 1295 accessible = null; 1296 Enumeration elements = children.elements (); 1297 while (elements.hasMoreElements ()) { 1298 AccessibleObject child = cast(AccessibleObject) elements.nextElement (); 1299 if (child.isLightweight) OS.g_object_unref (child.handle); 1300 } 1301 if (parent !is null) parent.removeChild (this, false); 1302 } 1303 1304 void removeChild (AccessibleObject child, bool unref) { 1305 children.remove (new LONG (child.handle)); 1306 if (unref && child.isLightweight) OS.g_object_unref (child.handle); 1307 } 1308 1309 void selectionChanged () { 1310 OS.g_signal_emit_by_name0 (handle, ATK.selection_changed.ptr); 1311 } 1312 1313 void setFocus (int childID) { 1314 updateChildren (); 1315 AccessibleObject accObject = getChildByID (childID); 1316 if (accObject !is null) { 1317 ATK.atk_focus_tracker_notify (accObject.handle); 1318 } 1319 } 1320 1321 void setParent (AccessibleObject parent) { 1322 this.parent = parent; 1323 } 1324 1325 void textCaretMoved(int index) { 1326 OS.g_signal_emit_by_name1 (handle, ATK.text_caret_moved.ptr, index); 1327 } 1328 1329 void textChanged(int type, int startIndex, int length) { 1330 if (type is ACC.TEXT_DELETE) { 1331 OS.g_signal_emit_by_name2 (handle, ATK.text_changed_delete.ptr, startIndex, length); 1332 } else { 1333 OS.g_signal_emit_by_name2 (handle, ATK.text_changed_insert.ptr, startIndex, length); 1334 } 1335 } 1336 1337 void textSelectionChanged() { 1338 OS.g_signal_emit_by_name0 (handle, ATK.text_selection_changed.ptr); 1339 } 1340 1341 void updateChildren () { 1342 if (isLightweight) return; 1343 AccessibleControlListener[] listeners = getControlListeners (); 1344 if (listeners.length is 0) return; 1345 1346 AccessibleControlEvent event = new AccessibleControlEvent (this); 1347 for (int i = 0; i < listeners.length; i++) { 1348 listeners [i].getChildren (event); 1349 } 1350 if (event.children !is null && event.children.length > 0) { 1351 Vector idsToKeep = new Vector (children.size ()); 1352 if ( null !is cast(Integer)event.children [0]) { 1353 /* an array of child id's (Integers) was answered */ 1354 int parentType = AccessibleFactory.getDefaultParentType (); 1355 for (int i = 0; i < event.children.length; i++) { 1356 AccessibleObject object = getChildByIndex (i); 1357 if (object is null) { 1358 int childType = AccessibleFactory.getChildType (accessible, i); 1359 object = new AccessibleObject (childType, null, accessible, parentType, true); 1360 AccessibleObjects[object.handle] = object; 1361 addChild (object); 1362 object.index = i; 1363 } 1364 try { 1365 object.id = (cast(Integer)event.children[i]).intValue (); 1366 } catch (ClassCastException e) { 1367 /* a non-ID value was given so don't set the ID */ 1368 } 1369 idsToKeep.addElement (new LONG (object.handle)); 1370 } 1371 } else { 1372 /* an array of Accessible children was answered */ 1373 int childIndex = 0; 1374 for (int i = 0; i < event.children.length; i++) { 1375 AccessibleObject object = null; 1376 try { 1377 object = (cast(Accessible)event.children [i]).accessibleObject; 1378 } catch (ClassCastException e) { 1379 /* a non-Accessible value was given so nothing to do here */ 1380 } 1381 if (object !is null) { 1382 object.index = childIndex++; 1383 idsToKeep.addElement (new LONG (object.handle)); 1384 } 1385 } 1386 } 1387 /* remove old children that were not provided as children anymore */ 1388 Enumeration ids = children.keys (); 1389 while (ids.hasMoreElements ()) { 1390 LONG id = cast(LONG)ids.nextElement (); 1391 if (!idsToKeep.contains (id)) { 1392 AccessibleObject object = cast(AccessibleObject) children.get (id); 1393 removeChild (object, true); 1394 } 1395 } 1396 // AtkObject*[] idsToKeep = new AtkObject*[]( children.length ); 1397 // idsToKeep.length = 0; 1398 // if ( null !is (cast(Integer)event.children[0] )) { 1399 // /* an array of child id's (Integers) was answered */ 1400 // auto parentType = AccessibleFactory.getDefaultParentType (); 1401 // for (int i = 0; i < event.children.length; i++) { 1402 // AccessibleObject object = getChildByIndex (i); 1403 // if (object is null) { 1404 // auto childType = AccessibleFactory.getChildType (accessible, i); 1405 // object = new AccessibleObject (childType, null, accessible, parentType, true); 1406 // AccessibleObjects[object.handle] = object; 1407 // addChild (object); 1408 // object.index = i; 1409 // } 1410 // if( auto intChild = cast(Integer)event.children[i] ){ 1411 // object.id = intChild.intValue (); 1412 // } 1413 // else { 1414 // /* a non-ID value was given so don't set the ID */ 1415 // } 1416 // idsToKeep ~= object.handle; 1417 // } 1418 // } else { 1419 // /* an array of Accessible children was answered */ 1420 // int childIndex = 0; 1421 // for (int i = 0; i < event.children.length; i++) { 1422 // AccessibleObject object = null; 1423 // if( auto accChild = cast(Accessible)event.children[i] ){ 1424 // object = accChild.accessibleObject; 1425 // } else { 1426 // /* a non-Accessible value was given so nothing to do here */ 1427 // } 1428 // if (object !is null) { 1429 // object.index = childIndex++; 1430 // idsToKeep ~= object.handle; 1431 // } 1432 // } 1433 // } 1434 // /* remove old children that were not provided as children anymore */ 1435 // foreach( id; children.keys ){ 1436 // if ( !tango.core.Array.contains( idsToKeep, id )) { 1437 // AccessibleObject object = cast(AccessibleObject) children[id]; 1438 // removeChild (object, true); 1439 // } 1440 // } 1441 } 1442 } 1443 }