How do I add a menu item to a right-click menu?

Adding a menu item to a right-click menu is fairly simple to do in either the core C++ or a JavaScript extension. This article explores both methods for doing so.

JavaScript

xTuple's extensions do this fairly often. Here are some excerpts from the Manufacturing extension. This code adds the option to open the Bill of Operations (BOO, sometimes called a "Routing") window from the Work Order Schedule window when the user right-clicks on a particular work order.

First, write a function to open the window:

function sViewBOO()
{
var params = new Object; // create an object to hold the data we need
params.wo_id = _list.id(); // collect the internal id of the work order
// ask the database for the item being made by this work order id
var qry = toolbox.executeQuery("SELECT itemsite_item_id "
+" FROM wo, itemsite "
+" WHERE((wo_itemsite_id=itemsite_id)"
+" AND (wo_id=<? value('wo_id') ?>));", params);
if(qry.first())
{
params.item_id = qry.value("itemsite_item_id"); // save the item's internal id
params.mode = "view"; // say we want to view, not edit
var wnd = toolbox.openWindow("boo", 0, Qt.NonModal, Qt.Window); // open the window
toolbox.lastWindow().set(params); // tell the window which item and mode
}
else if (qry.lastError().type != QSqlError.NoError) // check for and report errors
{
QMessageBox.critical(mywindow, qsTr("Database Error"), qry.lastError().text);
}
}

Next, write a second JavaScript function that adds the View BOO item to the menu already created by the application:

function populateMenu(pMenu, pItem, pCol)
{
// ... - irrelevant code removed
if(pMenu != null) // safety check
{
if (metrics.boolean("Routings")) // if BOOs/Routings are enabled
{
pMenu.addSeparator();

var tmpact = pMenu.addAction(qsTr("View Routing...")); // add the menu item
tmpact.enabled = (privileges.check("MaintainBOOs") // restrict access
|| privileges.check("ViewBOOs"));
tmpact.triggered.connect(sViewBOO); // call the first function if the user picks this
// ...
}
}
// ...
}

Then connect the user right-click on the list to the function that builds/extends the menu:

var _list = mywindow.findChild("_list");
_list["populateMenu(QMenu *, XTreeWidgetItem *, int)"].connect(populateMenu)

Note:

  • Beware of function and variable name collisions between your script and other scripts on the same window.
  • Be aware that windows defined at the C++ level as dialogs need to be handled differently than windows defined as "main windows" or "widgets" (long story)

C++

The JavaScript above is very similar to the C++ code that builds the menu in the first place. You can read the full source file if you like.

There's a C++ method that gathers the data and opens the window:

void dspWoSchedule::sViewParentWO()
{
ParameterList params; // gathers the data
params.append("mode", "view");
params.append("wo_id", list()->altId());
workOrder *newdlg = new workOrder(); // creates the window
newdlg->set(params); // passes in the data
omfgThis->handleNewWindow(newdlg); // opens the window
}

A second C++ method takes the menu to extend, the current line, and the column clicked on, and uses that information to create a menu item.

void dspWoSchedule::sPopulateMenu(QMenu *pMenu, QTreeWidgetItem *pSelected, int)
{
QAction *menuItem;
XTreeWidgetItem * item = (XTreeWidgetItem*)pSelected; // get the current line
// ...
QString ordtype = item->rawValue("wo_ordtype").toString(); // and order type from it
// ...
if (list()->altId() != -1) // if it's an order
{
if (ordtype == "S") // specifically a sales order
{
pMenu->addSeparator();
menuItem = pMenu->addAction(tr("View Parent Sales Order Information..."), this, SLOT(sViewParentSO())); // add one menu item
}
else if (ordtype == "W") // but for work orders
{
pMenu->addSeparator();
menuItem = pMenu->addAction(tr("View Parent Work Order..."), this, SLOT(sViewParentWO())); // add a different menu item
}
}
}

Finally, connect the user right-click to the menu-building function, in this case handled in the parent class.

The article Hacking the xTuple Desktop Client Core is a good place to start reading to learn more.