ContextMenu plugin

Discuss and announce AkelPad plugins
  • Author
  • Message
Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

Is it possible to implement the external call for the Link() the same way as it is done for the Favourites() method?
How it works for the Favourites() method:

Code: Select all

Call("ContextMenu::Main", 2, OPERATION)
  Parameters:
    2
      External call for favourite files list.
    OPERATION
      Operation number. [1...4] See description of Favourites() method.
How it could work for the Link() method:

Code: Select all

Call("ContextMenu::Main", NUMBER, OPERATION)
  Parameters:
    NUMBER [e.g. 3]
      External call for favourite files list.
    OPERATION
      Operation number. [1...6] See description of Links() method.
ContextMenu-Eng.txt:

Code: Select all

    Link(number)
      Link(1)
        method opens hyperlink (work in "URL menu" only).
      Link(2)
        method copies hyperlink (work in "URL menu" only).
      Link(3)
        method selects hyperlink (work in "URL menu" only).
      Link(4)
        method cuts hyperlink (work in "URL menu" only).
      Link(5)
        method replaces hyperlink with the clipboard text (work in "URL menu" only).
      Link(6)
        method deletes hyperlink (work in "URL menu" only).
    Favourites(number)
      Favourites(1)
        method adds current file to favourites (with dialog).
      Favourites(2)
        method adds current file to favourites (without dialog).
      Favourites(3)
        method opens dialog to manage favourites.
      Favourites(4)
        method deletes current file from favourites.

Offline
Site Admin
Posts: 6430
Joined: Thu Jul 06, 2006 7:20 am

Re: ContextMenu plugin

Post by Instructor »

yozhic wrote: Tue Nov 25, 2025 4:26 pm 1. В SET(32) обработку ИЛИ. Чтобы пункт отображался при наличии хотя бы одного из перечисленных файлов.

Это могло бы пригодиться для более аккуратной работы с вложенными меню (submenu, субменю).
Создано вложенное меню «Статистика», где часть пунктов вызывают плагин Stats, а часть — скрипты. Каждый пункт этого субменю обёрнут в SET(32) для Stats.dll и Scripts.dll, но если ни одного из этих плагинов нет, то и отображать вход в субменю, кажется, не имеет смысла. Т.е. подразумевается нечто следующее:

Code: Select all

SET(32, Stats.dll ИЛИ Scripts.dll)
  "Statistics" Menu("STATISTICS")
UNSET(32)
Или создано субменю «Открыть с помощью...», в котором перечислено несколько программ для передачи текущего файла. Если ни одной из них нет, чтобы и само меню не отображалось.

Всё это, по идее, можно сделать с помощью скрипта, но тогда надо, чтобы был плагин Scripts. Т.е. получается неполнота, лишняя зависимость.
Да, чтобы это релизовать необходимо использовать возможности Scripts:

Code: Select all

SET(64, If(`Call("Scripts::Main", 4, "EvalCmd.js", '0x3; var oSys=AkelPad.SystemFunction(); if (oSys.Call("kernel32::GetFileAttributesW", "%a\AkelFiles\Plugs\Stats.dll") != -1 && oSys.Call("kernel32::GetFileAttributesW", "%a\AkelFiles\Plugs\Scripts.dll") != -1) AkelPad.ScriptExitCode(1);')`))
  "Statistics" Menu("STATISTICS")
UNSET(64)
2. Если текущая вкладка/документ не имеет имени (не сохранена), то, кажется, нет смысла отображать такие пункты, как «Меню Explorer», «Copy path», «Reveal in Explorer». Но сейчас их никак не скрыть. Точнее можно, но сработает это единожды при старте программы, а при переключении по вкладкам ничего меняться не будет.
В этом случае нужно регулировать статус пункта (SET 128), но не видимость.
ewild wrote: Thu Feb 19, 2026 9:01 am Is it possible to implement the external call for the Link() the same way as it is done for the Favourites() method?
How should this work? The external call won't know which link you want to work with.

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

Code: Select all

    Link(number)
      Link(1)
        method opens hyperlink (work in "URL menu" only).
      Link(2)
        method copies hyperlink (work in "URL menu" only).
      Link(3)
        method selects hyperlink (work in "URL menu" only).
      Link(4)
        method cuts hyperlink (work in "URL menu" only).
      Link(5)
        method replaces hyperlink with the clipboard text (work in "URL menu" only).
      Link(6)
        method deletes hyperlink (work in "URL menu" only).
