Make Menu Item Do Something AppleScript - menu

I'm writing a program in AppleScript that creates a menu in the menu bar on MacOS. This is my code:
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
property StatusItem : missing value
property selectedMenu : "" -- each menu action will set this to a number, this will determin which IP is shown
property theDisplay : ""
property defaults : class "NSUserDefaults"
property internalMenuItem : class "NSMenuItem"
property externalMenuItem : class "NSMenuItem"
property newMenu : class "NSMenu"
property theList : "Settings Battery Quit"
-- example list for the menu items that can be used. Ideally you will have your list created dynamically
(*MENU ITEMS
- Settings
- Battery Stats
----------
- Quit
*)
-- check we are running in foreground - YOU MUST RUN AS APPLICATION. to be thread safe and not crash
if not (current application's NSThread's isMainThread()) as boolean then
display alert "This script must be run from the main thread." buttons {"Cancel"} as critical
error number -128
end if
on menuNeedsUpdate:(menu)
(* NSMenu's delegates method, when the menu is clicked this is called.
We use it here to call the method makeMenus(). Which removes the old menuItems and builds new ones.
This means the menu items can be changed dynamically.
*)
my makeMenus()
end menuNeedsUpdate:
on makeMenus()
newMenu's removeAllItems() -- remove existing menu items
-----< (* this is just to show in this example a dynamic list for the menu items
set allMenuItems to {"Settings", "Battery Stats", "Quit"}
---- <
repeat with i from 1 to number of items in allMenuItems
set this_item to item i of allMenuItems
set thisMenuItem to (current application's NSMenuItem's alloc()'s initWithTitle:this_item action:"someAction:" keyEquivalent:"")
(newMenu's addItem:thisMenuItem)
(thisMenuItem's setTarget:me) -- required for enabling the menu item
if i is equal to 2 then
(newMenu's addItem:(current application's NSMenuItem's separatorItem)) -- add a seperator
end if
end repeat
end makeMenus
--menuItems action is requied for the menu to be enabled
on someAction:sender
MenuItem --do some thing
end someAction:
-- create an NSStatusBar
on makeStatusBar()
set bar to current application's NSStatusBar's systemStatusBar
set StatusItem to bar's statusItemWithLength:-1.0
-- set up the initial NSStatusBars title
StatusItem's setTitle:"IP"
-- set up the initial NSMenu of the statusbar
set newMenu to current application's NSMenu's alloc()'s initWithTitle:"Custom"
newMenu's setDelegate:me (*
Requied delegation for when the Status bar Menu is clicked the menu will use the delegates method (menuNeedsUpdate:(menu)) to run dynamically update.
*)
StatusItem's setMenu:newMenu
end makeStatusBar
my makeStatusBar()
The function
on someAction:sender
--MenuItem --do some thing
end someAction:
is what I need to change. I don't know how to run specific tasks based on which menu item is clicked. When I put code straight in the function:
on someAction:sender
--MenuItem --do some thing
display dialog "This runs on menu item click"
end someAction:
then the code runs whenever ANY menu item is clicked. I want it to run specific tasks for specific menu items. The menu items are:
Settings
Battery Stats
Quit
I tried this:
on someAction:sender
--MenuItem --do some thing
if sender is equal to "Settings"
--Code here!
end if
end someAction:
However, nothing happened when I clicked "Settings" in the menu.
Any help would be appreciated!

You can run specific tasks for specific menu items by checking the sender title:
on someAction:sender
set theTitle to title of sender as string
if theTitle is "Settings" then
display dialog "This runs when menu item \"Settings\" clicked"
else if theTitle is "Battery Stats" then
display dialog "This runs when menu item \"Battery Stats\" clicked"
else if theTitle is "Quit" then
quit
else
-- do nothing
end if
end actionHandler:

Related

How to click one of the options listed in the drop down menu in hand coded UI testing?

UITestControl uiLinkAboutus = new UITestControl(_bw);
uiLinkAboutus.TechnologyName = "Web";
uiLinkAboutus.SearchProperties.Add("ControlType", "Image");
uiLinkAboutus.SearchProperties.Add("TagName", "IMG");
uiLinkAboutus.SearchProperties.Add("Alt", "Open Menu");
Mouse.Click(uiLinkAboutus);
/* Upon execution, the drop down button is clicked but the option listed below is not clicked. How to solve this problem?
In stead of searching for an image, search for the list or dropdown control using a wpfCombobox, winCombobox or HtmlComboBox control. There you set the selectedItem property to the value you want. that is the most robust way of selecting an item in the UI. so e.g. :
ApplicationUnderTest aut = ApplicationUnderTest.Launch(#"yourapp.exe");  
WpfComboBox cbBox = new WpfComboBox(aut);
cbBox.SearchProperties.Add(WpfComboBox.PropertyNames.AutomationId,"cmbCountries");//AutomationId == xName roperty
cbBox.SelectedItem = "your value";

How to prevent change in a listbox selected item when users click and move mouse before mouseup

I am using the following code in a scrolling field. It works fine in both windows and android. The only problem is that the selected item changes as the mouse mouses up and down the list. I would like the original highlighted line to remain highlighted during the scroll and only change if the mouse have not moves move than 11 units(row height is 22) from initial vertical position. How do I modify this code to achieve that?
local lmousev,lvscroll,lscrolling
on mousedown
--If we don't use lscrolling the list will scroll when the mouse hovers over it
put true into lscrolling
--Set the initial vertical position of the cursor
put the mousev into lmousev
--Set the inital position of the vertical scroll
put the dgvscroll of me into lvscroll
end mousedown
on mousemove x,y
if lscrolling=true then --lscrolling is only true after mousedown so no scroll when mouse hovers over
--adjust the scroll position based on the vertical distance that the mouse has moved since mousedown
set the dgvscroll of me to lvscroll -(y - lmousev)
end if
end mousemove
on mouseUp
--stop scrolling when mouse hovers
put false into lscrolling
If abs( the mousev-lmousev)<11 then --If vertival position of mouse has not moved far from vertical position at mousedown
--selectlist command is in the group. It insert list selection into the textbox
selectlist
end if
end mouseUp
on mouserelease
--stop scrolling when mouse is release outside the listbox and there is no mouseup
put false into lscrolling
end mouserelease
The button,textbox and listbox that forms the combobox are grouped.The code to show/hide listbox and add selected to textbox is in the group script as follows:
Additional Info
local ldrop=false
on buttonclick --show/hide the listbox
--Get the name of the group,textbox & listbox.
--Naming convention required:- textboxname=txt & groupName,listboxname=lst & groupname
--Using quote as deliminater allows us to get the groupname from group "groupname"
set the itemdel to quote
put item 2 of the name of me into groupname
put "lst" & groupname into lstName
put "txt" & groupname into txtName
--status of ldrop let us know if the listbox is visible
if ldrop=false then
--show listbox
set the visible of field lstName to true
put true into ldrop
else
--hide listbox
set the visible of field lstName to false
put false into ldrop
end if
end buttonclick
on selectlist
--Get the name of the group,textbox & listbox.
--Naming convention required:- textboxname=txt & groupName,listboxname=lst & groupname
--Using quote as deliminater allows us to get the groupname from group "groupname"
set the itemdel to quote
put item 2 of the name of me into groupname
put "lst" & groupname into lstName
put "txt" & groupname into txtName
put the hilitedLine of field lstName into lhilitedLine
put line lhilitedLine of field lstName into field txtName
set the visible of field lstName to false
put false into ldrop
end selectlist
I think you have to make your own control that acts like a list so that you can control all the messages it sends and receives. You are otherwise stuck with some of those basic functions of LC's built-in controls. seems like you'd need an engine-level tweak to the control.

Does the Livecode combobox support multiple column

I have linked a livecode application to a relational database. I want to use a combobox to display values related to an ID. I other program such as msaccess this is done by having combobox with 2 columns. The first is linked to the ID and set to 0 width and the second displays the related value. Is that possible in livecode?
I have a text field and a list field and I managed to get it working using the following code on rawkeyup
global strHilitedLine,strTempHilitedLine,booAfterReturn,booFirstKeyUp
on keyup -- when your press a character key
TempHilited
end keyup
on returninfield --when user press return key on keyboard
--accept the temporary hilite
put the hilitedLine of field "lstfood" into strHilitedLine
put empty into strTempHilitedLine
--set booFirstKeyUp to true so that the keyup command will know that
-- the next keyup is the first after clicking the enter/return key
put "True" into booFirstKeyUp
--clear field txtfood
put empty into fld "txtFood"
end returninfield
on enterkey --when user press enter key on mobile(code same as on returninfield)
set the hilitedLine of field "lstfood" to strTempHilitedLine
put the hilitedLine of field "lstfood" into strHilitedLine
put empty into strTempHilitedLine
put "True" into booFirstKeyUp
put empty into fld "txtFood"
end enterkey
on TempHilited
if booFirstKeyUp="True" then
--store the value of hilitedline if this is the first keyup after
-- clicking enter(see on enterkey)
put the hilitedLine of field "lstfood" into strHilitedLine
put "False" into booFirstKeyUp
end if
--set hilitedlines to the hilitedlines just after clicking enter
set the hilitedLine of field "lstfood" to strHilitedLine
-- cleartemporary hilitedlines from previous keyup
put empty into strTempHilitedLine
-- create array from field lstFood and find if the text in txtFood
-- appears at the start of any item in the array
put field "lstfood" into arrFood
filter lines of arrFood with regex pattern "^" & me into strLineText
--create new value to temporarily hilite
if the length of strHilitedLine>0 and the length of strLineText>0 then
put strHilitedLine & "," & lineoffset (strLineText ,field
"lstfood") after strTempHilitedLine
else if the length of strHilitedLine>0 then
put strHilitedLine into strTempHilitedLine
else if the length of strLineText>0 then
put lineoffset (strLineText ,field "lstfood") into strTempHilitedLine
end if
--set temporay hilite
set the hilitedLine of field "lstfood" to strTempHilitedLine
put the length of me into mylength
--Select the part of txtFood that user did to type so that it is overwritten on the next keyup
put strlinetext into field "txtFood"
select char mylength +1 to the length of me of field "txtFood"
End TempHilited
As you can see it is a long convoluted code. Happy to hear if you have a more
-- efficient way of achieving the same.
You mentioned "Store the data in a custom property, filter to include relevant lines, put the remaining data into the field" and I believe you were alluding to another method of doing it but I have not really work out how to do that
No, menu buttons don't allow for multiple columns in LiveCode. However, it is possible to make your own combobox, e.g. by using a stack panel for a menu and adding two fields to that stack. Use the properties inspector of a menu button to assign a stack as a stack panel.
I made such a stack panel, with only one column, quite some time ago:
It would be easy to make this a two-column menu: just make the field half as wide, add another field and update the hilitedLine of the field that doesn't have focus when the hilitedLine of the focused field changes. This example isn't a real stack panel, but it works pretty much the same. I open the stack hidden as a palette, set the size and location, and show it making sure that it has focus.
(The picture is from my own website; the library is in an area available to donors only).

How to refresh item in a popup menu in Livecode

I have a pop-up menu and items are read from an array and I want to refresh when I press the right mouse button. Also, is there any way to disable/enable the items of the pop-up menu?
First create a pop-up menu and give it a name, e.g. "List A". Now create a control with a mouseDown handler or add a mouseDown handler to the card script.
on mouseDown theMouseButton
if theMouseButton is 3 then
put "One item,Another item,A third item,The last item" into myList
replace comma with cr in myList
put myList into btn "List A"
popup btn "List A"
end if
end mouseDown
Set the script of the pop-up menu button to the following:
on menuPick theItem
switch theItem
case "One item"
answer "Congrats"
break
default
beep
answer "Not implented yet"
break
end switch
end menuPick
You can disable individual items by preceding them with a parenthesis:
put "One item,Another item,(A third item,The last item" into myList
This will disable the third item in the menu.
There is no need to use arrays.

How do I safely add menuItems to an existing menu in Maya MEL

I tried adding menu items to an existing menu in Maya using MEL
Example:
menuItem -label "Mylabel" -insertAfter "someEntry" -parent $gMainFileMenu;
menuItem -label "Mylabel2" -insertAfter "someEntry" -parent $gMainFileMenu;
The issue is that the menu doesn't get populated by the normal entries like it should but only by the entries I've added with these 2 lines of code.
For example, the file menu normally contains "New scene", "Open scene", "Save scene", etc., but when I do these 2 lines, it only contains "Mylabel" and "Mylabel2".
Is there a fix or a workaround to make sure the menu gets fully populated? Is there a way to force Maya to build it without the user actually clicking on it?
Actually the problem is that Maya first populates the menu the first time the user opens it. It checks if the menu length and if it's greater than 0, it doesn't populate it. Since you've added 2 entries, the menu length is greater than 0 and doesn't get populated with the standard entries.
To fix that problem there is two ways to do it. You can do it by force-building the menu items or by registering your build menuItem. Bot ways are usable in different cases.
By force building the menu :
What you have to do, is find the function that Maya call to build the menu you need. You can find these functions in the folder <MayaInstall>/scripts/startup/*. A good way to find the procedure name is to open the Maya Console, enable "echo all commands" and then to click the menu you want to see the function called to build it.
In your case the function is called buildFileMenu() and can be found in the script FileMenu.mel.
Now that you have that function name, you have to check it's parameters. Sometime it need a menu name as parameters some times not. See the bottom section about how to find a menu name if you need one.
Now let's build it.
global string $gMainFileMenu; // Retrieve the menu name
buildFileMenu(); // Build it
// Now build your menu
menuItem -divider true -parent $gMainFileMenu SuperMenuDivider;
menuItem -label "MyLabel1" -parent $gMainFileMenu SuperMenuLab1;
menuItem -label "MyLabel2" -parent $gMainFileMenu SuperMenuLab2;
// Create a proc to remove your menu items
global proc RemoveMyMenuItems()
{
if(`menuItem -ex SuperMenuDivider`) deleteUI -mi SuperMenuDivider;
if(`menuItem -ex SuperMenuLab1`) deleteUI -mi SuperMenuLab1;
if(`menuItem -ex SuperMenuLab2`) deleteUI -mi SuperMenuLab2;
}
// You can then call that function when in the unload function of your plugin.
By registering the build menuItem call
What you have to do is use a function called addMenuItemSafe which takes 3 parameters, the menu you want to populate, the name of the function which populates the menu, and a global variable name to hold the callback.
So first thing you have to do is create the function that will populate your menu, then the function to remove it and then call the AddMenuItemSafe method. Note that in the function you have to check if your menu is already created because Maya will call that function every time the menu is shown.
First, the two functions to add and remove our menu entries:
global proc string AddMyMenuItems()
{
// Global variable to hold the test to see if the menu is populated.
global int $gMyMenuItemsTest;
// Menu var needed in our case because we are inserting in the middle of the menu
global string $gMainFileMenu;
if( $gMyMenuItemsTest == 0 )
{
// Actually build your menu.
// Note that if you don't need to insert it after a specific entry,
// You can just do `menuItem -label "blep"`. No need of -ia and -p
// Also, for inserting in the middle you have to put stuff in reverse order.
// If you are just appending, put it in the normal order.
menuItem -label "Mylabel2" -insertAfter "someEntry" -parent $gMainFileMenu MyMenuLab2;
menuItem -label "Mylabel" -insertAfter "someEntry" -parent $gMainFileMenu MyMenuLab;
menuItem -divider true -parent $gMainFileMenu MyMenuDiv;
$gMyMenuItemsTest = 1;
}
return "RemoveMyMenuItems()"; // Returns the callback
}
global proc RemoveMyMenuItems()
{
global int $gMyMenuItemsTest;
if( $gMyMenuItemsTest == 1 )
{
// Delete your items if they exist (yes we are kind of
// doing the check twice, but I find it safe.
// The user could have deleted it from Maya in the command
// line for whatever reason, more robustness is always good.
if(`menu -ex MyMenuDiv`) deleteUI -mi MyMenuDiv;
if(`menu -ex MyMenuLab`) deleteUI -mi MyMenuLab;
if(`menu -ex MyMenuLab2`) deleteUI -mi MyMenuLab2;
}
}
And then the actual call to the AddMenuItemSafe:
// The menu we want to use ... here it is the File Menu.
global string $gMainFileMenu;
// Our variables needed for the addSafe call
global int $gMyMenuItemsTest;
global string $gMyMenuVariable;
$gMyMenuItemsTest = 0;
$gMyMenuVariable = "";
// The menu creation
addMenuItemSafe($gMainFileMenu, "AddMyMenuItems", "gMyMenuVariable");
You are free to put that function call in a plugin instantiation or where ever it pleases you.
For more information about the function AddMenuItemSafe, you can go in your Maya Scripts Directory it should be there "AddMenuItemSafe.mel".
How to find a menu name
Note for the menu variable names, you can "build" them by using the following convention
"g" + View + Name + "Menu"
Where :
View is the view where you can find that menu. Ex: Main, Polygons, Animations, etc.
Name is the name of the menu. Ex: File, Edit, Mesh, etc.
Note Autodesk will sometimes rename menus and use old globalVariable names so using this method might not always work

Resources