Two TMemo objects sharing one TPopupMenu - c++builder

I have a TForm with two TMemo objects and one TPopupMenu. Both TMemos are using the same TPopupMenu.
The popup menu has one item for 'clearing' the Memo, through the use of a TAction.
My question is, when clicking on the Menu item on one of the TMemos, how can I figure out which TMemo was the one that displayed the TPopupMenu?
I have the following code in the TAction's execute function:
TAction* action = dynamic_cast<TAction*>(Sender);
TMenuItem* item = dynamic_cast<TMenuItem*>(action->ActionComponent);
if(item)
{
if(dynamic_cast<TMemo*>(item->Owner))
{
dynamic_cast<TMemo*>(item->Owner) -> Clear();
}
}
But the TMenuItem's owner is not a TMemo.
Any hints? I hope to avoid to have to use two different TPopupMenus.

You need to use the TPopupMenu::PopupComponent property to know which TMemo invoked the menu:
TMemo *memo = dynamic_cast<TMemo*>(PopupMenu1->PopupComponent);
if (memo)
memo->Clear();
Or, if you want to discover the TPopupMenu that the TMenuItem is linked to:
TAction* action = static_cast<TAction*>(Sender);
TMenuItem* item = dynamic_cast<TMenuItem*>(action->ActionComponent);
if (item)
{
TPopupMenu *popup = dynamic_cast<TPopupMenu*>(item->GetParentMenu());
if (popup)
{
TMemo *memo = dynamic_cast<TMemo*>(popup->PopupComponent);
if (memo)
memo->Clear();
}
}

Related

Using DockPanelSuite, how do you get context menu for tab strip separate from document tab?

When using DockPanelSuite, is it possible to have a context menu for the tab strip that is different from the one for a document tab? For example, right click an empty space on the tab strip and get one context menu then right click a document tab and get a different context menu specific to the document.
I tried setting the ContextMenuStrip property of the DockPanel. I got a context menu for any empty space on the DockPanel control as well as the document tab strip when visible and all open document tabs. That's a good start but I really only wanted the context menu for the tab strip. Not the main control or any tabs.
I also followed along with the sample project to make a context menu for the document by setting the TabPageContextMenuStrip property of the DockContent form. I discovered that you get a document specific context menu by right clicking the document tab, but it also overrides the DockPanel's ContextMenuStrip. While that is useful, it's still not the desired result.
Edit:
Updating this post in case anyone else is interested in achieving the objective of the question.
After much source code analysis and testing, I concluded that the objective could not be achieved using the available public Properties, Methods, and Events. However, we can achieve the goal by using a bit of reflection.
Discoveries:
DockContent.ContextMenuStrip
This property does nothing for the DockPanel. It will provide a context menu in the client area of the document. However, for some reason, the RichTextBox control set to Fill in the provided sample blocks the context menu from popping up.
DockContent.TabPageContextMenuStrip
This property causes the associated ContextMenuStrip to display when the document is active. However, it displays when you right click anywhere on the tab strip, not just when you right click the document tab.
Solution:
First, add a public property to the DockContent form which will contain a reference to the context menu.
public ContextMenuStrip TabContextMenu { get { return contextMenuTabPage; } }
Next, add an event handler in the MDI main form for the DockPanel.ActiveDocmentChanged event. This will be used to add an event handler to the tab strip after it’s been created.
this.dockPanel.ActiveDocumentChanged += new System.EventHandler(this.dockPanel_ActiveDocumentChanged);
private void dockPanel_ActiveDocumentChanged(object sender, EventArgs e)
{
// Hook into the document pane tabstrip mouse up event
// if we haven't already.
if (dockPanel.ActiveDocumentPane != null
&& dockPanel.ActiveDocumentPane.TabStripControl != null
&& dockPanel.ActiveDocumentPane.TabStripControl.Tag == null)
{
dockPanel.ActiveDocumentPane.TabStripControl.Tag = "MouseUp Hooked";
dockPanel.ActiveDocumentPane.TabStripControl.MouseUp +=
TabStripControl_MouseUp;
}
}
Finally, add the event handler for the TabStripControl.
private void TabStripControl_MouseUp(object sender, MouseEventArgs e)
{
// Capture right click action
if (e.Button == MouseButtons.Right)
{
ContextMenuStrip menu = contextMenuDocumentPane;
Point screenPos = Cursor.Position;
Point tabstripsPos = dockPanel.ActiveDocumentPane
.TabStripControl.PointToClient(screenPos);
// Determine if cursor is over a tab
var tabstrip = dockPanel.ActiveDocumentPane.TabStripControl;
var tabs = tabstrip.GetType()
.GetProperty("Tabs", BindingFlags.Instance |
BindingFlags.NonPublic).GetValue(tabstrip);
foreach (var tab in (IEnumerable)tabs)
{
var bounds = tab.GetType()
.GetProperty("Rectangle")
.GetValue(tab);
if (((Rectangle)bounds).Contains(tabstripsPos))
{
// Display context menu for this document tab
var document = tab.GetType()
.GetProperty("Content")
.GetValue(tab);
menu = ((ContentWindow)document).TabContextMenu;
}
}
// Show appropriate context menu
menu.Show(screenPos);
}
}

How to add click listener or context menu to Vaadin's Grid column Header