Specifically, I'm interested in Link(3).
However, I believe other methods could also find their use in some cases.

As to my case, please find a part of the example script below.

Currently, I need to manually preselect a hyperlink at first in one way or another: using Link(3) from the "URL menu" manually, literally manually with the mouse or keyboard, or by placing the caret (by left-click or keyboard) anywhere in line with a hyperlink (then that line will be selected automatically by the script). Then my script utilizes the selection-related methods to process the hyperlink.

With the newly proposed feature (if it's possible to implement one), I expect I could right-click the hyperlink and right away invoke my script from the "URL menu" without any intermediate steps, when the hyperlink selection would occur automatically and internally within the script (with something like the proposed Link() based method), and as soon as the hyperlink gets selected the script could proceed as usual with the rest of its stuff.

The example script:

Code: Select all

// fixURLs.js : decode, clean or encode URLs
// "URL decode" Call("Scripts::Main",1,"fixURLs.js","decode") Icon("%a\Icons.dll",178)
// "URL clean" Call("Scripts::Main",1,"fixURLs.js","clean") Icon("%a\Icons.dll",246)
// "URL encode" Call("Scripts::Main",1,"fixURLs.js","encode") Icon("%a\Icons.dll",179)

// get command-line argument
ArgLine=AkelPad.GetArgLine();

// get scroll position
//var window = AkelPad.GetEditWnd();
//var system = AkelPad.SystemFunction();
//var scroll = getScroll(window);

// get selection
start=AkelPad.GetSelStart();
end=AkelPad.GetSelEnd();
if (start == end)
AkelPad.SetSel(0,-1);
text=AkelPad.GetSelText();

// encode URLs
if (/encode/.test(ArgLine)){
text=encodeURIComponent(text);// encode
AkelPad.ReplaceSel(text);}// finalize encoding

// decode URLs
if (/decode/.test(ArgLine)){

// within explicit selection only (to prevent unmatched % errors in regular text)
// see: https://www.google.com/search?q=decodeURIComponent()+The+URI+to+be+decoded+is+not+a+valid+encoding
if (start != end){
text=decodeURIComponent(text)};// decode

// within current line (with the caret) only (for the same reasons as above)
else {
AkelPad.SetSel(start,start);// set caret at initial selection start
up = '.*';// backward extension pattern
AkelPad.TextFind(0,up,0x001C0000);// select leading segment from the caret to the current line start
startup = AkelPad.GetSelStart();// get updated start offset

AkelPad.SetSel(end,end);// set caret at initial selection end
down = '(?m-s).*?[\\r]?';// forward extension pattern
AkelPad.TextFind(0,down,0x000C0001);// select trailing segment from the caret to the current line end
endup = AkelPad.GetSelEnd();// get updated end offset

AkelPad.SetSel(startup,endup);// set updated selection
text = AkelPad.GetSelText();// get selected text
text=decodeURIComponent(text);}//decode

AkelPad.ReplaceSel(text);}// finalize decoding

AkelPad.SetSel(start,start);// restore caret position
//setScroll(window,scroll);// restore scroll position

//function getScroll(handle)
//function setScroll(handle,position)
Last edited by ewild on Tue Mar 03, 2026 8:03 am, edited 2 times in total.

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

ewild wrote: Tue Mar 03, 2026 3:08 am

Code: Select all

// fixURLs.js : decode, clean or encode URLs
// "URL decode" Call("Scripts::Main",1,"fixURLs.js","decode") Icon("%a\Icons.dll",178)
// "URL clean" Call("Scripts::Main",1,"fixURLs.js","clean") Icon("%a\Icons.dll",246)
// "URL encode" Call("Scripts::Main",1,"fixURLs.js","encode") Icon("%a\Icons.dll",179)

// get command-line argument
ArgLine=AkelPad.GetArgLine();

// get selection
start=AkelPad.GetSelStart();
end=AkelPad.GetSelEnd();
if (start == end)
AkelPad.SetSel(0,-1);
text=AkelPad.GetSelText();

// encode URLs
if (/encode/.test(ArgLine)){
text=encodeURIComponent(text);// encode
AkelPad.ReplaceSel(text);}// finalize encoding

// decode URLs
if (/decode/.test(ArgLine)){

// within explicit selection only (to prevent unmatched % errors in regular text)
// see: https://www.google.com/search?q=decodeURIComponent()+The+URI+to+be+decoded+is+not+a+valid+encoding
if (start != end){
text=decodeURIComponent(text)};// decode

// within current line (with the cursor) only (for the same reasons as above)
else {
AkelPad.SetSel(start,start);// set cursor at initial selection start
up = '.*';// extend-selection-backward pattern
AkelPad.TextFind(0,up,0x001C0000);// select leading line segment from cursor to its start
startup = AkelPad.GetSelStart();// get updated start offset

AkelPad.SetSel(end,end);// set cursor at initial selection end
down = '(?m-s).*?[\\r]?';// extend-selection-forward pattern
AkelPad.TextFind(0,down,0x000C0001);// select trailing line segment from cursor to its end
endup = AkelPad.GetSelEnd();// get updated end offset

AkelPad.SetSel(startup,endup);// set updated selection
text = AkelPad.GetSelText();// get selected text
text=decodeURIComponent(text);}//decode

AkelPad.ReplaceSel(text);}// finalize decoding

AkelPad.SetSel(start,start);// restore cursor position
//setScroll(window,scroll);// restore scroll position
By the way, here I have to deal with an AkelPad's bug(?), when the app ignores the /*FRF_REGEXPNONEWLINEDOT*/ flag in the border conditions, where the cursor is placed and/or the selection starts/ends at the beginning or at the end of a line.

Here's a script I made to visualize the issue and to make a workaround:

Code: Select all

// https://akelpad.sourceforge.net/forum/viewtopic.php?t=240&p=37030#p37030
// AkelPad_extendSelectionInline.js :: step by step analyzis
// 2025-04-01 ewild
// mode 1: if there's no actual selection: extend selection so the entire line under the cursor gets selected
// mode 2: if there's an actual selection: extend selection so the entire segment gets selected
//          - from the beginning of the line the initial selection start has belonged to
//          - to the end of the line the initial selection end has belonged to
// "extend selection fix" Call("Scripts::Main",1,"AkelPad_extendSelectionInline.js") Icon("%a\Icons.dll",171)

hWndEdit = AkelPad.GetEditWnd();// edit window

// initial cursor position, remember it
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);
nCursorIni = nCursor;

