diff --git a/headers/os/interface/Menu.h b/headers/os/interface/Menu.h index 3fede3d4fa..1c39a91b4d 100644 --- a/headers/os/interface/Menu.h +++ b/headers/os/interface/Menu.h @@ -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, diff --git a/src/kits/interface/Menu.cpp b/src/kits/interface/Menu.cpp index eafa643785..f280436e70 100644 --- a/src/kits/interface/Menu.cpp +++ b/src/kits/interface/Menu.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -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 invokeMessage(new BMessage()); + BMessenger target; + ObjectDeleter 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 invokeMessage(new BMessage()); + bool targetPresent = true; + BMessenger target; + ObjectDeleter 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