/* ObjectWindows circular child-object list. */ #include "tdkpin_object_list.h" enum { CONTAINER_CHILD_TAIL_OFFSET = 0x0a, CHILD_NEXT_OFFSET = 0x19, CHILD_WINDOW_HANDLE_OFFSET = 4, CHILD_CAN_CLOSE_SLOT = 0x3c, CHILD_BLOCKS_CLOSE_OFFSET = 0x0ec0, CHILD_GROUP_OFFSET = 0x17, CHILD_FLAGS_OFFSET = 0x16, CHILD_ACTIVATE_SLOT = 0x20, CHILD_TRANSFER_SLOT = 0x40, CHILD_TRANSFER_FLAG = 0x10, OBJECT_TRANSFER_POINTER_OFFSET = 0x0e, }; static Win16FarPtr read_far(Win16FarPtr object, uint16_t offset) { return win16_read_far_pointer(object, offset); } static void write_far( Win16FarPtr object, uint16_t offset, Win16FarPtr value) { win16_write_u16(object, offset, win16_far_offset(value)); win16_write_u16( object, (uint16_t)(offset + 2), win16_far_selector(value)); } /* * 1018:069a — append one child to a circular singly linked ring. * The container stores the tail; tail->next is the oldest/head child. */ void object_windows_append_child( Win16FarPtr container, Win16FarPtr child) { if (child == 0) { return; } Win16FarPtr tail = read_far(container, CONTAINER_CHILD_TAIL_OFFSET); if (tail == 0) { write_far(child, CHILD_NEXT_OFFSET, child); } else { write_far(child, CHILD_NEXT_OFFSET, read_far(tail, CHILD_NEXT_OFFSET)); write_far(tail, CHILD_NEXT_OFFSET, child); } write_far(container, CONTAINER_CHILD_TAIL_OFFSET, child); } /* 1018:071c — unlink one exact far child, maintaining the tail invariant. */ void object_windows_remove_child( Win16FarPtr container, Win16FarPtr child) { Win16FarPtr tail = read_far(container, CONTAINER_CHILD_TAIL_OFFSET); if (tail == 0) { return; } Win16FarPtr previous = tail; while (true) { Win16FarPtr candidate = read_far(previous, CHILD_NEXT_OFFSET); if (candidate == child || candidate == tail) { if (candidate != child) { return; } if (candidate == previous) { write_far(container, CONTAINER_CHILD_TAIL_OFFSET, 0); return; } if (candidate == tail) { write_far(container, CONTAINER_CHILD_TAIL_OFFSET, previous); } write_far(previous, CHILD_NEXT_OFFSET, read_far(candidate, CHILD_NEXT_OFFSET)); return; } previous = candidate; } } /* 1018:080d — visit head-to-tail and return the first matching child. */ Win16FarPtr object_windows_find_child( Win16FarPtr container, Win16FarPtr predicate) { Win16FarPtr tail = read_far(container, CONTAINER_CHILD_TAIL_OFFSET); if (tail == 0) { return 0; } Win16FarPtr child = read_far(tail, CHILD_NEXT_OFFSET); while (true) { if (win16_call_object_predicate(predicate, child)) { return child; } if (child == tail) { return 0; } child = read_far(child, CHILD_NEXT_OFFSET); } } /* * 1018:085a — visit every child head-to-tail. The next far pointer is read * before each callback so the callback may unlink or destroy the current node. */ void object_windows_for_each_child( Win16FarPtr container, Win16FarPtr action) { Win16FarPtr tail = read_far(container, CONTAINER_CHILD_TAIL_OFFSET); if (tail == 0) { return; } Win16FarPtr child = read_far(tail, CHILD_NEXT_OFFSET); while (child != tail) { Win16FarPtr next = read_far(child, CHILD_NEXT_OFFSET); win16_call_object_action(action, child); child = next; } win16_call_object_action(action, tail); } /* 1018:08ac — inherited virtual default returning AX=0xffff. */ uint16_t object_windows_minus_one_default(Win16FarPtr object) { (void)object; return 0xffff; } /* 1018:0ec0 — predicate: a windowed child whose CanClose returns false. */ bool object_windows_child_blocks_close( uint16_t ignored, Win16FarPtr child) { (void)ignored; if (win16_read_u16(win16_far_add_offset( child, CHILD_WINDOW_HANDLE_OFFSET)) == 0) { return false; } uint16_t vmt = win16_read_u16(child); Win16FarPtr can_close = win16_read_far_pointer(win16_dgroup_pointer(vmt), CHILD_CAN_CLOSE_SLOT); return !win16_call_object_bool_method(can_close, child); } /* 1018:0ef4 — true exactly when no child blocks container shutdown. */ bool object_windows_all_children_can_close(Win16FarPtr container) { Win16FarPtr blocker = object_windows_find_child( container, win16_relocated_code_pointer(4, CHILD_BLOCKS_CLOSE_OFFSET)); return blocker == 0; } /* 1018:03e9 — nested ForEach callback: dispose the supplied child object. */ void object_windows_dispose_child_callback( uint16_t ignored, Win16FarPtr child) { (void)ignored; borland_dispose_object(child); } /* 1018:0466 — nested predicate for signed group field +0x17. */ bool object_windows_child_group_matches( int16_t group, Win16FarPtr child) { return group >= 0 && win16_read_u16(win16_far_add_offset(child, CHILD_GROUP_OFFSET)) == (uint16_t)group; } /* * 1018:049e — evaluate one child and preserve the minimized-window text * refresh side effect. The function returns true only for flag-4 children * whose VMT +0x20 method returns false. */ bool object_windows_try_child_activation( uint16_t ignored, Win16FarPtr child) { (void)ignored; bool continue_processing = true; uint8_t flags = win16_read_u8( win16_far_add_offset(child, CHILD_FLAGS_OFFSET)); if ((flags & 4) != 0) { uint16_t vmt = win16_read_u16(child); Win16FarPtr procedure = win16_read_far_pointer( win16_dgroup_pointer(vmt), CHILD_ACTIVATE_SLOT); if (!win16_call_object_bool_method(procedure, child)) { continue_processing = false; } } if (continue_processing) { HWND16 window = win16_read_u16(win16_far_add_offset( child, CHILD_WINDOW_HANDLE_OFFSET)); if (IsIconic16(window) != 0) { char text[82]; (void)GetWindowText16(window, text, 81); (void)SetWindowText16(window, text); } } return !continue_processing; } /* 1018:0524 — group-zero predicate gated by 1018:049e. */ bool object_windows_unindexed_child_matches( uint16_t ignored, Win16FarPtr child) { return win16_read_u16(win16_far_add_offset( child, CHILD_GROUP_OFFSET)) == 0 && object_windows_try_child_activation(ignored, child); } static Win16FarPtr find_child_in_group( Win16FarPtr container, int16_t group, bool require_activation) { Win16FarPtr tail = read_far(container, CONTAINER_CHILD_TAIL_OFFSET); if (tail == 0) { return 0; } Win16FarPtr child = read_far(tail, CHILD_NEXT_OFFSET); while (true) { bool matches = object_windows_child_group_matches(group, child); if (matches && (!require_activation || object_windows_try_child_activation(0, child))) { return child; } if (child == tail) { return 0; } child = read_far(child, CHILD_NEXT_OFFSET); } } /* * 1018:055c — scan numbered groups starting at one. Each found child is passed * through 049e; after the first missing group, group zero is searched for a * child whose 049e result is true. The unusual final inversion is preserved. */ bool object_windows_validate_child_groups(Win16FarPtr container) { int16_t group = 1; bool result = false; Win16FarPtr child; do { child = find_child_in_group(container, group, false); if (child != 0) { result = object_windows_try_child_activation(0, child); } group++; } while (!result && child != 0); if (!result) { return find_child_in_group(container, 0, true) == 0; } return true; } /* 1018:0d9b — validate child groups, store status -4, or call VMT +0x44(2). */ void object_windows_prepare_window(Win16FarPtr window) { if (!object_windows_validate_child_groups(window)) { win16_write_u16(window, 2, 0xfffc); return; } uint16_t vmt = win16_read_u16(window); Win16FarPtr procedure = win16_read_far_pointer(win16_dgroup_pointer(vmt), 0x44); win16_call_object_u16_method(procedure, window, 2); } /* * 1018:0dcd — nested transfer callback. Flag 0x10 selects participants; * VMT +0x40 receives child, direction, and the current far cursor. AX advances * only the cursor offset with 16-bit wrap, preserving its selector. */ void object_windows_transfer_child_callback( ObjectWindowsTransferContext *context, Win16FarPtr child) { uint8_t flags = win16_read_u8( win16_far_add_offset(child, CHILD_FLAGS_OFFSET)); if ((flags & CHILD_TRANSFER_FLAG) == 0) { return; } uint16_t vmt = win16_read_u16(child); Win16FarPtr procedure = win16_read_far_pointer( win16_dgroup_pointer(vmt), CHILD_TRANSFER_SLOT); uint16_t consumed = win16_call_object_transfer_method( procedure, child, context->direction, context->cursor); context->cursor = win16_make_far_pointer( win16_far_selector(context->cursor), (uint16_t)(win16_far_offset(context->cursor) + consumed)); } /* 1018:0e0a — transfer across the circular child list head-to-tail. */ void object_windows_transfer_child_data( Win16FarPtr object, uint16_t direction) { Win16FarPtr cursor = read_far(object, OBJECT_TRANSFER_POINTER_OFFSET); if (cursor == 0) { return; } ObjectWindowsTransferContext context = {cursor, direction}; Win16FarPtr tail = read_far(object, CONTAINER_CHILD_TAIL_OFFSET); if (tail == 0) { return; } Win16FarPtr child = read_far(tail, CHILD_NEXT_OFFSET); while (child != tail) { Win16FarPtr next = read_far(child, CHILD_NEXT_OFFSET); object_windows_transfer_child_callback(&context, child); child = next; } object_windows_transfer_child_callback(&context, tail); }