// step 1: initial selection
nStart  = AkelPad.GetSelStart();nStartIni=nStart;// initial selection start offset, remember it
nEnd    = AkelPad.GetSelEnd();nEndIni=nEnd;// initial selection end offset, remember it
pText   = AkelPad.GetSelText();// initial selection text (if any)
title   = 'step 1: initial selection';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,0/*Replace all text in the output panel*/);
WScript.Echo(title+'\r\r\rOK to continue...');

// step 2: select leading segment from the cursor to the current line start
pattern = '.*';// backward extension pattern
AkelPad.TextFind(0,pattern,0x001C0000/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_UP*/);// find up/backward from cursor
//AkelPad.TextFind(0,pattern,0x00180000/*FRF_REGEXP|FRF_UP*/);
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// current cursor position
nStart  = AkelPad.GetSelStart();nStartExt=nStart;// extended selection start offset, remember it
nEnd    = AkelPad.GetSelEnd();// current selection end offset
pText   = AkelPad.GetSelText();// currently selected text (has to be line's leading segment)
title   = 'step 2: selection from the cursor to the current line start';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);
WScript.Echo(title+'\r\r\rOK to continue...');

AkelPad.SetSel(nEndIni,nEndIni);// set cursor at initial selection end

// step 3: select trailing segment from the cursor to the current line end
pattern = '(?m-s).*?[\\r]?';// forward extension pattern
AkelPad.TextFind(0,pattern,0x000C0001/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_DOWN*/);// find down/forward from cursor
//AkelPad.TextFind(0,pattern,0x00080001/*FRF_REGEXP|FRF_DOWN*/);
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// cursor position
nStart  = AkelPad.GetSelStart();// current selection start offset
nEnd    = AkelPad.GetSelEnd();nEndExt=nEnd;// extended selection end offset, remember it
pText   = AkelPad.GetSelText();// currently selected text (has to be line's trailing segment)
title   = 'step 3: selection from the cursor to the current line end';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);
WScript.Echo(title+'\r\r\rOK to continue...');