This question is similar to this. But I'm interested in Vaadin 14 as I am migrating old Table components to Grids. There no longer a HeaderClickListener. Therefore, how can you implement click listener on Grid header.
I don't mind adding a context menu with a column selection for copying, but as I understand it, there is no API to get data in a column?
In Vaadin 14 there is no HeaderClickListener. Instead you need to do something like this:
Column<MyBean> column = grid.addColumn(..)
HeaderRow headerRow = this.getHeaderRows().get(0); // Get first header row
Div component = new Div(); // Just example, anything that has click listener can be used
component.setText("Header");
headerRow.getCell(column).setComponent(component);
component.addClickListener(..);
Alternatively you can use GridContextMenu
grid.addColumn(..).setId("column");
GridContextMenu<MyBean> menu = grid.addContextMenu();
item.addMenuItemClickListener(event -> {
Notification.show(selectedColumn);
});
menu.addGridContextMenuOpenedListener(event -> {
// item is not present when clicking header
if (!event.getItem().isPresent()) {
event.getColumnId().ifPresent(id -> {
selectedColumn = id;
});
} else {
menu.close(); // Do not let menu open on body
}
});

Vaadin grid - change component column in one row only

I have a grid with several columns. For three columns i used the column renderer. Each of the columns contains one button.
If i click one of those buttons, i want to replace the three buttons in that specific row with two other buttons. All the other rows should not be affected. Is this possible in a Vaadin grid?
Components in different columns don't know each other, as they are all defined in a separate scope (in the componentRenderer of their own column. You cannot define the Button outside of the componentRenderer as you found out in another question today). So the "obvious" solution won't work, where you add a clickListener on the Button to directly change the other buttons.
If you had one column with 3 Buttons inside then this would be much easier.
There is a way, but I see this more as a hack than as a solution. Because you need some extra implementation in the item class for this to work.
In the ComponentRenderer, you can add an if-statement where you look at some value of the item. In one case, you'll render button 1, in the other case you'll render the other button. And in the click listeners of the button you change that value in the item and refresh the dataprovider, so the componentRenderer is invoked again. It will now see the value on the item has changed, therefore displaying some other Button.
Here is some code to show what I mean:
// grid item class
public class Foo {
private boolean buttonPressed = false;
public Foo(){
}
public isButtonPressed(){
return buttonPressed;
}
public setButtonPressed(boolean buttonPressed){
this.buttonPressed = buttonPressed;
}
}
// adding of button columns
// do this 3 times for a test of your scenario.
grid.addComponentColumn(item -> {
if(!item.isButtonPressed()){
return new Button("Before Button was Pressed", click -> {
item.setButtonPressed(true);
grid.getDataProvider().refresh(item);
});
} else {
return new Button("Button was Pressed", click -> {
item.setButtonPressed(false);
grid.getDataProvider().refresh(item);
})
}
})

Two TListBoxes using the same TPopup menu?

I have one TListBox with 'movie' items and another one with 'snapshots'. I want to use one popup menu for both Listboxes. However, in the onClick event for a popups menuitem, how do I resolve which list box was used?
I tried this:
void __fastcall TMainForm::DeleteAll1Click(TObject *Sender)
{
TListBox* lb = dynamic_cast<TListBox*>(Sender);
if(lb == mMoviesLB)
{
...
where DeleteAll1 is a TMenuItem in the Popup menu. The lb is always NULL so there is something missing here..
The TPopupMenu::PopupComponent property tells you which UI control displayed the popup menu, eg:
void __fastcall TMainForm::DeleteAll1Click(TObject *Sender)
{
TListBox* lb = dynamic_cast<TListBox*>(PopupMenu1->PopupComponent);
...
}
If the TPopupMenu is displayed automatically (ie: right-clicking on a control when TPopupMenu::AutoPopup is true), the PopupComponent is populated automatically. However, if you call TPopupMenu::Popup() yourself, the PopupComponent will be NULL unless you assign it beforehand, eg:
PopupMenu1->PopupComponent = ListBox1;
PopupMenu1->Popup(X, Y);

How do I access an mxml component through an actionscript variable in adobe flex?

Lets say I have a button
<s:Button id = "button1" label="Click" click = "buttonHandler()"/>
and I have another button that is supposed to remove this button by calling the following function
protected function remove_Button(event:MouseEvent):void
{
var button1:Button = ?????
this.removeChild(button1);
}
how do I first declare the variable to be the same button in the mxml file?
If the button and corresponding code for the second button are in the same MXML document, you can simply refer to the first button by the ID that you have assigned it, button1 in this case.
Example.mxml:
<s:Button id = "button1" label="Click" click = "buttonHandler()"/>
<s:Button label="Remove 1st Button" click = "remove_Button(event)"/>
// this code appears inside a script block in Example.mxml
protected function remove_Button(event:MouseEvent):void
{
// no need to declare button1 as a variable, that has already been
// done in the 1st <Button> object above
this.removeChild(button1);
}
It should be noted that any object you declare with an MXML tag is a public variable of that MXML document. So if the second button is in a different MXML document/class then you can still access it through the ID that you have assigned (ie: button1).
In remove_Button function write this.removeElement(button1); if your skin not supported this.removeChild function.

Resources