I'm developing small tools using texteditor.gadget that require copying text to the clipboard and pasting from it. Instead of clipboard.device, I'm using textclip.library, which works well for both copying to and retrieving from the clipboard. However, I'm facing issues with texteditor.gadget. Here are the details:
1. Unmarking Text and Setting Cursor After Copy
After copying text to the clipboard, I want the selection to unmark and the cursor to move to the start of the previously marked text. My current approach, executed after ITextClip->WriteClipVector, is:
// Set cursor to selection start
IIntuition->SetGadgetAttrs((struct Gadget *)textEditor, window, NULL,
GA_TEXTEDITOR_CursorX, startx,
GA_TEXTEDITOR_CursorY, starty,
TAG_DONE);
// Activate the text editor
if (IIntuition->ActivateGadget((struct Gadget *)textEditor, window, NULL)) {
IDOS->Printf("Text editor activated successfully\n");
} else {
IDOS->Printf("Failed to activate text editor\n");
}
// Verify cursor position
ULONG newCursorX = 0, newCursorY = 0;
IIntuition->GetAttr(GA_TEXTEDITOR_CursorX, textEditor, &newCursorX);
IIntuition->GetAttr(GA_TEXTEDITOR_CursorY, textEditor, &newCursorY);
IDOS->Printf("Cursor set to position (%lu, %lu), selection should be cleared\n", newCursorX, newCursorY);
// Refresh to clear selection visually
IIntuition->RefreshGadgets((struct Gadget *)textEditor, window, NULL);
} else {
IDOS->Printf("Failed to write to clipboard\n");
}
This works, but is there a simpler way to instruct texteditor.gadget to unmark and reposition the cursor?
2. Pasting from Clipboard
I'm stuck when pasting text from the clipboard into the texteditor window. I successfully read the clipboard buffer, but pasting fails. My code is:
// Ensure window and gadget are active
IIntuition->ActivateWindow(window);
if (!IIntuition->ActivateGadget((struct Gadget *)textEditor, window, NULL)) {
IDOS->Printf("Failed to activate text editor before paste\n");
ITextClip->DisposeClipVector(clipText);
return;
}
// Attempt to insert text at cursor
struct GP_TEXTEDITOR_InsertText insertMsg = {
.MethodID = GM_TEXTEDITOR_InsertText,
.GInfo = NULL, // GadgetInfo can be NULL
.text = clipText,
.pos = GV_TEXTEDITOR_InsertText_Cursor
};
if (IIntuition->IDoMethodA(textEditor, (Msg)&insertMsg)) {
IDOS->Printf("Successfully pasted %lu bytes into text editor\n", textLen);
// Activate and refresh
if (IIntuition->ActivateGadget((struct Gadget *)textEditor, window, NULL)) {
IDOS->Printf("Text editor activated successfully after paste\n");
} else {
IDOS->Printf("Failed to activate text editor after paste\n");
}
IIntuition->RefreshGadgets((struct Gadget *)textEditor, window, NULL);
IIntuition->RefreshWindowFrame(window);
} else {
IDOS->Printf("Failed to paste text into text editor: possible error in context or method\n");
}
ITextClip->DisposeClipVector(clipText);
I consistently get "Failed to paste text..." with IDoMethodA. Switching to DoGadgetMethodA yields the same error, though the text appears visually. Any suggestions? Dealt with by using GA_TEXTEDITOR_Contents instead of GM_TEXTEDITOR_InsertText (which is strange, shouldn't it work too?)
3. Reading Selected Text for Clipboard
Reading selected text to copy to the clipboard works, but the process feels cumbersome. My current logic is:
Get selection coordinates via GM_TEXTEDITOR_BlockInfo (startx, starty, stopx, stopy). Export full text with GM_TEXTEDITOR_ExportText. Split text into lines by counting \n and replacing with \0. Calculate indices, adjust order if needed. Compute length, allocate a buffer, copy the selected portion, and add \n between lines. Copy to clipboard. Is there a more efficient method?
1. How should a scrollbar be integrated with the texteditor.gadget to reflect its content changes? Are there system-provided methods to handle this automatically, or must I manually calculate and update the scrollbar's properties? For example, do I need to compute the number of lines, window dimensions, and adjust the scrollbar size and position accordingly? Is there a simpler, built-in mechanism to attach a scrollbar to the text editor and have it function seamlessly, without manual recalculations ?
2.Is there a straightforward way to ensure the editor window remains active (i.e., with the cursor visible and ready for typing) after actions such as resizing the window, moving the window, or clicking outside the texteditor.gadget but within the main window (excluding toolbar buttons)? Are there specific options or flags to achieve this, or must I manually handle each case by checking conditions and invoking "ActivateGadget((struct Gadget *)textEditor, window, NULL);" together with "RefreshGadgets((struct Gadget *)textEditor, window, NULL);" to restore focus to the text editor ?
Is there a straightforward way to ensure the editor window remains active
Use a method like WMHI_INTUITICK to poll the Window->MouseX amd MouseY values then when the mouse id over the editgadget activate it with LayoutActivateGadget() (on OS4)
Check that another gadget is not active at the time by testing the toplevel layout
This stops you continuosly activateing the gadget and also stop it from steel foxus from another activated gadget just because the mouse moved over the editor,
@Andy Thanks a bunch ! It helps and clear code a lot. I do use WMHI_INTUITICK already for control ghosting/unghosting states, but with window active is still not that clear. We don't have LayoutActivateGadget, and LM_ACTIVATEGADGET (WM one instead) but you probably mean something like :
// Check if another gadget is active in the top-level layout
struct Gadget *layoutGadget = (struct Gadget *)resizableLayout;
if (!(layoutGadget->Activation & GACT_ACTIVEGADGET))
{
// Activate the text editor gadget using the layout
IIntuition->IDoMethod(resizableLayout, WM_ACTIVATEGADGET,
(struct Gadget *)textEditor, window, NULL);
}
?
This one didn't work, but this one works:
// Check if mouse is over the text editor gadget
if (mouseX >= editorLeft && mouseX <= editorRight &&
mouseY >= editorTop && mouseY <= editorBottom)
{
// Check if another gadget is active in the top-level layout
struct Gadget *layoutGadget = (struct Gadget *)resizableLayout;
if (!(layoutGadget->Activation & GACT_ACTIVEGADGET))
{
// Activate the text editor gadget directly
IIntuition->ActivateGadget((struct Gadget *)textEditor, window, NULL);
}
}
Anyway, that help only when the mouse over the texteditor , but texteditor not start to be active not after resize of window (because mouse not over it), not after i simple drag window (while it still active), not when i click on the toolbar gadget (but not toolbar buttons), and not when i simple iconify/deiconify, etc. I need it to react like this : if window is active , then texteditor gadget is active and cursor stays.
Same goes to cut/copy/paste : after it texteditor not active if i use toolbar buttons or menu when mouse outside of text editor.
So, thank you for idea with WHMI_INTUITICK, i make it as i want by this:
case WMHI_ACTIVE:
// Set flag when window becomes active
windowActive = TRUE;
break;
case WMHI_INTUITICK:
// Activate text editor if the window is active and no other gadget is active
if (window && windowActive)
{
// Check if another gadget is active in the top-level layout
struct Gadget *layoutGadget = (struct Gadget *)resizableLayout;
if (!(layoutGadget->Activation & GACT_ACTIVEGADGET))
{
// Activate the text editor gadget directly
IIntuition->ActivateGadget((struct Gadget *)textEditor, window, NULL);
}
}
break;
kas1e wrote:@Andy Thanks a bunch ! It helps and clear code a lot. I do use WMHI_INTUITICK already for control ghosting/unghosting states, but with window active is still not that clear. We don't have LayoutActivateGadget, and LM_ACTIVATEGADGET (WM one instead) but you probably mean something like :
Er we do except that its ActivateLayoutGadget() but I the got OS dependency the wrong way round and you should indeed use the WM_ACTIVATEGADGET. Quote:
// Check if another gadget is active in the top-level layout
struct Gadget *layoutGadget = (struct Gadget *)resizableLayout;
if (!(layoutGadget->Activation & GACT_ACTIVEGADGET))
{
// Activate the text editor gadget using the layout
IIntuition->IDoMethod(resizableLayout, WM_ACTIVATEGADGET,
(struct Gadget *)textEditor, window, NULL);
}
?
This one didn't work, but this one works:
Well it wouldn't you just applied the WM_ACTIVATEGADGET to a layooutgadget, should be the window class object! Also the method structure takes just ME=ethodID and gadget so that should be:
// Check if mouse is over the text editor gadget
if (mouseX >= editorLeft && mouseX = editorTop && mouseY Activation & GACT_ACTIVEGADGET))
{
// Activate the text editor gadget directly
IIntuition->ActivateGadget((struct Gadget *)textEditor, window, NULL);
}
}
You *should* use the WM_METHOD and not Activate gadget directly, as the active gadget in a window.class window is the layout not the any of it's children( the layout passes on events to the texteditor or whichever gadget it thinks is active)
If you do that then the texteditor should remain active after resizing the window.
Quote:
Anyway, that help only when the mouse over the texteditor , but texteditor not start to be active not after resize of window (because mouse not over it), not after i simple drag window (while it still active), not when i click on the toolbar gadget (but not toolbar buttons), and not when i simple iconify/deiconify, etc. I need it to react like this : if window is active , then texteditor gadget is active and cursor stays.
Same goes to cut/copy/paste : after it texteditor not active if i use toolbar buttons or menu when mouse outside of text editor.
So, thank you for idea with WHMI_INTUITICK, i make it as i want by this:
case WMHI_ACTIVE:
// Set flag when window becomes active
windowActive = TRUE;
break;
case WMHI_INTUITICK:
// Activate text editor if the window is active and no other gadget is active
if (window && windowActive)
{
// Check if another gadget is active in the top-level layout
struct Gadget *layoutGadget = (struct Gadget *)resizableLayout;
if (!(layoutGadget->Activation & GACT_ACTIVEGADGET))
{
// Activate the text editor gadget directly
IIntuition->ActivateGadget((struct Gadget *)textEditor, window, NULL);
}
}
break;
Is anyone aware how to check visibility of texteditor ? I mean, something like IIntuition->GetAttr(GA_Visible, td->textEditor, &visible); , but we have no GA_Visible sadly. Maybe there other ways ?
@all How implement "Select ALL" functionality ? AREXX commands for texteditor didn't have MARKALL or co, but they do have CURSOR UP/S,DOWN/S,LEFT/S,RIGHT/S, so is this only way like moving the cursor to the top-left (start) using repeated CURSOR UP and CURSOR LEFT commands then extending the selection to the bottom-right (end) using repeated CURSOR DOWN/S and CURSOR RIGHT/S commands, etc ?
EDIT: or maybe POSITION SOF/MARK ON/POSITION EOF ? EDIT2 : did it this way : POSITION EOF + GETCURSOR LINE + GETCURSOR COLUMN and at the end GM_TEXTEDITOR_MarkText.
Edited by kas1e on 2025/5/26 7:10:35 Edited by kas1e on 2025/5/26 7:10:58 Edited by kas1e on 2025/5/26 20:02:19
For scroller use GA_TextEdit_Prop_#? to get the top visible and total entries and use these to apply the equivalent SCROLLER attributes.
Where is better to do, same in INTUITICK ? I just tried, and while when i scroll by keys all ok, but if i grab scrollbar by mouse, it start jumping, setting top=0 always, like, some other gadget taking over..
@broadblues I’ve implemented at first in WMHI_INTUITICK case:
case WMHI_INTUITICK:
if (textEditor && customScroller)
{
uint32 topLine, totalLines, visibleLines;
// Get text editor attributes
IIntuition->GetAttr(GA_TEXTEDITOR_Prop_First, textEditor, &topLine);
IIntuition->GetAttr(GA_TEXTEDITOR_Prop_Entries, textEditor, &totalLines);
IIntuition->GetAttr(GA_TEXTEDITOR_Prop_Visible, textEditor, &visibleLines);
// Set scroller attributes to match text editor
IIntuition->SetGadgetAttrs((struct Gadget *)customScroller, window, NULL,
SCROLLER_Top, topLine,
SCROLLER_Total, totalLines,
SCROLLER_Visible, visibleLines,
TAG_END);
// Get scroller attributes for verification
uint32 scrollerTop, scrollerTotal, scrollerVisible;
IIntuition->GetAttr(SCROLLER_Top, customScroller, &scrollerTop);
IIntuition->GetAttr(SCROLLER_Total, customScroller, &scrollerTotal);
IIntuition->GetAttr(SCROLLER_Visible, customScroller, &scrollerVisible);
// Print both text editor and scroller attributes
IDOS->Printf("TextEditor Props: First=%ld, Entries=%ld, Visible=%ld\n",
topLine, totalLines, visibleLines);
IDOS->Printf("Scroller Props: Top=%ld, Total=%ld, Visible=%ld\n",
scrollerTop, scrollerTotal, scrollerVisible);
}
break;
The issue is that when I navigate the text editor using keyboard inputs (up/down/pgup/pgdown), the scroller updates correctly, reflecting the text editor’s position. However, when I interact with the scroller directly using the mouse (e.g., dragging it), the behavior becomes erratic: the scroller jumps, and the SCROLLER_Top value seems to "stick" to the last value set when navigating via the keyboard. This makes it impossible to move the scroller to the desired position accurately.
Could the issue be caused by the WMHI_INTUITICK event being consumed or interfered with by the text editor gadget? I will switch to an OM_NOTIFY way as you say, but I’m puzzled why WMHI_INTUITICK causes this erratic behavior.
No the issue is that your code always sets the scroller to the gader position, but never does the reverse. So when the gadget changes due to text input or cursor keys, the scroller updates, but the scroller is dragged, the code immediate outs back to the current gadget state.
You need to listen to both gadget changes and apply to scroller and scroller changes and apply to gadget. Both via OM_NOTIFY
WMHI_INTUITICK will triggering at least 10 times a second, you only want updates when things change.
@Andy For OM_NOTIFY should i install hook via OM_ADDMEMBER and then IDCMP_IDCMPUPDATE ? Or it can be done more easy way without messing with IDCMP msgs ?
@all Just can't get it, can maybe someone enligh me: OM_NOTIFY mean that we add ICA_MAP and ICA_TARGET when create an object, but then, how to listen them ? I found only in intuition/icclass.h that you should use IDCMP_IDCMPUPDATE, but then, how to catch it, if our loop are reaction based on while ((result = IIntuition->IDoMethod(win_obj, WM_HANDLEINPUT, &code)) != WMHI_LASTMSG). Puttin IDCMP_IDCMPUPDATE in this loop din't make it work, as it want messages to be send and replied, so mean while (msg = (struct IntuiMessage *)IExec->GetMsg(window->UserPort)) { way, then how to make it works both in the same loop, and WMHI_* ones and IDCMP_IDCMPUPDATE ?