// step 4: make entire extended selection
AkelPad.SetSel(nStartExt,nEndExt);// set target extended selection
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// final cursor position
nStart  = AkelPad.GetSelStart();// extended selection start offset
nEnd    = AkelPad.GetSelEnd();// extended selection end offset
pText   = AkelPad.GetSelText();// currently extended text (has to be the valid target text)
title   = 'step 4: entire text selected from start to end';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);
WScript.Echo(title+'\r\r\rOK to continue...');

// step 5: fix
if (pText.match(/^[\r]/g) || pText.match(/[\r]$/g)){
nStartFix=nStartExt;nEndFix=nEndExt;// initialize fixed selection offset variables
if (pText.match(/^[\r]/g)){nStartFix = nStartExt+1;}// fix selection start offset
if (pText.match(/[\r]$/g)){nEndFix = nEndExt-1;}// fix selection end offset
AkelPad.SetSel(nStartFix,nEndFix);// set fixed selection
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// cursor position
nStart  = AkelPad.GetSelStart();// fixed selection start offset
nEnd    = AkelPad.GetSelEnd();// fixed selection end offset
//pText = pText.replace(/^\r/g,"").replace(/\r$/g,"");// fix selected text
pText   = AkelPad.GetSelText();// fixed text (has to be valid target text)
title   = 'step 5: valid selection (fixed)';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText;
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);}

//AkelPad.SetSel(nCursorIni,nCursorIni);// restore cursor initial position

// Scripts-Eng.txt
// 0x00040000 /*FRF_REGEXPNONEWLINEDOT*/ dot symbol '.' in regular expressions specifies any character except new line
// 0x00080000 /*FRF_REGEXP*/ search with regular expressions
// 0x00100000 /*FRF_UP*/ find up
// 0x00000001 /*FRF_DOWN*/ find down
// 0x00180000 /*FRF_REGEXP|FRF_UP*/ find up + regex
// 0x00080001 /*FRF_REGEXP|FRF_DOWN*/ find down + regex
// 0x001C0000 /*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_UP*/ find up + regex + dot-excludes-new-line
// 0x000C0001 /*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_DOWN*/ find down + regex + dot-excludes-new-line

  1. Regarding the original task (to extend the selection), the script seemingly works like a charm if the cursor is not placed and/or the selection does not start/end at the beginning or at the end of a line.
  2. As soon as the cursor is placed and/or the selection does start/end at the beginning or at the end of a line, AkelPad ignores the very first (adjacent to the cursor or to the respective selection edge) occurrence of the new-line upon AkelPad.TextFind() up, and AkelPad.TextFind() down (in the respective direction).
  3. Without the issue, the pattern = '.*' would be enough in both cases.
  4. With the issue, the weird pattern = '(?m-s).*?[\\r]?' is required for the AkelPad.TextFind() down to prevent AkelPad from capturing the next line beyond the initial selection. And even then, it still captures the trailing new line.
  5. With the issue, the 'step 5' in the script is required as a workaround for the said border cases to get valid target selection.
Note: Any multiline text is suitable for testing. I use the following one:

Code: Select all

ABCDEFGHIJ KLMNOPQRST UVWXYZÄÖÜẞ
0123456789 0123456789 0123456789
0123456789 0123456789 0123456789
0123456789 0123456789 0123456789
ABCDEFGHIJ KLMNOPQRST UVWXYZÄÖÜẞ
To see the issue, for instance (before running the script):
  • place the cursor at the beginning of lines 2, 3, or 4 - before the digit 0, or at the end of lines 2, 3, or 4 - after the digit 9, or
  • start the selection at the beginning of lines 2, 3, or 4 - from the digit 0, including the digit 0, and/or
    end the selection at the end of lines 2, 3, or 4 - at the digit 9, including the digit 9.
Last edited by ewild on Tue Mar 03, 2026 10:17 am, edited 6 times in total.

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

By the way #2.
Currently, as I can see, the hyperlink highlighting feature is limited in length by the AkelPad(?) with a strict 512-character maximum.
For any longer hyperlink (512+1 characters and more), the rest will be rendered as regular text.
Maybe it's worth increasing the limit, and even making it user-configurable?

Google says the limit is 2097152 chars for Chrome, ~80000 for Safari, functionally unlimited (but the address bar stops displaying it after 65536 chars) for Firefox, and 2083 chars for Edge/IE.

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

https://sourceforge.net/p/akelpad/codesvn/4530/log/

Code: Select all

