I'm trying to have an "undo selection" button for a multi-select listview
So on every selchange event I'm saving the current selection in some global vector
On button click I restore lv.value = old_sel of this listview to some previously saved state. But I want these "artificial" selection changes to be ignored and not saved. I've tried setting a global nosave variable before setting the value and after it, but it seems to have no effect as, I guess, this artificial selection happens asynchronously
So how can I differentiate between these two types of selchange events?
So you're saying that sel_save within a hotkey event should be set to true only AFTER all selchange events from .value= are processed? Because all the selchange events print sel_save as true all the time even for these events
sel_save = false; // prevent this event from being recorded
dbg("undid selection, set sel_save to false");
lv.value=sel_ids;
dbg("reset sel_save to true");
sel_save = true; // prevent this event from being recorded
Thanks, have considered timers, but haven't tested them whether they'd differentiate between a key hold/multiselects and artificial send yet
When you improve this, please also add a way to "group" one batch of events together as otherwise I don't know how to do undo properly since the number of events can differ depending on the type of user action, so I don't know by how many events to go back to to restore a single action
I'm not sure we can group the events in any meaningful way. They come from the operating system as individual events, and we just forward them to the script.
I see, are you aware of any apps caring enough about user selection to preserve it (hate it when you carefuly multi-select something and then 1 wrong click ruins everything)?
What do they do?
I guess one option would be to carefully track key/mouse events to divine start/end of 1 user action, but then scripts don't have access to those, only its hotkeys
This example is straigt forward but will only work for event driven/asynchronous languages. So you change the var, then the selection and then the var again, but the event for the selection change will be evaluated in the same loop later, so this approach does not work. What will work is when you know the number of elements that changed and ignore selchange events that often. I prepared an example for you here
var ItemsChangedFromSoftware = 0;
var ListView;
function OnClick(clickData)
{
var dlg = DOpus.Dlg;
dlg.template = "dialog";
dlg.Create();
ListView = dlg.Control("listview1");
for(var i = 1; i < 5; i++)
{
var index = ListView.AddItem("item " + i);
ListView.GetItemAt(index).subitems(0) = "lbl"+i;
ListView.GetItemAt(index).subitems(1) = "path"+i;
}
while (true)
{
var Msg = dlg.GetMsg();
if (!Msg.result)
break;
if (Msg.event === "selchange")
OnSelectionChanged();
if (Msg.event === "click" && Msg.Control === "button1")
ChangeSelectionFromSoftware(DOpus.Create.Vector(0,2));// select 1st and 3rd items
}
}
function OnSelectionChanged()
{
if(ItemsChangedFromSoftware > 0) //Ignore as many events as items changed
{
Log("Software change");
ItemsChangedFromSoftware--;
return;
}
Log("Now user selection event");
}
function ChangeSelectionFromSoftware(selection)
{
ListView.value = selection;
ItemsChangedFromSoftware = selection.count;
}
function Log(msg, e)
{
DOpus.Output(String(msg), e || false);
}
I had some sort of issue with this in a dialog where some change in a combo box would modify many controls state (value, enabled, ...).
After the first callback ended, most of the modifications triggered events for the changes that I did not want to consider as user changes.
I used the rather new Dialog.FlushMsg() function in my first callback to avoid that. Since its a user action and first callback triggers quickly, the chance for another action from the user dropped by this FlushMsg seems very low.
Sounds like an even better solution, didnt know about that and just posted a solution I came up with some years ago since I struggled with that as well.
Another option is to go for the global variable flag but to set it not in the callback but in another callback that you trigger through custom messages you can now send in the message loop :
// sel_save = false; // prevent this event from being recorded
DOpus.SendCustomMsg("SetSaveStatus", -1);
dbg("undid selection, set sel_save to false");
lv.value=sel_ids;
dbg("reset sel_save to true");
// sel_save = true; // prevent this event from being recorded
DOpus.SendCustomMsg("SetSaveStatus", 0);
// You will have to code the callback to this new custom event, setting the global var according to the second parameter.