BMenu: implement scripting
Fixes #15540. Change-Id: I965d04bcbb6db41cc7a744d18fa3c9ffab2a81f1 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2941 Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
@@ -192,6 +192,14 @@ private:
|
|||||||
bool keyDown = false);
|
bool keyDown = false);
|
||||||
void _Hide();
|
void _Hide();
|
||||||
BMenuItem* _Track(int* action, long start = -1);
|
BMenuItem* _Track(int* action, long start = -1);
|
||||||
|
void _ScriptReceived(BMessage* message);
|
||||||
|
void _ItemScriptReceived(BMessage* message,
|
||||||
|
BMenuItem* item);
|
||||||
|
status_t _ResolveItemSpecifier(const BMessage& specifier,
|
||||||
|
int32 what, BMenuItem*& item);
|
||||||
|
status_t _InsertItemAtSpecifier(
|
||||||
|
const BMessage& specifier, int32 what,
|
||||||
|
BMenuItem* item);
|
||||||
|
|
||||||
void _UpdateNavigationArea(BPoint position,
|
void _UpdateNavigationArea(BPoint position,
|
||||||
BRect& navAreaRectAbove,
|
BRect& navAreaRectAbove,
|
||||||
|
|||||||
+323
-28
@@ -41,6 +41,7 @@
|
|||||||
#include <Window.h>
|
#include <Window.h>
|
||||||
|
|
||||||
#include <AppServerLink.h>
|
#include <AppServerLink.h>
|
||||||
|
#include <AutoDeleter.h>
|
||||||
#include <binary_compatibility/Interface.h>
|
#include <binary_compatibility/Interface.h>
|
||||||
#include <BMCPrivate.h>
|
#include <BMCPrivate.h>
|
||||||
#include <MenuPrivate.h>
|
#include <MenuPrivate.h>
|
||||||
@@ -453,6 +454,9 @@ BMenu::Draw(BRect updateRect)
|
|||||||
void
|
void
|
||||||
BMenu::MessageReceived(BMessage* message)
|
BMenu::MessageReceived(BMessage* message)
|
||||||
{
|
{
|
||||||
|
if (message->HasSpecifiers())
|
||||||
|
return _ScriptReceived(message);
|
||||||
|
|
||||||
switch (message->what) {
|
switch (message->what) {
|
||||||
case B_MOUSE_WHEEL_CHANGED:
|
case B_MOUSE_WHEEL_CHANGED:
|
||||||
{
|
{
|
||||||
@@ -1169,34 +1173,8 @@ BMenu::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
|
|||||||
BPropertyInfo propInfo(sPropList);
|
BPropertyInfo propInfo(sPropList);
|
||||||
BHandler* target = NULL;
|
BHandler* target = NULL;
|
||||||
|
|
||||||
switch (propInfo.FindMatch(msg, 0, specifier, form, property)) {
|
if (propInfo.FindMatch(msg, index, specifier, form, property) >= B_OK) {
|
||||||
case B_ERROR:
|
target = this;
|
||||||
break;
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
case 7:
|
|
||||||
target = this;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
// TODO: redirect to menu
|
|
||||||
target = this;
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
case 10:
|
|
||||||
case 11:
|
|
||||||
case 12:
|
|
||||||
target = this;
|
|
||||||
break;
|
|
||||||
case 13:
|
|
||||||
// TODO: redirect to menuitem
|
|
||||||
target = this;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target)
|
if (!target)
|
||||||
@@ -1619,6 +1597,323 @@ BMenu::_Hide()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BMenu::_ScriptReceived(BMessage* message)
|
||||||
|
{
|
||||||
|
BMessage replyMsg(B_REPLY);
|
||||||
|
status_t err = B_BAD_SCRIPT_SYNTAX;
|
||||||
|
int32 index;
|
||||||
|
BMessage specifier;
|
||||||
|
int32 what;
|
||||||
|
const char* property;
|
||||||
|
|
||||||
|
if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
|
||||||
|
!= B_OK) {
|
||||||
|
return BView::MessageReceived(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
BPropertyInfo propertyInfo(sPropList);
|
||||||
|
switch (propertyInfo.FindMatch(message, index, &specifier, what,
|
||||||
|
property)) {
|
||||||
|
case 0: // Enabled: GET
|
||||||
|
if (message->what == B_GET_PROPERTY)
|
||||||
|
err = replyMsg.AddBool("result", IsEnabled());
|
||||||
|
break;
|
||||||
|
case 1: // Enabled: SET
|
||||||
|
if (message->what == B_SET_PROPERTY) {
|
||||||
|
bool isEnabled;
|
||||||
|
err = message->FindBool("data", &isEnabled);
|
||||||
|
if (err >= B_OK)
|
||||||
|
SetEnabled(isEnabled);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // Label: GET
|
||||||
|
case 3: // Label: SET
|
||||||
|
case 4: // Mark: GET
|
||||||
|
case 5: { // Mark: SET
|
||||||
|
BMenuItem *item = Superitem();
|
||||||
|
if (item != NULL)
|
||||||
|
return Supermenu()->_ItemScriptReceived(message, item);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 6: // Menu: CREATE
|
||||||
|
if (message->what == B_CREATE_PROPERTY) {
|
||||||
|
const char *label;
|
||||||
|
ObjectDeleter<BMessage> invokeMessage(new BMessage());
|
||||||
|
BMessenger target;
|
||||||
|
ObjectDeleter<BMenuItem> item;
|
||||||
|
err = message->FindString("data", &label);
|
||||||
|
if (err >= B_OK) {
|
||||||
|
invokeMessage.SetTo(new BMessage());
|
||||||
|
err = message->FindInt32("what",
|
||||||
|
(int32*)&invokeMessage->what);
|
||||||
|
if (err == B_NAME_NOT_FOUND) {
|
||||||
|
invokeMessage.Unset();
|
||||||
|
err = B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (err >= B_OK) {
|
||||||
|
item.SetTo(new BMenuItem(new BMenu(label),
|
||||||
|
invokeMessage.Detach()));
|
||||||
|
}
|
||||||
|
if (err >= B_OK) {
|
||||||
|
err = _InsertItemAtSpecifier(specifier, what, item.Get());
|
||||||
|
}
|
||||||
|
if (err >= B_OK)
|
||||||
|
item.Detach();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 7: { // Menu: DELETE
|
||||||
|
if (message->what == B_DELETE_PROPERTY) {
|
||||||
|
BMenuItem *item = NULL;
|
||||||
|
err = _ResolveItemSpecifier(specifier, what, item);
|
||||||
|
if (err >= B_OK) {
|
||||||
|
if (item->Submenu() == NULL)
|
||||||
|
err = B_BAD_VALUE;
|
||||||
|
else
|
||||||
|
RemoveItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 8: { // Menu: *
|
||||||
|
// TODO: check that submenu looper is running and handle it
|
||||||
|
// correctly
|
||||||
|
BMenu *submenu = NULL;
|
||||||
|
BMenuItem *item;
|
||||||
|
err = _ResolveItemSpecifier(specifier, what, item);
|
||||||
|
if (err >= B_OK)
|
||||||
|
submenu = item->Submenu();
|
||||||
|
if (submenu != NULL) {
|
||||||
|
message->PopSpecifier();
|
||||||
|
return submenu->_ScriptReceived(message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 9: // MenuItem: COUNT
|
||||||
|
if (message->what == B_COUNT_PROPERTIES)
|
||||||
|
err = replyMsg.AddInt32("result", CountItems());
|
||||||
|
break;
|
||||||
|
case 10: // MenuItem: CREATE
|
||||||
|
if (message->what == B_CREATE_PROPERTY) {
|
||||||
|
const char *label;
|
||||||
|
ObjectDeleter<BMessage> invokeMessage(new BMessage());
|
||||||
|
bool targetPresent = true;
|
||||||
|
BMessenger target;
|
||||||
|
ObjectDeleter<BMenuItem> item;
|
||||||
|
err = message->FindString("data", &label);
|
||||||
|
if (err >= B_OK) {
|
||||||
|
err = message->FindMessage("be:invoke_message",
|
||||||
|
invokeMessage.Get());
|
||||||
|
if (err == B_NAME_NOT_FOUND) {
|
||||||
|
err = message->FindInt32("what",
|
||||||
|
(int32*)&invokeMessage->what);
|
||||||
|
if (err == B_NAME_NOT_FOUND) {
|
||||||
|
invokeMessage.Unset();
|
||||||
|
err = B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (err >= B_OK) {
|
||||||
|
err = message->FindMessenger("be:target", &target);
|
||||||
|
if (err == B_NAME_NOT_FOUND) {
|
||||||
|
targetPresent = false;
|
||||||
|
err = B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (err >= B_OK) {
|
||||||
|
item.SetTo(new BMenuItem(label, invokeMessage.Detach()));
|
||||||
|
if (targetPresent)
|
||||||
|
err = item->SetTarget(target);
|
||||||
|
}
|
||||||
|
if (err >= B_OK) {
|
||||||
|
err = _InsertItemAtSpecifier(specifier, what, item.Get());
|
||||||
|
}
|
||||||
|
if (err >= B_OK)
|
||||||
|
item.Detach();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 11: // MenuItem: DELETE
|
||||||
|
if (message->what == B_DELETE_PROPERTY) {
|
||||||
|
BMenuItem *item = NULL;
|
||||||
|
err = _ResolveItemSpecifier(specifier, what, item);
|
||||||
|
if (err >= B_OK)
|
||||||
|
RemoveItem(item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 12: { // MenuItem: EXECUTE
|
||||||
|
if (message->what == B_EXECUTE_PROPERTY) {
|
||||||
|
BMenuItem *item = NULL;
|
||||||
|
err = _ResolveItemSpecifier(specifier, what, item);
|
||||||
|
if (err >= B_OK) {
|
||||||
|
if (!item->IsEnabled())
|
||||||
|
err = B_NOT_ALLOWED;
|
||||||
|
else
|
||||||
|
err = item->Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 13: { // MenuItem: *
|
||||||
|
BMenuItem *item = NULL;
|
||||||
|
err = _ResolveItemSpecifier(specifier, what, item);
|
||||||
|
if (err >= B_OK) {
|
||||||
|
message->PopSpecifier();
|
||||||
|
return _ItemScriptReceived(message, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return BView::MessageReceived(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != B_OK) {
|
||||||
|
replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
|
||||||
|
|
||||||
|
if (err == B_BAD_SCRIPT_SYNTAX)
|
||||||
|
replyMsg.AddString("message", "Didn't understand the specifier(s)");
|
||||||
|
else
|
||||||
|
replyMsg.AddString("message", strerror(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
replyMsg.AddInt32("error", err);
|
||||||
|
message->SendReply(&replyMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BMenu::_ItemScriptReceived(BMessage* message, BMenuItem* item)
|
||||||
|
{
|
||||||
|
BMessage replyMsg(B_REPLY);
|
||||||
|
status_t err = B_BAD_SCRIPT_SYNTAX;
|
||||||
|
int32 index;
|
||||||
|
BMessage specifier;
|
||||||
|
int32 what;
|
||||||
|
const char* property;
|
||||||
|
|
||||||
|
if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
|
||||||
|
!= B_OK) {
|
||||||
|
return BView::MessageReceived(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
BPropertyInfo propertyInfo(sPropList);
|
||||||
|
switch (propertyInfo.FindMatch(message, index, &specifier, what,
|
||||||
|
property)) {
|
||||||
|
case 0: // Enabled: GET
|
||||||
|
if (message->what == B_GET_PROPERTY)
|
||||||
|
err = replyMsg.AddBool("result", item->IsEnabled());
|
||||||
|
break;
|
||||||
|
case 1: // Enabled: SET
|
||||||
|
if (message->what == B_SET_PROPERTY) {
|
||||||
|
bool isEnabled;
|
||||||
|
err = message->FindBool("data", &isEnabled);
|
||||||
|
if (err >= B_OK)
|
||||||
|
item->SetEnabled(isEnabled);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // Label: GET
|
||||||
|
if (message->what == B_GET_PROPERTY)
|
||||||
|
err = replyMsg.AddString("result", item->Label());
|
||||||
|
break;
|
||||||
|
case 3: // Label: SET
|
||||||
|
if (message->what == B_SET_PROPERTY) {
|
||||||
|
const char *label;
|
||||||
|
err = message->FindString("data", &label);
|
||||||
|
if (err >= B_OK)
|
||||||
|
item->SetLabel(label);
|
||||||
|
}
|
||||||
|
case 4: // Mark: GET
|
||||||
|
if (message->what == B_GET_PROPERTY)
|
||||||
|
err = replyMsg.AddBool("result", item->IsMarked());
|
||||||
|
break;
|
||||||
|
case 5: // Mark: SET
|
||||||
|
if (message->what == B_SET_PROPERTY) {
|
||||||
|
bool isMarked;
|
||||||
|
err = message->FindBool("data", &isMarked);
|
||||||
|
if (err >= B_OK)
|
||||||
|
item->SetMarked(isMarked);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6: // Menu: CREATE
|
||||||
|
case 7: // Menu: DELETE
|
||||||
|
case 8: // Menu: *
|
||||||
|
case 9: // MenuItem: COUNT
|
||||||
|
case 10: // MenuItem: CREATE
|
||||||
|
case 11: // MenuItem: DELETE
|
||||||
|
case 12: // MenuItem: EXECUTE
|
||||||
|
case 13: // MenuItem: *
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return BView::MessageReceived(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != B_OK) {
|
||||||
|
replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
|
||||||
|
replyMsg.AddString("message", strerror(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
replyMsg.AddInt32("error", err);
|
||||||
|
message->SendReply(&replyMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t BMenu::_ResolveItemSpecifier(const BMessage& specifier, int32 what,
|
||||||
|
BMenuItem*& item)
|
||||||
|
{
|
||||||
|
status_t err;
|
||||||
|
item = NULL;
|
||||||
|
switch (what) {
|
||||||
|
case B_INDEX_SPECIFIER:
|
||||||
|
case B_REVERSE_INDEX_SPECIFIER: {
|
||||||
|
int32 index;
|
||||||
|
err = specifier.FindInt32("index", &index);
|
||||||
|
if (err < B_OK)
|
||||||
|
return err;
|
||||||
|
if (what == B_REVERSE_INDEX_SPECIFIER)
|
||||||
|
index = CountItems() - index;
|
||||||
|
item = ItemAt(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case B_NAME_SPECIFIER: {
|
||||||
|
const char* name;
|
||||||
|
err = specifier.FindString("name", &name);
|
||||||
|
if (err < B_OK)
|
||||||
|
return err;
|
||||||
|
item = FindItem(name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item == NULL)
|
||||||
|
return B_BAD_INDEX;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t BMenu::_InsertItemAtSpecifier(const BMessage& specifier, int32 what,
|
||||||
|
BMenuItem* item)
|
||||||
|
{
|
||||||
|
status_t err;
|
||||||
|
switch (what) {
|
||||||
|
case B_INDEX_SPECIFIER:
|
||||||
|
case B_REVERSE_INDEX_SPECIFIER: {
|
||||||
|
int32 index;
|
||||||
|
err = specifier.FindInt32("index", &index);
|
||||||
|
if (err < B_OK) return err;
|
||||||
|
if (what == B_REVERSE_INDEX_SPECIFIER)
|
||||||
|
index = CountItems() - index;
|
||||||
|
if (!AddItem(item, index))
|
||||||
|
return B_BAD_INDEX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case B_NAME_SPECIFIER:
|
||||||
|
return B_NOT_SUPPORTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - mouse tracking
|
// #pragma mark - mouse tracking
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user