[r4530] (HEAD) by  instructor_ 2026-03-04 01:27:57
Fixed: finding ".*" up with AEFR_REGEXP|AEFR_REGEXPNONEWLINEDOT flags.
[r4529] by  instructor_ 2026-03-03 23:31:23
Added: variable %lb - URL start position (work in "URL menu" only).
Added: variable %le - URL end position (work in "URL menu" only).
Instructor
Looks like those changes are somehow related to my messages here in this topic.
Thank you very much for your efforts.

However, unfortunately, as for me (at least as I can see and understand it) "Fixed: finding ".*" up" doesn't actually fix anything, rather it makes things even worse.
Now, neither pattern=".*" nor even pattern="[^\\r]*" work properly for me for
AkelPad.TextFind(0,pattern,0x001C0000/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_UP*/);// find up/backward from cursor
AkelPad.TextFind(0,pattern,0x000C0001/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_DOWN*/);// find down/forward from cursor
Now, in the said border situations, both of them jump over the adjacent newline characters and catch the entire neighbouring lines.
Thus, to make it work (with the fixing step 5 still required), I'm forced to change the patterns as follows:

Code: Select all

// step 2: select leading segment from the cursor to the current line start
// '.*','[^\\r]*' patterns do not work here, looks like a bug
// '(?m-s)[\\r]*.*?' pattern at least prevents AkelPad.TextFind () up to catch the entire previous line
pattern = '(?m-s)[\\r]*';// backward extension pattern
AkelPad.TextFind(0,pattern,0x001C0000/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_UP*/);// find up/backward from cursor
...
// step 3: select trailing segment from the cursor to the current line end
// '.*','[^\\r]*' patterns do not work here, looks like a bug
// '(?m-s).*?[\\r]*' pattern at least prevents AkelPad.TextFind () down to catch the entire following line
pattern = '(?m-s).*?[\\r]*';// forward extension pattern
AkelPad.TextFind(0,pattern,0x000C0001/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_DOWN*/);// find down/forward from cursor
The entire script for testing:

Code: Select all

// https://akelpad.sourceforge.net/forum/viewtopic.php?t=240&p=37030#p37030
// AkelPad_extendSelectionInline.js :: step by step analyzis
// 2025-04-01 ewild
// mode 1: if there's no actual selection: extend selection so the entire line under the cursor gets selected
// mode 2: if there's an actual selection: extend selection so the entire segment gets selected
//          - from the beginning of the line the initial selection start has belonged to
//          - to the end of the line the initial selection end has belonged to
// "extend selection fix" Call("Scripts::Main",1,"AkelPad_extendSelectionInline.js") Icon("%a\Icons.dll",171)

hWndEdit = AkelPad.GetEditWnd();// edit window

// initial cursor position, remember it
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);
nCursorIni = nCursor;

// step 1: initial selection
nStart  = AkelPad.GetSelStart();nStartIni=nStart;// initial selection start offset, remember it
nEnd    = AkelPad.GetSelEnd();nEndIni=nEnd;// initial selection end offset, remember it
pText   = AkelPad.GetSelText();// initial selection text (if any)
title   = 'step 1: initial selection';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,0/*Replace all text in the output panel*/);
WScript.Echo(title+'\r\r\rOK to continue...');

// step 2: select leading segment from the cursor to the current line start
// '.*','[^\\r]*' patterns do not work here, looks like a bug
// '(?m-s)[\\r]*.*?' pattern at least prevents AkelPad.TextFind () up to catch the entire previous line
pattern = '(?m-s)[\\r]*';// backward extension pattern
AkelPad.TextFind(0,pattern,0x001C0000/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_UP*/);// find up/backward from cursor
//AkelPad.TextFind(0,pattern,0x00180000/*FRF_REGEXP|FRF_UP*/);
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// current cursor position
nStart  = AkelPad.GetSelStart();nStartExt=nStart;// extended selection start offset, remember it
nEnd    = AkelPad.GetSelEnd();// current selection end offset
pText   = AkelPad.GetSelText();// currently selected text (has to be line's leading segment)
title   = 'step 2: selection from the cursor to the current line start';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);
WScript.Echo(title+'\r\r\rOK to continue...');

AkelPad.SetSel(nEndIni,nEndIni);// set cursor at initial selection end

