BMenu: implement scripting
Fixes #15540. Change-Id: I965d04bcbb6db41cc7a744d18fa3c9ffab2a81f1 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2941 Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
parent
abbd654ba9
commit
8fbce87334
@ -192,6 +192,14 @@ private:
|
||||
bool keyDown = false);
|
||||
void _Hide();
|
||||
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,
|
||||
BRect& navAreaRectAbove,
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <Window.h>
|
||||
|
||||
#include <AppServerLink.h>
|
||||
#include <AutoDeleter.h>
|
||||
#include <binary_compatibility/Interface.h>
|
||||
#include <BMCPrivate.h>
|
||||
#include <MenuPrivate.h>
|
||||
@ -453,6 +454,9 @@ BMenu::Draw(BRect updateRect)
|
||||
void
|
||||
BMenu::MessageReceived(BMessage* message)
|
||||
{
|
||||
if (message->HasSpecifiers())
|
||||
return _ScriptReceived(message);
|
||||
|
||||
switch (message->what) {
|
||||
case B_MOUSE_WHEEL_CHANGED:
|
||||
{
|
||||
@ -1169,34 +1173,8 @@ BMenu::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier,
|
||||
BPropertyInfo propInfo(sPropList);
|
||||
BHandler* target = NULL;
|
||||
|
||||
switch (propInfo.FindMatch(msg, 0, specifier, form, property)) {
|
||||
case B_ERROR:
|
||||
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 (propInfo.FindMatch(msg, index, specifier, form, property) >= B_OK) {
|
||||
target = this;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user