SOLVED TreeView Multiple Selection With LMB

Yes, but you don't check for arrayCell.

If you could provide the minimal piece of code that still reproduce your crash, this way I could reproduce the same issue you are facing and can try to help you (help us to help you).
So at least GetNext/GetFirst/GetDown/IsSelected/GetName/GetID/GetPred/Select methods.

If you are interested in Treeview you may find valuable information in:


@m_adam Here are required functions:

void* Tree::GetFirst(void* root, void* userdata)
	TreeNode* obj = (TreeNode*)root;
	return obj;

void* Tree::GetNext(void* root, void* userdata, void* obj)
	TreeNode* res = (TreeNode*)obj;

	if (res && res->next)
		return res->next;
	return NULL;

void* Tree::GetPred(void* root, void* userdata, void* obj)
	TreeNode* res = (TreeNode*)obj;

	if (res && res->pred)
		return res->pred;
	return NULL;

void* Tree::GetDown(void* root, void* userdata, void* obj)
	TreeNode* res = (TreeNode*)obj;
	if (res && res->root && res->down)
		return res->down;

	return NULL;

String Tree::GetName(void* root, void* userdata, void* obj)
	if (!obj) return "";
	TreeNode* res = (TreeNode*)obj;
	if (res->root) {
		String name = res->cell->second.GetString(OBJSET_NAME);
		if (res->cell->second.GetInt32(OBJSET_ID))
			return name;
		else {
			Filename fn(name);
			return fn.GetFile().GetString();
	return std::get<2>(*(res->arrayCell)).GetString(DESCPREF_NAME);

Int Tree::GetId(void* root, void* userdata, void* obj)
	return (Int)obj;

Bool Tree::IsSelected(void* root, void* userdata, void* obj)
	TreeNode* res = (TreeNode*)obj;
	if (!res) return false;
	if (res->root) 
		return std::find(parent->objID.begin(), parent->objID.end(), &(*res->cell)) != parent->objID.end();
	return std::find(parent->clickID.begin(), parent->clickID.end(), &(*res->arrayCell)) != parent->clickID.end();

And Select() from post above. None of my attempts to check for nullptr helped, so nothing new to provide for that function.
My code with stuff like &(*res->cell)) and &(*res->arrayCell)) may look unsafe, but it works as intended. If i select with clicking and holding SHIFT button, everything works fine, but when i try to drag LMB i get instant crash even when there are no objects in drag rectangle.


"When i select with mouse drag, what does Select get as obj?"
Normally Select shouldn't be called when you hold a click while dragging.

Just to clarify and I should have asked first, but how do you select multiple items?

  1. By clicking all of them individually.
    • Select will be called each time you click.
  2. By clicking on one entry, then SHIFT + click on another one.
    • For the first click, Select will be called with SELECTION_NEW.
    • For the second click, Select will be called over each other entry with SELECTION_ADD.
  3. By clicking on one entry, holding the click, drag the mouse down/up, and release the mouse.
    • I'm not sure I would need to try. But in any case, DragStart will be called to see what to do with the currently selected object and if they are allowed to be dragged. But you can return only TREEVIEW_DRAGSTART_SELECT this way object will be selected (but I'm not sure) or you will need to directly override MouseDown to implement this behavior.

Note that I overlooked, the fact you didn't override DragStart. Which define what to do when a drag operation is starting, Selection state, and if the object can be dragged.

But just to recap the dragging process:

  • Drag is starting from the TreeView:
    • DragStart Called when the Drag operation start, to know what to do. Should return TREEVIEW_DRAGSTART_ALLOW and/or TREEVIEW_DRAGSTART_SELECT to inform what to do.
    • GetDragType Called after DragStart. Should return the drag type the current cell is representing.
    • According to the type returned by GetDragType:
    • SetDragObject is called, so we can store the dragged obj if we want to.
  • Drop received:
    • AcceptDragObject is called to see if we accept the drag object. obj can be any kind, this is why you need to check the value of dragtype. Should return, if obj should be inserted and where it should be.
    • InsertObject is the called (if it's an array-like in the case of DRAGTYPE_ATOMARRAY, the function will be called once, with obj* being an AtomArray). It's up to you to do proper insertion within your structure.
    • A Refresh and a redraw will be done to update the treeview according to the new internal Data Structure.

In any case I can't really help you more, I still miss, MainMap, MainMapCellArray class InsertObject, AcceptDragObject, GetDragType, DragStart.
And please provide also the GeDialog or at least the option you used to register the treeview.

Hope this help a bit more,

@m_adam I select them with third case, by clicking on one, holding, dragging and releasing mouse.
Thanks for useful information, i'm gonna try everything what you suggesed and answer on my results here.

@m_adam Maybe i'm little misunderstood you. I meant not exactly third case. But a case, when you start holding your mouse at empty space and selecting objects with outcoming rectangle.

Oki doki, I forget this case, so normally what will happen is:

  1. Select with SELECTION_SUB will be called for each entry.
  2. Select with SELECTION_NEW will be called for the uppermost entry
  3. Select with SELECTION_ADD will be called for each bellow entries.

So it should work as expected if your methods are safe.
Does unselect by ctrl+click on a selected entry work for you? (Looks like SELECTION_NEW, and SELECTION_ADD works so the only left is SELECTION_SUB).

Without your code, I can't help you more.


@m_adam Yes. I just checked that. A select few items with shift+click, and unselect with ctrl+click. Everything works fine.

I want to provide you my code, but there is a messy structure like this

typedef std::tuple<DescID, Int32, BaseContainer> MainMapCellArrayCell;
typedef std::list<MainMapCellArrayCell> MainMapCellArray;
typedef std::list<std::pair<MainMapCellArray, BaseContainer>> MainMap;
typedef std::pair<MainMapCellArray, BaseContainer> MainMapCell;

And i'm not sure it will be efficient way to help. Can i send you whole project to email?

Of course, having the whole project would probably more helpful, you can send it to [email protected], and if needed we can sign an NDA.


Hi after digging into your code, the main issue is that your root defined by SetRoot is also your first object.
So you should rethink how you structure your internal data structure.
I know this is misleading, but root, in the treeviewfunction expects an object that hosts the first object(like the dialog) but the root is not the first object.

Note the issue is a more general issue that could lead you to other problems, and in fact, you can reproduce your crash when you click on nothing, the drag operation is fine.
Since internally there are several places where the next logic is used

DoAction(void* pItem)
(pItem == root)
   pItem = pItem->GetFirst()
   pItem = pItem->GetDown()

for (; pItem; pItem = functions->GetNext(root, userdata, pItem))

So in your case, this creates an infinite loop and at the end, create a stack overflow.
pItem is the root and the first object. So if you call DoAction with pItem == root,
pItem == GetFirst == the previous pItem so in fact we didn't changed the object.
Then we arrive in the loop, and the first step is to call DoAction with pItem, so we get the infinite loop.


@m_adam Now i see the problem. Thanks alot for helping!