// step 3: select trailing segment from the cursor to the current line end
// '.*','[^\\r]*' patterns do not work here, looks like a bug
// '(?m-s).*?[\\r]*' pattern at least prevents AkelPad.TextFind () up to catch the entire following line
pattern = '(?m-s).*?[\\r]*';// forward extension pattern
AkelPad.TextFind(0,pattern,0x000C0001/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_DOWN*/);// find down/forward from cursor
//AkelPad.TextFind(0,pattern,0x00080001/*FRF_REGEXP|FRF_DOWN*/);
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// cursor position
nStart  = AkelPad.GetSelStart();// current selection start offset
nEnd    = AkelPad.GetSelEnd();nEndExt=nEnd;// extended selection end offset, remember it
pText   = AkelPad.GetSelText();// currently selected text (has to be line's trailing segment)
title   = 'step 3: selection from the cursor to the current line end';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);
WScript.Echo(title+'\r\r\rOK to continue...');

// step 4: make entire extended selection
AkelPad.SetSel(nStartExt,nEndExt);// set target extended selection
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// final cursor position
nStart  = AkelPad.GetSelStart();// extended selection start offset
nEnd    = AkelPad.GetSelEnd();// extended selection end offset
pText   = AkelPad.GetSelText();// currently extended text (has to be the valid target text)
title   = 'step 4: entire text selected from start to end';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText+'\r\r\r';
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);
WScript.Echo(title+'\r\r\rOK to continue...');

// step 5: fix
if (pText.match(/^[\r]/g) || pText.match(/[\r]$/g)){
nStartFix=nStartExt;nEndFix=nEndExt;// initialize fixed selection offset variables
if (pText.match(/^[\r]/g)){nStartFix = nStartExt+1;}// fix selection start offset
if (pText.match(/[\r]$/g)){nEndFix = nEndExt-1;}// fix selection end offset
AkelPad.SetSel(nStartFix,nEndFix);// set fixed selection
nCursor = AkelPad.SendMessage(hWndEdit,3138/*AEM_GETRICHOFFSET*/,5/*AEGI_CARETCHAR*/,0);// cursor position
nStart  = AkelPad.GetSelStart();// fixed selection start offset
nEnd    = AkelPad.GetSelEnd();// fixed selection end offset
pText   = AkelPad.GetSelText();// fixed text (has to be valid target text)
title   = 'step 5: valid selection (fixed)';
message = title+
'\rselection start : '+nStart+
'\rcursor          : '+nCursor+
'\rselection end   : '+nEnd+
'\rselected text   : '+pText;
AkelPad.Call("Log::Output",4,message,-1,2/* Add text in the output panel in new line */);}

//AkelPad.SetSel(nCursorIni,nCursorIni);// restore cursor initial position

// Scripts-Eng.txt
// 0x00040000 /*FRF_REGEXPNONEWLINEDOT*/ dot symbol '.' in regular expressions specifies any character except new line
// 0x00080000 /*FRF_REGEXP*/ search with regular expressions
// 0x00100000 /*FRF_UP*/ find up
// 0x00000001 /*FRF_DOWN*/ find down
// 0x00180000 /*FRF_REGEXP|FRF_UP*/ find up + regex
// 0x00080001 /*FRF_REGEXP|FRF_DOWN*/ find down + regex
// 0x001C0000 /*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_UP*/ find up + regex + dot-excludes-new-line
// 0x000C0001 /*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_DOWN*/ find down + regex + dot-excludes-new-line


Simple example text for testing:

Code: Select all

ABCDEFGHIJ KLMNOPQRST UVWXYZÄÖÜẞ
0123456789 0123456789 0123456789
0123456789 0123456789 0123456789
0123456789 0123456789 0123456789
ABCDEFGHIJ KLMNOPQRST UVWXYZÄÖÜẞ

Offline
Site Admin
Posts: 6430
Joined: Thu Jul 06, 2006 7:20 am

Re: ContextMenu plugin

Post by Instructor »

ewild wrote: Tue Mar 03, 2026 3:08 amCurrently, I need to manually preselect a hyperlink at first in one way or another...
With ContextMenu v18.3 you can use %lb and %le variables.

Code: Select all

-"Select URL" Call("Scripts::Main", 1, "SelectURL.js", "-LinkBegin=%lb -LinkEnd=%le")

Code: Select all

var nLinkBegin=AkelPad.GetArgValue("LinkBegin", 0);
var nLinkEnd=AkelPad.GetArgValue("LinkEnd", 0);

