2

Working with an AppleScript file from https://github.com/bat-tomr/dialog-node ...

# 06/04/16 09:35:02
# Author: Shane Stanley
# Adapted by Christopher Stone

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

my datePicker()

on datePicker()
    set theApp to path to frontmost application as text

    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
    -- create a view
    set theView to current application's NSView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, 100, 175))
    -- create date picker
    set datePicker to current application's NSDatePicker's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, 100, 100))
    -- set style: choices are NSTextFieldAndStepperDatePickerStyle, NSClockAndCalendarDatePickerStyle, or NSTextFieldDatePickerStyle
    #datePicker's setDatePickerStyle:(current application's NSClockAndCalendarDatePickerStyle)
    datePicker's setDatePickerStyle:(current application's NSTextFieldAndStepperDatePickerStyle)
    #datePicker's setDatePickerStyle:(current application's NSTextFieldDatePickerStyle)
    -- set elements: choices include NSHourMinuteDatePickerElementFlag, NSHourMinuteSecondDatePickerElementFlag, NSTimeZoneDatePickerElementFlag, NSYearMonthDatePickerElementFlag, and NSEraDatePickerElementFlag
    datePicker's setDatePickerElements:((current application's NSYearMonthDayDatePickerElementFlag) + (current application's NSHourMinuteSecondDatePickerElementFlag as integer))

    -- set initial date
    #datePicker's setDateValue:(current application's NSDate's |date|())
    --set theCal var to a new(empty) instance of the calendar
    set theCal to current application's class "NSCalendar"'s currentCalendar()
    -- unpack the components of theCal to a variable
    set theComponents to theCal's components:254 fromDate:(current application's NSDate's |date|())
    theComponents's setSecond:0
    theComponents's setMinute:0
    theComponents's setHour:12
    theComponents's setYear:2015
    theComponents's setMonth:4
    theComponents's setDay:1
    datePicker's setDateValue:(theCal's dateFromComponents:theComponents)

    -- get the size it needs
    set theSize to datePicker's fittingSize()
    --resize the picker and view accordingly
    theView's setFrameSize:theSize
    datePicker's setFrameSize:theSize
    -- add the picker to the view
    theView's setSubviews:{datePicker}
    -- create an alert
    set theAlert to current application's NSAlert's alloc()'s init()
    -- set up alert
    tell theAlert
        its setMessageText:"Pick a date and time"
        its setInformativeText:"Any date"
        its addButtonWithTitle:"OK"
        its addButtonWithTitle:"Cancel"
        its setAccessoryView:theView
    end tell

    -- show alert in modal loop
    set returnCode to theAlert's runModal()
    if returnCode = (current application's NSAlertSecondButtonReturn) then error number -128
    -- retrieve date
    #set theDate to datePicker's dateValue() as date
    # Shane Stanley
    # http://macscripter.net/viewtopic.php?pid=119633#p119633
    set theDate to datePicker's dateValue()
    #set theCal to current application's class "NSCalendar"'s currentCalendar()
    -- unpack the components of theDate to a variable
    set theComponents to theCal's components:254 fromDate:theDate
    set theDate to current date
    set day of theDate to 1 -- important
    set seconds of theDate to theComponents's |second|()
    set year of theDate to theComponents's |year|()
    set month of theDate to theComponents's |month|()
    set day of theDate to theComponents's |day|()
    set hours of theDate to theComponents's hour()
    set minutes of theDate to theComponents's minute()

    return (theDate as text)
    #return (theDate)
end datePicker

The date picker does not display (at least on Mojave). It does display, however, if I put a display alert before runModal (but not otherwise).

I understand this may have to do with a need for performSelectorOnMainThread in latter Mac OSX versions, but if that is the case, I was not able to add it such that the code would work. Any ideas?

CJK
  • 5,732
  • 1
  • 8
  • 26
Brett Zamir
  • 14,034
  • 6
  • 54
  • 77

1 Answers1

2

Here you go... Not much to say really. I basically just rewrote it because I can't stand messy code:

# 06/04/16 09:35:02
# Author: Shane Stanley
# Adapted by Christopher Stone
# Fixed & Rewritten by CJK
--------------------------------------------------------------------------------
use framework "AppKit"
use scripting additions

property this : a reference to the current application
property nil : a reference to missing value
property _1 : a reference to reference

property NSAlert : a reference to NSAlert of this
property NSDatePicker : a reference to NSDatePicker of this
property NSView : a reference to NSView of this

property NSAlertSecondButtonReturn : 1001
property NSHourMinuteSecondDatePickerElementFlag : 14
property NSTextFieldAndStepperDatePickerStyle : 0
property NSYearMonthDayDatePickerElementFlag : 224
--------------------------------------------------------------------------------
property date : missing value
--------------------------------------------------------------------------------
on run
    its performSelectorOnMainThread:("showDatePicker:") withObject:{¬
        NSTextFieldAndStepperDatePickerStyle, ¬
        NSYearMonthDayDatePickerElementFlag + ¬
        NSHourMinuteSecondDatePickerElementFlag} ¬
        waitUntilDone:true

    return my date
end run

on showDatePicker:params
    local params

    set {PickerStyle, PickerElements} to params

    tell (current date) to set ¬
        [dateFrom, day, its month, day, year, time] to ¬
        [it, 1, 4, 1, 2015, 12 * hours + 0 * minutes]

    tell NSDatePicker's alloc()
        initWithFrame_({{0, 0}, {100, 100}})
        setDatePickerStyle_(PickerStyle)
        setDatePickerElements_(PickerElements)
        setDateValue_(dateFrom)
        set fittingSize to fittingSize()
        setFrameSize_(fittingSize)

        set View to NSView's alloc()
        View's initWithFrame:{{0, 0}, {100, 175}}
        View's setFrameSize:fittingSize
        View's addSubview:it

        tell NSAlert's alloc()
            init()
            setMessageText_("Pick a date and time")
            setInformativeText_("Any date")
            addButtonWithTitle_("OK")
            addButtonWithTitle_("Cancel")
            setAccessoryView_(View)

            runModal()
        end tell

        set my date to dateValue() as date
    end tell
end showDatePicker:
---------------------------------------------------------------------------❮END❯

System info: AppleScript version: 2.7 System version: 10.13.6

CJK
  • 5,732
  • 1
  • 8
  • 26
  • This looks great, thank you, and I see it runs from Script Editor. I'm eager to compare it to the old version. But first--can I just ask--do you know why this doesn't run when I use `osascript datepicker.scpt` against this file from Terminal? – Brett Zamir May 20 '19 at 11:53
  • I've added Terminal for now to System Preferences Accessibility as well as chmod 0755 and a `#!/usr/bin/osascript` shebang as I saw in use elsewhere, and these didn't work either – Brett Zamir May 20 '19 at 12:01
  • I believe this sort of script needs a GUI context in which to run in order to be able to draw windows, graphics contexts, and views. This means any program that uses `osascript` to execute its AppleScripts also won't be able to do much with scripts that contain `NSWindow`, `NSView`, `WKWebView`, and similar classes of object, even though those programs run in the GUI. You're basically limited to _Script Editor_, _Script Debugger_, and _FastScripts_, or creating your own AppleScript environment. – CJK May 20 '19 at 12:01
  • I was just hoping to update this tool which is intended for use in a Node application where one doesn't have such a GUI environment (and I don't know that I am motivated to learn how to do all that if it is involved as I'd suspect). – Brett Zamir May 20 '19 at 12:04
  • I see it works compiling to an app, but I don't know if there'd be any way to get a command line result back from it... – Brett Zamir May 20 '19 at 12:07
  • Seems above my pay grade and limited time to figure this out. :) (and paying for it won't work for an open source tool). Was looking at using the Applescript in Automator, but it doesn't seem that returning the date there returns it to command line. – Brett Zamir May 20 '19 at 12:26
  • Maybe I can pipe in the Applescript results in automator to a shell script which returns them... Kind of driving blind here--but I'll mark yours as the answer, as it seems you've gotten the basics working again, thank you! – Brett Zamir May 20 '19 at 12:30
  • 1
    I want a Node script to spawn the Applescript and get the date back. It looks like I may be able to do `/usr/bin/automator datepicker.workflow` after using Automator to make a workflow with this AppleScript. It looks like I could even have (as observed by http://krypted.com/mac-os-x/invoking-automator-workflows-command-line/ ) a workflow that builds the Automator script out of the Applescript file. As open source code, I would just like to have the build process be repeatable and transparent. – Brett Zamir May 20 '19 at 12:45
  • Btw, are you cool with your code going as part of our MIT license? – Brett Zamir May 20 '19 at 12:53
  • 1
    Nice findings, Re: _Automator_. And, yes, I'm fine with the sharing of code. I put some of my code up on GitHub under the same licence, so it's good to have it being put to use. – CJK May 20 '19 at 12:57
  • This is great, thanks very much. It's a pity that (as per `man automator`), one can't actually create workflows from the command line though (unless one already has a workflow that one trusts to create other workflows). – Brett Zamir May 20 '19 at 12:59