dynamic File List in submenu menu

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
amon
In need of some credit
In need of some credit
Posts: 2
Joined: Thu Feb 10, 2005 9:45 pm
Location: UK
Contact:

dynamic File List in submenu menu

Post by amon » Thu Feb 10, 2005 10:07 pm

my aim is to construct a list of files (similar to a "recently accessed files" list) in a submenu of a popup menu. This list is creaited at runtime (and can be added to) from a vector.

i have creaited the submenu and the popup menu and i am able to write to files. my difficulty is in selecting which file to write to as i don't think you can send an argument to an event.

here is the relevent code snipits:

Code: Select all

int i;
wxMenu *playlistmenu= new wxMenu;

for(i=0;i<(PlaylistVector.size());i++)
{
        playlistmenu->Append(ID_AddToPlayList, PlaylistVector[i]->GetFileName());
}
    
wxMenu popmenu(_T("Options"));

popmenu.Append(ID_EditID3v1Tag,    "Edit Tag");
popmenu.Append(ID_launchMP3Player, "Play selected files");
popmenu.Append(Menu_Popup_Submenu, "Add to Playlist", playlistmenu, "Select Playlist to add selected files to");

PopupMenu(&popmenu, event.GetPoint());
as you can see there is a variable sized vector storing the list of files and i am able to creait the list and i can get it to perform an action. my dificulty is in finding which file has been selected.

I hope this makes sence to people as i'm finding it hard to explain for some reason (may have something to do with the 5 hours ive been trying to get it to work)

My dev system: Linux (Debian)
wxwidgets version: 2.4.2

Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg » Fri Feb 11, 2005 8:18 am

Hi,

The trick is changing the menu ID for every new item you create. For example your snippet:

Code: Select all

for(i=0;i<(PlaylistVector.size());i++)
{
        playlistmenu->Append(ID_AddToPlayList + i, PlaylistVector[i]->GetFileName());
} 
Mind the "+ i" behind ID_AddToPlayList. Whenever you reserve room for let's say 5 or 10 most recent files, you can simply implement an EVT_MENU for a range of menu items or dynamically attach a handler to the menu items you add (See wxCommandEvent, EVT_MENU_RANGE(id1, id2, func))

- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb

amon
In need of some credit
In need of some credit
Posts: 2
Joined: Thu Feb 10, 2005 9:45 pm
Location: UK
Contact:

Post by amon » Fri Feb 11, 2005 9:22 am

Thanks, That will help my project,

however i was hoping there may be a neater way of doing it in-case someone decided that they wanted to work with over 10 files

But this solution will most likely be ample for my project and i suppose it is only a very insignificant addition in memory to add more ID numbers.

Thanks again

Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg » Fri Feb 11, 2005 10:09 am

Well you have to understand that let's say a list of 100 files in your menu is not really a smart way to list files. It will be too big and memory consuming. Instead I would suggest doing:

- List 10 files (or 20 if people want that)
- One seperator
- Make a menu entry called "List all recent files ..."

The last menu entry shows the complete history of files in a dialog, with the option to delete the history, some files, or let it "clean up" where the no longer present files are removed.

If you really value a flexible list with flexible extension, look into SetClientData and GetClientData for teh wxCommandEvent. Maybe there is a way to pass the menu pointer that is selected to the event handler with a single ID, and determine the title of the wxMenu entry that way.

I really miss a Sender object with a wxEvent. Whenever an event is sent, I would like to see a Sender value that can be cast to let's say a wxMenu, wxButton or what control that is responsible for the event.

- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb

blockn102
Experienced Solver
Experienced Solver
Posts: 70
Joined: Sat Oct 25, 2008 2:38 am

Post by blockn102 » Fri Aug 21, 2009 7:26 am

Code: Select all

EVT_MENU_RANGE(id1, id2, func)
Can I add PlaylistVector.size() to id2??? I think I can't do it. And, how to use Connect() event?
??? year olds :)

blockn102
Experienced Solver
Experienced Solver
Posts: 70
Joined: Sat Oct 25, 2008 2:38 am

Post by blockn102 » Fri Aug 21, 2009 7:27 am

Code: Select all

EVT_MENU_RANGE(id1, id2, func)
Can I add PlaylistVector.size() to id2??? I think I can't do it. And, how to use Connect() event?
??? year olds :)

DavidHart
Site Admin
Site Admin
Posts: 3943
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart » Fri Aug 21, 2009 9:50 am

Hi,
I think I can't do it.
I'm sure you can't.

One easy choice is to use an enum to define a range of IDs:

Code: Select all