AkelPad.SetSel(nLinkBegin, nLinkEnd);

ewild wrote: Tue Mar 03, 2026 3:51 amBy the way, here I have to deal with an AkelPad's bug(?), when the app ignores the /*FRF_REGEXPNONEWLINEDOT*/ flag in the border conditions, where the cursor is placed and/or the selection starts/ends at the beginning or at the end of a line.
Test version x86 / x64
ewild wrote: Tue Mar 03, 2026 7:38 amCurrently, as I can see, the hyperlink highlighting feature is limited in length by the AkelPad(?) with a strict 512-character maximum.
You can configurate it with AEM_SETURLMAXLENGTH message, but be careful, this affects the performance of working with text.
Now, neither pattern=".*" nor even pattern="[^\\r]*" work for me for... Now, in the said border situations, both of them jump over the adjacent newlines and catch the entire neighbouring lines.
Yes. How should it work in your opinion?

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

Please, find a video I've made to show the actual issue with the ".*" pattern for AkelPad.TextFind () up|down as I see it:
https://seyarabata.com/69a800c559818 (.mp4, 2.93 MB, 4:10)
Points 3. and 4. in the video are two examples of the bordering situations that I'm talking about.

AkelPad.TextFind(0,".*",0x001C0000/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_UP*/);// find up/backward from cursor
AkelPad.TextFind(0,".*",0x000C0001/*FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_DOWN*/);// find down/forward from cursor
Both should not match the adjacent newline characters as they do now.

NB. Yes, I've been working with the latest (today's) AkelPad build [r4530].

Edit:
The issue has been fixed with the new FRF_REGEXPMINMATCH flag (0x00020000) flag (see Instructor's message below).
Still, a bit earlier, I made another, an extended video covering more examples of the bordering situation (just couldn't share it in time).
So I'm just leaving it here for future readers:
https://seyarabata.com/69a9c991b470c (.mp4, 8.59 MB, 8:41)
Tests 3-8 in the video are six examples of the said bordering situations.
Last edited by ewild on Thu Mar 05, 2026 7:07 pm, edited 3 times in total.

Offline
Site Admin
Posts: 6430
Joined: Thu Jul 06, 2006 7:20 am

Re: ContextMenu plugin

Post by Instructor »

ewild
In test version x86 / x64 you can use FRF_REGEXPMINMATCH flag (0x00020000).

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

Instructor wrote: Wed Mar 04, 2026 9:35 am With ContextMenu v18.3 you can use %lb and %le variables.

Code: Select all

-"Select URL" Call("Scripts::Main", 1, "SelectURL.js", "-LinkBegin=%lb -LinkEnd=%le")

Code: Select all

var nLinkBegin=AkelPad.GetArgValue("LinkBegin", 0);
var nLinkEnd=AkelPad.GetArgValue("LinkEnd", 0);
AkelPad.SetSel(nLinkBegin, nLinkEnd);
Looks promising, I'll definitely look into that. Thank you!


Instructor wrote: Wed Mar 04, 2026 9:35 am You can configurate it with AEM_SETURLMAXLENGTH message, but be careful, this affects the performance of working with text.
For me, that's more than enough. Thank you!
For future readers:
By default, AkelPad limits the maximum length of a detected URL to 512 characters.
To change the maximum length to a different value (e.g., 1024 characters), you can use the following script command (full syntax):

Code: Select all

AkelPad.SendMessage(AkelPad.GetEditWnd(),3254/*AEM_SETURLMAXLENGTH*/,1024,0);


Instructor wrote: Wed Mar 04, 2026 9:33 pm ewild
In test version x86 / x64 you can use FRF_REGEXPMINMATCH flag (0x00020000).
Instructor
This is just great! I could hardly wish for more!
That flag makes the difference! Thank you a lot!
For future readers:
With said flag, the related commands in my scripts (from my previous messages here) become as follows (full syntax):

Code: Select all

find = '.*';// selection extension pattern
AkelPad.TextFind(0,find,0x001E0000/*FRF_UP|FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_REGEXPMINMATCH*/);// find up/backward
AkelPad.TextFind(0,find,0x000E0001/*FRF_DOWN|FRF_REGEXP|FRF_REGEXPNONEWLINEDOT|FRF_REGEXPMINMATCH*/);// find down/forward

Online
Posts: 303
Joined: Mon Jun 20, 2011 8:33 am
Location: Электросталь

Re: ContextMenu plugin

Post by yozhic »

Instructor wrote:В этом случае нужно регулировать статус пункта (SET 128), но не видимость.
Спасибо. А можно ли сделать, чтобы SET 128 срабатывал ещё и для пунктов с подменю? Т.е. сейчас команда:

Code: Select all

SET(128, If(`SendMain(1223 /*AKD_GETFRAMEINFO*/, 33 /*FI_FILELEN*/, 0)`, 0x0, 0x2))
"Explorer"
{
  EXPLORER
}
UNSET(128)
не меняет статус пункта "Explorer", он не становится серым. А хотелось бы :)

И ещё во время тестов кажется всплыл косячок. Вот при такой записи в Tab menu:

Code: Select all

Пункт...
Пункт...
EXPLORER
Пункт...
Пункт...
происходит вот что
На видео у меня AkelPad r4531 x64, MDI, Win 11 23H2. Открыто два файла во вкладках. При первом вызове меню в первой вкладке оно отображается корректно, а после переключения в другую вкладку — «ломается».

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

Instructor wrote: Wed Mar 04, 2026 9:35 am With ContextMenu v18.3 you can use %lb and %le variables.

Code: Select all

-"Select URL" Call("Scripts::Main", 1, "SelectURL.js", "-LinkBegin=%lb -LinkEnd=%le")

Code: Select all

var nLinkBegin=AkelPad.GetArgValue("LinkBegin", 0);
var nLinkEnd=AkelPad.GetArgValue("LinkEnd", 0);

AkelPad.SetSel(nLinkBegin, nLinkEnd);
Instructor
Just been testing ways to integrate that into my scripts...

Looks like a bug:
  • Once successfully defined upon a valid request, %lb,%le offsets will reside in memory (being accessible outside of the ContextMenu URL menu) until redefined by another valid request, or until the program restarts
How to reproduce:
  1. Firts call:
    Enter a window with at least one URL in a text.
    Right-click the URL and run the "ContextMenu URL menu item":
    -"Select URL" Call("Scripts::Main", 1, "SelectURL.js", "-LinkBegin=%lb -LinkEnd=%le")
    Result: the URL gets selected (within the %lb,%le offsets), everything works as intended.
  2. Second and the following calls:
    • A: Do not close AkelPad and enter the same window with the same text/URL.
      Deselect the URL.
      Run the "SelectURL.js" script from somewhere outside of the "ContextMenu URL menu".
      Result: the URL still gets selected.
    • B: Do not close AkelPad and enter another window with enough text.
      Run the "SelectURL.js" script from somewhere outside of the "ContextMenu URL menu".
      Result: random text within the initial %lb,%le offsets will be selected.
    • C: Do not close AkelPad and run something like "GetArgs.js" from the regular ContextMenu.

      Code: Select all

      // "GetArgs" Call("Scripts::Main",1,"GetArgs.js","-LinkBegin=%lb -LinkEnd=%le")
      
      var pArgLine   = AkelPad.GetArgLine();
      
      var nLinkBegin = AkelPad.GetArgValue("LinkBegin",0);
      var nLinkEnd   = AkelPad.GetArgValue("LinkEnd",0);
      
      AkelPad.SetSel(nLinkBegin,nLinkEnd);
      
      WScript.Echo(
      'ArgLine:\r'+
      'type  = '+AkelPad.VarType(pArgLine)+'\r'+
      'value = '+pArgLine+'\r'+
      '------------------\r'+
      'ArgValues:\r'+
      'nLinkBegin type = '+AkelPad.VarType(nLinkBegin)+'; value = '+nLinkBegin+'\r'+
      'nLinkEnd   type = '+AkelPad.VarType(nLinkEnd)  +'; value = '+nLinkEnd);

      Result: %lb,%le offsets are non-zero and always there (equalling the initial ones got upon the very first call), until redefined by another valid request, or until the program restarts.
Last edited by ewild on Thu Mar 12, 2026 4:59 pm, edited 1 time in total.

Offline
Posts: 61
Joined: Sat Jul 05, 2008 11:30 am
Location: Odesa, Ukraine

Re: ContextMenu plugin

Post by ewild »

So, I believe, at least there should be a way to explicitly reset the %lb,%le variables from the initiating script as soon as their first call gets finished, and they are no longer required, and before the script exits.
Post Reply