enum my_foo_ids { MAX_RANGE=100, id1=10000, id2=id1+MAX_RANGE };
...
EVT_MENU_RANGE(id1, id2, func)
Alternatively, you can use Connect() (see http://docs.wxwidgets.org/stable/wx_wxe ... lerconnect and various examples in this forum) to connect each item as it's created.

In both cases, you'd use event.GetId() in the handler to distinguish between items.

Regards,

David

blockn102
Experienced Solver
Experienced Solver
Posts: 70
Joined: Sat Oct 25, 2008 2:38 am

Post by blockn102 » Fri Aug 21, 2009 10:32 am

Ok, I think so.

My solution:

Code: Select all

wxMenu menuPopup;
int data_row_count = 10;
// 10 menus in dynamic
for(i = 0; i < data_row_count; i++) {
	wxMenuItem* d_m = new wxMenuItem(&menuPopup, 
		ID_ParentMenu + i, 
		wxString::Format(wxT("Dynamic menu: %d"), i)
	);
	//set icon
	//d_m->SetBitmap(bitmap);
	this->Connect( ID_ParentMenu + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MyFrame::OnDynamicMenuSelected ) );
	menuPopup.Append(d_m);
}
It works good.

PS: How to get widget object from event.getID()? I want get menu item label from id. Thanks.

Code: Select all

void MyFrame::OnDynamicMenuSelected(wxCommandEvent& event)
{
    int ID = event.GetID();

    // how to get label of menu item?
}
??? year olds :)

DavidHart
Site Admin
Site Admin
Posts: 3943
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart » Fri Aug 21, 2009 10:47 am

PS: How to get widget object from event.getID()? I want get menu item label from id.
No, you get the event object from the event. See http://docs.wxwidgets.org/stable/wx_wxe ... ventobject

blockn102
Experienced Solver
Experienced Solver
Posts: 70
Joined: Sat Oct 25, 2008 2:38 am

Post by blockn102 » Fri Aug 21, 2009 1:26 pm

Thanks you. I try

Code: Select all

wxMenuItem* menu_item = static_cast<wxMenuItem*>(evt.GetEventObject());
wxString msg = menu_item ->GetLabel();
wxMessageBox(msg, _("Welcome to..."));
Error on menu item click (line 412)

Code: Select all

size_type length() const { return GetStringData()->nDataLength; }

Code: Select all

Unhandled exception at 0x0048a5fb in MyFrame.exe: 0xC0000005: Access violation reading location 0xfffffff8.
??? year olds :)

DavidHart
Site Admin
Site Admin
Posts: 3943
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart » Fri Aug 21, 2009 3:31 pm

Ah, I see what you're trying to do. No, afaik you can't use the event object for that.

I don't use Connect() for this (or for much else), so I'm not speaking from experience. However I think you can do what you want in 2 ways:
  • Change how you set the event ID, so that it's more useful. e.g. Set it to 10000+i. Then in the handler, int index = event.GetId() - 10000;
    You can now get your original stored data using index, or you can get the menuitem from it by using wxMenu::FindItemByPosition.

    Alternatively you can probably pass the information via the userData parameter of Connect(). I've never done this, or seen it done; but you should be able to pass the wxMenuItem there. In the handler you can apparently then access this by:
    wxMenuItem* menu_item = static_cast<wxMenuItem*>(evt.m_callbackUserData);

blockn102
Experienced Solver
Experienced Solver
Posts: 70
Joined: Sat Oct 25, 2008 2:38 am

Post by blockn102 » Sat Aug 22, 2009 1:38 am

Connect() is not good?

I try m_callbackUserData, it works good. Thanks you.
??? year olds :)

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria » Sat Aug 22, 2009 1:44 am

Connect() is much more flexible than event tables, so is superior in this aspect (namely, it allows you to catch events from every widget without having to derive a new class from it). It is however easier to shoot yourself in the foot.

wxWidgets 3.0 will include a new method called Bind that will provide compile-time checking of types, something Connect did not achieve. So Bind will be the best of all possible methods.
"Keyboard not detected. Press F1 to continue"
-- Windows

blockn102
Experienced Solver
Experienced Solver
Posts: 70
Joined: Sat Oct 25, 2008 2:38 am

Post by blockn102 » Tue Aug 25, 2009 1:45 pm

I see wxAui doesn't support Connect().
??? year olds :)

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria » Tue Aug 25, 2009 5:29 pm

blockn102 wrote:I see wxAui doesn't support Connect().
No clue why you say that. AFAIK all events in wxWidgets can be connected.
"Keyboard not detected. Press F1 to continue"
-- Windows

Post Reply