Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

カテゴリー: dialog

クリップボード内のテキストをSayコマンドで読み上げて音声ファイル化

Posted on 5月 27, 2020 by Takaaki Naganoya

読み上げ対象のテキストをコピーした状態で実行するAppleScriptです。クリップボード内のテキストをSayコマンドで読み上げて、音声ファイルにレンダリングします。出力後、読み上げ所要時間を出力ファイルから求めてダイアログ表示します。

音声レンダリング処理は実際の音声読み上げ処理よりも短い時間で完了します。

–> Downlad Script With library within its bundle

掲載のリストを実行しても、スライダー入力ライブラリが含まれていないため、そのままでは実行できません。↑のScriptをまるごとダウンロードして展開すると、ライブラリ入りのScriptになります。実行にはダウンロードしたScriptをご利用ください。プログラムリスト掲載は参考のために行なっているものです。


▲ステップ1:念のために、読み上げ対象テキストをダイアログ表示


▲ステップ2:読み上げ時の速度をスライダーで選択


▲ステップ3:読み上げ音声を選択


▲ステップ4:読み上げ所要時間をダイアログ表示


▲ステップ5:音声レンダリングしたファイルをQuickTime Playerでオープン

AppleScript名:クリップボード内のテキストをSayコマンドで読み上げて音声ファイル化.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/05/27
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "AVFoundation"
use scripting additions
use slLib : script "sliderLib"

property |NSURL| : a reference to current application’s |NSURL|
property NSDate : a reference to current application’s NSDate
property NSUUID : a reference to current application’s NSUUID
property NSFileManager : a reference to current application’s NSFileManager
property AVAudioPlayer : a reference to current application’s AVAudioPlayer
property NSDateFormatter : a reference to current application’s NSDateFormatter
property NSSpeechSynthesizer : a reference to current application’s NSSpeechSynthesizer

set aInfo to clipboard info
set uCount to (clipboard info for Unicode text)
if uCount = {} then
  display dialog "There is no text information in the clipboard" with title "Terminate information" buttons {"OK"} default button 1 with icon 2
  
return
end if

set totalCount to item 2 of item 1 of uCount

set aStr to the clipboard as text

–読み上げ内容の確認
display dialog aStr with title "Text length:" & (uCount as string) & " chars."

–読み上げ速度をSliderで入力
set rRes to slLib’s chooseBySlider(180, 220, "Select TTS reading pitch (small number:slow)")

–読み上げTTSキャラクタの選択
set aLoc to (current application’s NSLocale’s currentLocale()’s identifier()) as string –>  "ja_JP"
set vList to getTTSVoiceNameWithLanguage(aLoc) of me
set vRes to choose from list vList with prompt ("Select TTS Voice in your language :" & aLoc) without empty selection allowed
if vRes = false then return

set vCharacter to contents of first item of vRes

–音声ファイルの作成先パスを求める
set aUUID to NSUUID’s UUID()’s UUIDString() as string
set aPath to (((path to movies folder) as string) & aUUID & ".aif")
set aPOSIX to POSIX path of aPath

–音声レンダリング
tell current application
  say aStr using vCharacter saving to (aPOSIX) speaking rate rRes without waiting until completion
end tell

–レンダリングした音声の読み上げ所要時間を計算
set aDur to getDuration(aPath as alias) of me

–レンダリングした音声ファイルをオープン
tell application "QuickTime Player"
  open aPath
end tell

–完了報告
display dialog "読み上げ所要時間:" & my formatHMS(aDur)

on getTTSVoiceNameWithLanguage(voiceLang)
  set outArray to current application’s NSMutableArray’s new()
  
  
set aList to NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_("VoiceLocaleIdentifier == %@", voiceLang)
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aResList to (filteredArray’s valueForKey:"VoiceName") as list
  
  
return aResList
end getTTSVoiceNameWithLanguage

on getDuration(aFile)
  set aURL to |NSURL|’s fileURLWithPath:(POSIX path of aFile)
  
  
repeat 1000 times
    set aAudioPlayer to AVAudioPlayer’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
    
set aRes to aAudioPlayer’s prepareToPlay()
    
if aRes as boolean = true then exit repeat
    
delay 0.5
  end repeat
  
  
set channelCount to aAudioPlayer’s numberOfChannels()
  
set aDuration to aAudioPlayer’s duration()
  
return aDuration as real
end getDuration

on retAvailableTTSnames()
  set outList to {}
  
  
set aList to NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aInfo to (NSSpeechSynthesizer’s attributesForVoice:j)
    
set aInfoRec to aInfo as record
    
set aName to VoiceName of aInfoRec
    
set the end of outList to aName
  end repeat
  
  
return outList
end retAvailableTTSnames

on formatHMS(aTime)
  set aDate to NSDate’s dateWithTimeIntervalSince1970:aTime
  
set aFormatter to NSDateFormatter’s alloc()’s init()
  
  
—This formatter text is localized in Japanese.
  
if aTime < hours then
    aFormatter’s setDateFormat:"mm分ss秒"
  else if aTime < days then
    aFormatter’s setDateFormat:"HH時間mm分ss秒"
  else
    aFormatter’s setDateFormat:"DD日HH時間mm分ss秒"
  end if
  
  
set timeStr to (aFormatter’s stringFromDate:aDate) as string
  
return timeStr
end formatHMS

★Click Here to Open This Script 

Posted in dialog file Language Locale Text | Tagged 10.13savvy 10.14savvy 10.15savvy AVAudioPlayer NSDate NSDateFormatter NSFileManager NSMutableArray NSPredicate NSSpeechSynthesizer NSURL NSUUID | Leave a comment

アラートダイアログ上にWebViewでGoogle Chartsを表示(Geo Chart 1)

Posted on 5月 24, 2020 by Takaaki Naganoya

NSAlertのアラートダイアログ上にGoogleChartsを用いたGeoChartを表示するAppleScriptです。

Geo Chartに表示されている地図の上でマウスカーソルを移動させると、Chartが反応してデータ表示を行います。

本Scriptに掲載しているAPI KeyはGoogleに掲載されているサンプル用のものであり、サンプルデータの表示以外には使用できません。Google開発者アカウントを取得し、API Keyを取得して記入しておく必要があります。

AppleScript名:アラートダイアログ上にWebViewでGoogle Chartを表示(Geo Chart 1).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/02
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "WebKit"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSAlert : a reference to current application’s NSAlert
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSButton : a reference to current application’s NSButton
property NSBundle : a reference to current application’s NSBundle
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property WKUserContentController : a reference to current application’s WKUserContentController
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property returnCode : 0
property aBrowserAgentRes : ""

set mapsAPIKey to "AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY" –for demonstration only

my performSelectorOnMainThread:"getSafariUserAgentString:" withObject:(missing value) waitUntilDone:true

–Sample Data
set aList to {{"Country", "Popularity"}, ¬
  {"Germany", 200}, ¬
  {
"United States", 300}, ¬
  {
"Brazil", 400}, ¬
  {
"Canada", 500}, ¬
  {
"France", 600}, ¬
  {
"RU", 700}}

set aJsonArrayStr to array2DToJSONArray(aList) of me

–Map Template HTML
set myStr to "<!DOCTYPE html>
<html lang=\"UTF-8\">
<head>
<script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script>

<script type=\"text/javascript\">
google.charts.load(’current’, {’packages’:[’geochart’], ’mapsApiKey’: ’%@’});
google.charts.setOnLoadCallback(drawRegionsMap);

function drawRegionsMap() {
var data = google.visualization.arrayToDataTable(%@);

var options = {};

var chart = new google.visualization.GeoChart(document.getElementById(’regions_div’));
chart.draw(data, options);
};
</script>
</head>
<body>
  <div id=\"regions_div\" style=\"width: 900px; height: 500px;\"></div>
</body>
</html>"

set aString to NSString’s stringWithFormat_(myStr, mapsAPIKey, aJsonArrayStr) as string

set paramObj to {myMessage:"Google GeoChart Test", mySubMessage:"This is a simple GeoChart using google charts", htmlStr:aString}
–my browseStrWebContents:paramObj –for debug
my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true

on browseStrWebContents:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set htmlString to (htmlStr of paramObj)
  
  
set aWidth to 920
  
set aHeight to 520
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
  
–指定HTML内のJavaScriptをFetch
  
set jsSource to pickUpFromToStr(htmlString, "<script type=\"text/javascript\">", "</script>") of me
  
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) configuration:aConf
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
aWebView’s setCustomUserAgent:(my aBrowserAgentRes)
  
  
set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
  
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aWebView
    
    
set myWindow to its |window|
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
–Stop Web View Action
  
set bURL to |NSURL|’s URLWithString:"about:blank"
  
set bReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:bReq
  
  
if (my returnCode as number) = 1001 then error number -128
end browseStrWebContents:

on doModal:aParam
  set (my returnCode) to (aParam’s runModal()) as number
end doModal:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
  set a1Offset to offset of s1Str in aStr
  
if a1Offset = 0 then return false
  
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
  
set a2Offset to offset of s2Str in bStr
  
if a2Offset = 0 then return false
  
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
  
return cStr as string
end pickUpFromToStr

–リストを任意のデリミタ付きでテキストに
on retArrowText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retArrowText

on array2DToJSONArray(aList)
  set anArray to current application’s NSMutableArray’s arrayWithArray:aList
  
set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
  
set resString to NSString’s alloc()’s initWithData:jsonData encoding:(NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

–Safariと同じUser Agentの文字列を取得する
on getSafariUserAgentString:anObject
  set aBundle to NSBundle’s bundleWithIdentifier:"com.apple.Safari"
  
set aVer to (aBundle’s infoDictionary()’s objectForKey:"CFBundleShortVersionString") as text
  
–>  "9.0.2"
  
  
set aBuildNo to (aBundle’s infoDictionary()’s objectForKey:"CFBundleVersion") as text
  
–>  "11601.3.6"
  
  
set {v1, v2, v3} to parseVersionNumber(aBuildNo) of me
  
set aV1 to NSString’s stringWithString:v1
  
set bV1 to aV1’s substringFromIndex:2 –3 characters from tail –?????
  
–> "601"
  
  
set bStr to retStrFromArrayWithDelimiter({bV1, v2, v3}, ".") of me
  
  
set aWebView to WebView’s alloc()’s init()
  
set aRes to (aWebView’s stringByEvaluatingJavaScriptFromString:"navigator.userAgent") as text
  
–>  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.6 (KHTML, like Gecko)"
  
  
set aBrowserAgentRes to (aRes & " Version/" & aVer & " Safari/" & bStr)
end getSafariUserAgentString:

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on parseVersionNumber(a)
  set aStr to current application’s NSString’s stringWithString:a
  
set aRes to (aStr’s componentsSeparatedByString:".")
  
set bRes to aRes’s allObjects()
  
return bRes as list
end parseVersionNumber

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★Click Here to Open This Script 

Posted in dialog | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSArray NSBundle NSButton NSRunningApplication NSString NSURL NSURLRequest NSUTF8StringEncoding WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | Leave a comment

アラートダイアログのウィンドウタイトル部分にアイコン指定

Posted on 5月 23, 2020 by Takaaki Naganoya

アラートダイアログのウィンドウタイトル部分に任意の画像をアイコンとして表示するAppleScriptです。

本来、NSWindowのタイトル部分にアイコン表示を行うワザのようです。なにか、Cocoaのバグのような仕様を使っているようです。本来、タイトル部分に直接アイコンを表示させることはできないのですが、ドキュメントのURLを指定すると、該当するパス(URL)の書類アイコンを指定する機能はあります。


▲Mac App Store上で販売中のAppleScriptで開発したアプリケーション「Kamenoko」でも、ウィンドウにアイコン表示するコードが使われています。本Scriptではなく、もっとまっとうな方法で

そこに、存在しないURLを指定すると、直後にアイコン指定できるという、、、どこで役立つのかわかりませんが、そういう方法もあるということで。

AppleScript名:アラートダイアログでウィンドウタイトル部分にアイコン指定
— Created 2020-05-08 by Takaaki Naganoya
— 2020 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property |NSURL| : a reference to current application’s |NSURL|
property NSImage : a reference to current application’s NSImage
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSImageNameComputer : a reference to current application’s NSImageNameComputer
property NSWindowDocumentIconButton : a reference to current application’s NSWindowDocumentIconButton

property returnCode : 0

–Get Computer Icon
set anImage to getComputerIcon() of me
set aMainMes to "TEST"
set aSubMes to "Window title icon TEST"
set wTitle to "Window Title"

set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myWinTitle:wTitle, myImage:anImage}
–my displayAlertDialog:paramObj

my performSelectorOnMainThread:"displayAlertDialog:" withObject:(paramObj) waitUntilDone:true

on displayAlertDialog:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set anImage to myImage of paramObj
  
set aWinTitle to myWinTitle of paramObj
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
–its setIcon:anImage
    
set aWin to its |window|()
  end tell
  
  
aWin’s setTitle:aWinTitle
  
  
aWin’s setRepresentedURL:(|NSURL|’s URLWithString:"WindowTitle") –Dummy URL
  (
aWin’s standardWindowButton:(current application’s NSWindowDocumentIconButton))’s setImage:(anImage)
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
end displayAlertDialog:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

on getComputerIcon()
  return NSImage’s imageNamed:(NSImageNameComputer)
end getComputerIcon

★Click Here to Open This Script 

Posted in dialog | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSImage NSImageNameComputer NSRunningApplication NSURL NSWindowDocumentIconButton | Leave a comment

slider+buttonを作成 v5

Posted on 5月 21, 2020 by Takaaki Naganoya

スライダーをダイアログ上に表示して、ユーザーの入力を求めるAppleScriptです。


▲Script Editor上で実行


▲Script Debugger上で実行


▲Script Menu上で実行


▲Switch Control上で実行

最近はこうした箱庭ダイアログシリーズも書き方がこなれてきて、ランタイム環境に依存せずに(実際は、特定の環境で動かないということが少なくなりつつ)実行できるようになってきました。

スライダーコントロールはこうした箱庭シリーズの初期にいろいろいじくって試していたものですが、あまりに初期すぎて書き方が古いものが多かったので、最新のこなれた書き方で書き直しておきました。

ただ、スライダーコントロールが単体でダイアログ上に配置されていても、実際にハマる用途が存在せず、単なるデモンストレーションに終始してしまいそうであります。

アラートダイアログの通知メッセージ(一番大きな文字で表示される)をスライダーで変更することができなかったので、ダイアログのタイトルでスライダーの値を表示するようにしています。スライダーが必要な状況というのは、いくつかのパラメータを一緒に設定するケースが多いはずなので、やっぱり単体で設定できても意味がないような気がとてもします。

AppleScript名:slider+buttonを作成 v5
— Created 2020-5-17 by Takaaki Naganoya
— 2020 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSSlider : a reference to current application’s NSSlider
property NSSplitView : a reference to current application’s NSSplitView
property NSTickMarkBelow : a reference to current application’s NSTickMarkBelow
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSScreenSaverWindowLevel : a reference to current application’s NSScreenSaverWindowLevel

property windisp : false
property wController : false
property aSliderValMSG : ""

property sliderRes : 0

set aMaxVal to 12
set aButtonMSG to "OK"
set winWidth to 400
set aSliderValMSG to "Slider Value : "

set paramObj to {myMax:aMaxVal, myBMes:aButtonMSG, mySliderMes:aSliderValMSG, myWidth:winWidth}
–my getSliderValue:paramObj
my performSelectorOnMainThread:"getSliderValue:" withObject:(paramObj) waitUntilDone:true
return sliderRes

on getSliderValue:paramObj
  set aMaxVal to (myMax of paramObj) as real
  
set aButtonMSG to (myBMes of paramObj) as string
  
set aSliderValMSG to (mySliderMes of paramObj) as string
  
set winWidth to (myWidth of paramObj) as real
  
  
set (my aSliderValMSG) to aSliderValMSG
  
  
set aView to NSSplitView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, winWidth, 40))
  
aView’s setVertical:false
  
  
–Sliderをつくる
  
set aSlider to makeSider(aMaxVal) of me
  
aView’s setSubviews:{aSlider}
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:""
    
–its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aView
    
set aWin to its |window|()
    
set bList to buttons()
  end tell
  
  
aWin’s setTitle:(my aSliderValMSG & (sliderRes as text))
  
aWin’s setLevel:NSScreenSaverWindowLevel
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
  
set sliderRes to (aSlider’s intValue()) as number
end getSliderValue:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

on sliderChanged:aSender
  set sliderRes to aSender’s intValue()
  
set parentWin to aSender’s |window|()
  
parentWin’s setTitle:(my aSliderValMSG & (sliderRes as text))
end sliderChanged:

on makeSider(aMaxNum)
  set aSlider to NSSlider’s alloc()’s init()
  
aSlider’s setMaxValue:aMaxNum
  
aSlider’s setMinValue:1
  
aSlider’s setNumberOfTickMarks:aMaxNum
  
aSlider’s setKnobThickness:50
  
aSlider’s setAllowsTickMarkValuesOnly:true
  
aSlider’s setTickMarkPosition:(NSTickMarkBelow)
  
set sliderRes to (aMaxNum div 2)
  
aSlider’s setIntValue:sliderRes
  
aSlider’s setTarget:me
  
aSlider’s setAction:("sliderChanged:")
  
return aSlider
end makeSider

★Click Here to Open This Script 

Posted in dialog | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSRunningApplication NSSlider NSSplitView | Leave a comment

クリップボード内の文字種別を集計して円グラフ表示

Posted on 5月 16, 2020 by Takaaki Naganoya

クリップボード内に文字列が入っていれば、いいかえれば「文字列をコピーした状態であれば」、クリップボード内容をテキストとして取り出して文字種別ごとに集計して構成比を円グラフで表示するAppleScriptです。

# CAUTION: This script process Japanese characters. So, this script make no sense for other language users

グラフ表示部分は手抜きでGoogle Chartsを呼び出しているだけなので、Macがネットワークに接続されていない場合には表示できません。

macOS標準装備のScript Menuに入れて使っています。複数の円グラフを表示させることも可能なので、典型的な例文(新聞、論文、なろう系、技術系文章、文学作品)のグラフを一覧表示して、どの例文の使用比率に近いかといったことを見てわかるようにできそうです(やらないけど)。

ありものをただ引っ張り出してきて、Script文でつないだだけなので、オリジナルで記述した部分はほとんどありません。

とはいえ、技術的にはいろいろなハードルを乗り越えまくって動かしているものでもあります。

・メインスレッドでしか動かせないWkWebView、NSAlertをScriptから呼び出している(実行環境に左右されずに実行)
・Cocoa Scriptingを行うAppleScriptObjCをscript文でscript object化して(カプセル化して)呼び出し、再利用
・WkWebViewをdialog内に表示して、マウスオーバーでデータ内容が見えるようなインタラクティブなグラフを表示

といった、いろいろ無茶なことをやっているScriptです。ただ、すでに見慣れた光景になりつつありますけれども。

AppleScript名:クリップボード内の文字種別を集計して円グラフ表示.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/05/14
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "WebKit"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSAlert : a reference to current application’s NSAlert
property NSString : a reference to current application’s NSString
property NSButton : a reference to current application’s NSButton
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property WKUserContentController : a reference to current application’s WKUserContentController
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property NSScreenSaverWindowLevel : a reference to current application’s NSScreenSaverWindowLevel
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property returnCode : 0

–Calc Clipboard
set aRes to clipAnaliticsMain() of clipboardInfoKit
if aRes = false then return —クリップボードが空だった(文字列的に)

set totalC to totalC of aRes

set aList to {{"文字種別", "構成比"}} & rating of aRes

set aJsonArrayStr to array2DToJSONArray(aList) of me

–Pie Chart Template HTML
set myStr to "<!DOCTYPE html>
<html lang=\"UTF-8\">
<body>
<div id=\"piechart\"></div>

<script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script>

<script type=\"text/javascript\">
// Load google charts
google.charts.load(’current’, {’packages’:[’corechart’]});
google.charts.setOnLoadCallback(drawChart);

// Draw the chart and set the chart values
function drawChart() {
var data = google.visualization.arrayToDataTable(%@);

// Optional; add a title and set the width and height of the chart
var options = {
is3D: true,
   ’width’:600, ’height’:400
};

// Display the chart inside the <div> element with id=\"piechart\"
var chart = new google.visualization.PieChart(document.getElementById(’piechart’));
chart.draw(data, options);
}
</script>

</body>
</html>"

set aString to current application’s NSString’s stringWithFormat_(myStr, aJsonArrayStr) as string

set paramObj to {myMessage:"文字種別構成比", mySubMessage:"クリップボードの内容を集計。文字数は" & (totalC as string) & "文字", htmlStr:aString}
–my browseStrWebContents:paramObj–for debug
my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true

on browseStrWebContents:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set htmlString to (htmlStr of paramObj)
  
  
set aWidth to 600
  
set aHeight to 450
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
  
–指定HTML内のJavaScriptをFetch
  
set jsSource to pickUpFromToStr(htmlString, "<script type=\"text/javascript\">", "</script>") of me
  
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight – 100)) configuration:aConf
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
  
set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
  
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aWebView
    
    
set myWindow to its |window|
  end tell
  
  
myWindow’s setLevel:(NSScreenSaverWindowLevel)
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
–Stop Web View Action
  
set bURL to |NSURL|’s URLWithString:"about:blank"
  
set bReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:bReq
  
  
if (my returnCode as number) = 1001 then error number -128
end browseStrWebContents:

on doModal:aParam
  set (my returnCode) to (aParam’s runModal()) as number
end doModal:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
  set a1Offset to offset of s1Str in aStr
  
if a1Offset = 0 then return false
  
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
  
set a2Offset to offset of s2Str in bStr
  
if a2Offset = 0 then return false
  
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
  
return cStr as string
end pickUpFromToStr

–リストを任意のデリミタ付きでテキストに
on retArrowText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retArrowText

on array2DToJSONArray(aList)
  set anArray to current application’s NSMutableArray’s arrayWithArray:aList
  
set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
  
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

script clipboardInfoKit
  use scripting additions
  
use framework "Foundation"
  
property parent : AppleScript
  
  
property NSString : a reference to current application’s NSString
  
property NSNumber : a reference to current application’s NSNumber
  
property NSDictionary : a reference to current application’s NSDictionary
  
property NSCountedSet : a reference to current application’s NSCountedSet
  
property NSCharacterSet : a reference to current application’s NSCharacterSet
  
property NSMutableArray : a reference to current application’s NSMutableArray
  
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
  
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch
  
property NSNumberFormatterRoundUp : a reference to current application’s NSNumberFormatterRoundUp
  
property NSNumberFormatterRoundDown : a reference to current application’s NSNumberFormatterRoundDown
  
  
  
on clipAnaliticsMain()
    set cCount to 0
    
set hCount to 0
    
set kCount to 0
    
set oCount to 0
    
set tCount to 0
    
    
using terms from scripting additions
      set aStr to (the clipboard as «class utf8»)
      
if aStr = "" then
        display dialog "No text data in clipboard" buttons {"OK"} default button 1
        
return false
      end if
    end using terms from
    
    
set aRec to detectCharKindRating(aStr) of me
    
    
set cCount to cCount + (kanjiNum of aRec)
    
set hCount to hCount + (hiraganaNum of aRec)
    
set kCount to kCount + (katakanaNum of aRec)
    
set oCount to oCount + (otherNum of aRec)
    
set tCount to tCount + (totalCount of aRec)
    
    
return {rating:{{"漢字", cCount}, {"ひらがな", hCount}, {"カタカナ", kCount}, {"その他", oCount}}, totalC:tCount}
  end clipAnaliticsMain
  
  
  
  
on detectCharKindRating(aStr as string)
    set aList to NSMutableArray’s arrayWithArray:(characters of aStr)
    
set theCountedSet to NSCountedSet’s alloc()’s initWithArray:aList
    
set theEnumerator to theCountedSet’s objectEnumerator()
    
    
set cCount to 0
    
set hCount to 0
    
set kCount to 0
    
set oCount to 0
    
set totalC to length of aStr
    
    
repeat
      set aValue to theEnumerator’s nextObject()
      
if aValue is missing value then exit repeat
      
      
set aStr to aValue as string
      
set tmpCount to (theCountedSet’s countForObject:aValue)
      
      
set s1Res to chkKanji(aStr) of me
      
set s2Res to chkKatakana(aStr) of me
      
set s3Res to chkHiragana(aStr) of me
      
      
if s1Res = true then
        set cCount to cCount + tmpCount
      else if s2Res = true then
        set kCount to kCount + tmpCount
      else if s3Res = true then
        set hCount to hCount + tmpCount
      else
        set oCount to oCount + tmpCount
      end if
    end repeat
    
    
set ckRes to roundingUp((cCount / totalC) * 100, 1) of me
    
set kkRes to roundingUp((kCount / totalC) * 100, 1) of me
    
set hgRes to roundingUp((hCount / totalC) * 100, 1) of me
    
set otRes to roundingUp((oCount / totalC) * 100, 1) of me
    
    
return {kanjiNum:cCount, kanjiRating:ckRes, hiraganaNum:hCount, hiraganaRating:hgRes, katakanaNum:kCount, katakanaRating:kkRes, otherNum:oCount, otherRating:otRes, totalCount:totalC}
  end detectCharKindRating
  
  
  
on chkKanji(aChar)
    return detectCharKind(aChar, "[一-龠]") of me
  end chkKanji
  
  
on chkHiragana(aChar)
    return detectCharKind(aChar, "[ぁ-ん]") of me
  end chkHiragana
  
  
on chkKatakana(aChar)
    return detectCharKind(aChar, "[ァ-ヶ]") of me
  end chkKatakana
  
  
on detectCharKind(aChar, aPattern)
    set aChar to NSString’s stringWithString:aChar
    
set searchStr to NSString’s stringWithString:aPattern
    
set matchRes to aChar’s rangeOfString:searchStr options:(NSRegularExpressionSearch)
    
if matchRes’s location() = (current application’s NSNotFound) or (matchRes’s location() as number) > 9.99999999E+8 then
      return false
    else
      return true
    end if
  end detectCharKind
  
  
on roundingUp(aNum, aDigit as integer)
    set a to aNum as real
    
set aFormatter to NSNumberFormatter’s alloc()’s init()
    
aFormatter’s setMaximumFractionDigits:aDigit
    
aFormatter’s setRoundingMode:(NSNumberFormatterRoundUp)
    
set aStr to aFormatter’s stringFromNumber:(NSNumber’s numberWithFloat:a)
    
return (aStr as text) as real
  end roundingUp
end script

★Click Here to Open This Script 

Posted in dialog Internet JavaScript Text | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSButton NSCharacterSet NSCountedSet NSDictionary NSMutableArray NSNumber NSNumberFormatter NSNumberFormatterRoundDown NSNumberFormatterRoundUp NSRegularExpressionSearch NSRunningApplication NSScreenSaverWindowLevel NSString NSURL NSURLRequest NSUTF8StringEncoding WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | Leave a comment

アラートダイアログ上にWkWebViewでGoogle Chartsを表示 v2

Posted on 4月 29, 2020 by Takaaki Naganoya

アラートダイアログ上にWkWebViewを配置し、Google Chartsを用いてPie Chartを表示するAppleScriptです。

自分の開発環境(MacBook Pro Retina 2012, Core i7 2.6GHz)で10日間のアクセス履歴を処理して1.38秒程度でダイアログ表示してグラフ描画に入ります。3秒強ぐらいで描画が終了します。

WkWebViewに対して、文字列で組み立てたHTMLを読み込んでグラフ表示しています。Pie Chart上にマウスカーソルを乗せると反応します。一応表示できているぐらいで、デザイン的には余計な余白ができたままで、いまひとつな感じです。せめて、WkWebViewの背景を透明化できると大味な表示をいくらか緩和できるかと思っているのですが、WkWebViewの透明背景にはNSColorではなく(AppleScriptから作成できない)CGColorでclearColor(透明色)を指定する必要があるようで、、、、

サンプル表示データは、Safariの10日間のアクセス履歴からドメイン単位で計算したヒストグラムを、その場で計算したものを使っています。

本Scriptは、①NSAlertのダイアログ表示の経験 ②WkWebView上へのコンテンツ表示の経験 ③グラフ表示用FrameworkやSVGベースのグラフ作成の経験 を経て、2・3年越しで完成したものです。とくに、WkWebViewについてはサンプル数がそれほど多くなく、用途の方向性がObjective-CやSwiftと若干異なるために、参考にできるサンプルの数が多くない状況。必然的に、いろいろ試しては失敗を重ねてきました。急に出来上がってきたものではありません。

Safariの履歴集計ScriptをScriptオブジェクトの中に放り込んだら、scripting additionsの用語をusing terms from句で囲っておく必要がありました。何回か経験していますが、知らないと対処に困る現象です。

現状(macOS 10.14以降)では、Scripting AdditionはmacOS標準搭載のStandard Additionsしか存在していないし認識も行われないため、エラー内容がいまひとつわかりにくく、不正確であるように思えます。Apple純正のStandard Additionsしか存在していないんだから自分で判断しろやコラ、と言いたくなります。

AppleScript名:アラートダイアログ上にWebViewでGoogle Chartを表示 v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/02
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "WebKit"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSAlert : a reference to current application’s NSAlert
property NSString : a reference to current application’s NSString
property NSButton : a reference to current application’s NSButton
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property WKUserContentController : a reference to current application’s WKUserContentController
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property returnCode : 0

–Calc Safari access domain from history
set aRes to calcSafariHistoryBest10Domain(10) of safariHistoryLib

set aList to {{"Domain", "Access"}}
repeat with i in aRes
  set aDom to theName of i
  
set accessNum to numberOfTimes of i
  
set the end of aList to {aDom, accessNum}
end repeat

–set aList to {{"Task", "Hours per Day"}, {"Work", 8}, {"Eat", 2}, {"TV", 4}, {"Gym", 2}, {"Sleep", 8}}

set aJsonArrayStr to array2DToJSONArray(aList) of me

–Pie Chart Template HTML
set myStr to "<!DOCTYPE html>
<html lang=\"UTF-8\">
<body>
<div id=\"piechart\"></div>

<script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script>

<script type=\"text/javascript\">
// Load google charts
google.charts.load(’current’, {’packages’:[’corechart’]});
google.charts.setOnLoadCallback(drawChart);

// Draw the chart and set the chart values
function drawChart() {
var data = google.visualization.arrayToDataTable(%@);

// Optional; add a title and set the width and height of the chart
var options = {’title’:’’, ’width’:600, ’height’:400};

// Display the chart inside the <div> element with id=\"piechart\"
var chart = new google.visualization.PieChart(document.getElementById(’piechart’));
chart.draw(data, options);
}
</script>

</body>
</html>"

set aString to current application’s NSString’s stringWithFormat_(myStr, aJsonArrayStr) as string

set paramObj to {myMessage:"Pie Chart Test", mySubMessage:"This is a simple pie chart using google charts", htmlStr:aString}
–my browseStrWebContents:paramObj–for debug
my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true

on browseStrWebContents:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set htmlString to (htmlStr of paramObj)
  
  
set aWidth to 600
  
set aHeight to 450
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
  
–指定HTML内のJavaScriptをFetch
  
set jsSource to pickUpFromToStr(htmlString, "<script type=\"text/javascript\">", "</script>") of me
  
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight – 100)) configuration:aConf
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
  
set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
  
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aWebView
    
    
set myWindow to its |window|
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
–Stop Web View Action
  
set bURL to |NSURL|’s URLWithString:"about:blank"
  
set bReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:bReq
  
  
if (my returnCode as number) = 1001 then error number -128
end browseStrWebContents:

on doModal:aParam
  set (my returnCode) to (aParam’s runModal()) as number
end doModal:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
  set a1Offset to offset of s1Str in aStr
  
if a1Offset = 0 then return false
  
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
  
set a2Offset to offset of s2Str in bStr
  
if a2Offset = 0 then return false
  
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
  
return cStr as string
end pickUpFromToStr

–リストを任意のデリミタ付きでテキストに
on retArrowText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retArrowText

on array2DToJSONArray(aList)
  set anArray to current application’s NSMutableArray’s arrayWithArray:aList
  
set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
  
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

–Safariのn日前からの履歴をドメイン別に集計してソート
script safariHistoryLib
  use scripting additions
  
use framework "Foundation"
  
property parent : AppleScript
  
  
property |NSURL| : a reference to current application’s |NSURL|
  
  
script spd
    property sList : {}
    
property nList : {}
    
property sRes : {}
  end script
  
  
  
on calcSafariHistoryBest10Domain(daysBefore)
    –現在日時のn日前を求める
    
using terms from scripting additions
      set origDate to (current date) – (daysBefore * days)
    end using terms from
    
set dStr to convDateObjToStrWithFormat(origDate, "yyyy-MM-dd hh:mm:ss") of me
    
    
set aDBpath to "~/Library/Safari/History.db"
    
set pathString to current application’s NSString’s stringWithString:aDBpath
    
set newPath to pathString’s stringByExpandingTildeInPath()
    
    
set aText to "sqlite3 " & newPath & " ’SELECT datetime(history_visits.visit_time+978307200, \"unixepoch\", \"localtime\"), history_visits.title || \" @ \" || substr(history_items.URL,1,max(length(history_items.URL)*(instr(history_items.URL,\" & \")=0),instr(history_items.URL,\" & \"))) as Info FROM history_visits INNER JOIN history_items ON history_items.id = history_visits.history_item where history_visits.visit_time>(julianday(\"" & dStr & "\")*86400-211845068000) ORDER BY visit_time ASC LIMIT 999999;’"
    
    
using terms from scripting additions
      set (sRes of spd) to do shell script aText
    end using terms from
    
    
set (sList of spd) to (paragraphs of (sRes of spd))
    
    
set (nList of spd) to {}
    
    
repeat with i in (sList of spd)
      set j to contents of i
      
      
–Parse each field
      
set j2 to parseByDelim(j, {"|", "@ "}) of me
      
      
–Calculate URL’s domain only
      
set j3 to contents of last item of j2
      
set aURL to (|NSURL|’s URLWithString:j3)
      
      
if aURL = missing value then
        set j4 to ""
      else
        set j4 to (aURL’s |host|()) as string
      end if
      
      
set the end of (nList of spd) to j4
    end repeat
    
    
–登場頻度でURLを集計
    
set aList to countItemsByItsAppearance((nList of spd)) of me
    
    
    
–Best 10の計算のために10位以下をまとめる
    
set best9 to items 1 thru 9 of aList
    
set best10 to items 10 thru -1 of aList
    
    
set otherTimes to 0
    
repeat with i in best10
      set otherTimes to otherTimes + (numberOfTimes of i)
    end repeat
    
    
set the end of best9 to {theName:"Other", numberOfTimes:otherTimes}
    
return best9
  end calcSafariHistoryBest10Domain
  
  
  
  
on parseByDelim(aData, aDelim)
    set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to aDelim
    
set dList to text items of aData
    
set AppleScript’s text item delimiters to curDelim
    
return dList
  end parseByDelim
  
  
  
–出現回数で集計
  
on countItemsByItsAppearance(aList)
    set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
    
set bArray to current application’s NSMutableArray’s array()
    
set theEnumerator to aSet’s objectEnumerator()
    
    
repeat
      set aValue to theEnumerator’s nextObject()
      
if aValue is missing value then exit repeat
      
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{"theName", "numberOfTimes"})
    end repeat
    
    
–出現回数(numberOfTimes)で降順ソート
    
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:"numberOfTimes" ascending:false
    
bArray’s sortUsingDescriptors:{theDesc}
    
    
return bArray as list
  end countItemsByItsAppearance
  
  
  
on convDateObjToStrWithFormat(aDateO as date, aFormatStr as string)
    set aDF to current application’s NSDateFormatter’s alloc()’s init()
    
    
set aLoc to current application’s NSLocale’s currentLocale()
    
set aLocStr to (aLoc’s localeIdentifier()) as string
    
    
aDF’s setLocale:(current application’s NSLocale’s alloc()’s initWithLocaleIdentifier:aLocStr)
    
aDF’s setDateFormat:aFormatStr
    
set dRes to (aDF’s stringFromDate:aDateO) as string
    
return dRes
  end convDateObjToStrWithFormat
  
end script

★Click Here to Open This Script 

Posted in dialog Web Contents Control | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSButton NSRunningApplication NSString NSURL NSURLRequest NSUTF8StringEncoding Safari WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | 1 Comment

ファイル保存ダイアログ(SavePanel)表示

Posted on 4月 22, 2020 by Takaaki Naganoya

Mac App StoreアプリケーションをAppleScriptで作ったときに、ファイル保存ダイアログを実装するのにchoose file nameコマンドでは拡張子の処理に問題があります(ユーザーが拡張子まで入力しなかった場合への対処を行うと逆効果に)。

choose file nameコマンド
ユーザーが選択、入力したパス:Macintosh HD:Users:me:Desktop:aaaaa
Script側で拡張子を補足:Macintosh HD:Users:me:Desktop:aaaaa.kame
--> エラー(Sandbox制限による)

その回避策としてありものを引っ張り出してきて手直ししたNSSavePanelベースのファイル保存ダイアログ表示サブルーチンです。


▲choose file nameコマンドで表示したファイル名入力ダイアログ。ダイアログ上でタグの選択はできるがchoose file nameにはタグ受信のための機能がない。また、拡張子が入力されていなかったらそのまま


▲本Scriptで表示したNSSavePanelによる保存ファイル名入力ダイアログ。タグの選択、指定した拡張子の指定などが行える。ユーザーが拡張子を入力していなくても自動で補える

choose file nameによるダイアログも、NSSavePanelによるダイアログも、見た目は同じで見分けはつきません。

普通にAppleScriptを書いて自分で実行している分にはchoose file nameコマンドでも問題はありません。

AppleScript名:ファイル保存ダイアログ(SavePanel)表示
— Original by Shane Stanley
— Modified 2020-04-22 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property savePath : ""
property saveTag : {}

set savePath to ""
set saveTag to {}

set aParam to {extList:{"kame"}, saveMsg:"新規ファイルの名前と場所を指定", saveName:"Some name", initialDir:(POSIX path of (path to documents folder))}
my performSelectorOnMainThread:"showModalSave:" withObject:(aParam) waitUntilDone:true
return {savePath:savePath as string, saveTags:saveTag as list}
–> {savePath:"/Users/me/Desktop/Some name.kame", saveTags:{"ブルー", "ヤンキー"}}

on showModalSave:sender
  set tmpExt to extList of sender as list
  
set tmpMsg to saveMsg of sender as string
  
set defName to saveName of sender as string
  
set tmpInit to initialDir of sender as string
  
  
using terms from scripting additions
    set startURL to current application’s NSURL’s fileURLWithPath:(tmpInit)
  end using terms from
  
  
set thePanel to current application’s NSSavePanel’s savePanel()
  
tell thePanel
    its setMessage:(tmpMsg)
    
its setAllowedFileTypes:(tmpExt)
    
its setNameFieldStringValue:(defName)
    
its setShowsHiddenFiles:false
    
its setTreatsFilePackagesAsDirectories:false
    
–its setDirectoryURL:startURL–指定しないほうが前回呼び出し時と同じフォルダが表示されて便利
    
set returnCode to its runModal() as integer
  end tell
  
  
if returnCode = (current application’s NSFileHandlingPanelOKButton) as integer then
    set my savePath to thePanel’s |URL|()’s |path|()
    
    
if (thePanel’s respondsToSelector:"tagNames") as boolean then
      set my saveTag to thePanel’s tagNames()
    end if
    
  else
    — cancel button
    
error -1
  end if
end showModalSave:

★Click Here to Open This Script 

Posted in dialog File path Sandbox | Tagged 10.13savvy 10.14savvy 10.15savvy NSSavePanel NSURL | Leave a comment

プログラムで作ったアラートダイアログ上のTextViewをフォーカスする

Posted on 4月 14, 2020 by Takaaki Naganoya

AppleScriptでダイナミックに(動的に)生成したアラートダイアログ上にテキストビューを生成し、そのテキストビュー上にテキスト入力用のキャレットを移動させる(フォーカスする)AppleScriptです。

Xcode上でAppleScriptでGUIアプリケーションを作っていて、文字サイズの大きなテキスト入力フィールドを動的に生成。さらに、テキスト入力フィールドにテキスト入力用のキャレットを移動させようとして、割と手こずっていました。

テキスト入力を受け付けるGUI部品に対して、入力フォーカスを与え、テキスト入力のキャレットが置かれた状態を再現するには、GUIの画面上からはマウスカーソルでクリック操作するとできます。同じ状態をプログラム側から作る場合にはFirstResponderにセットするという処理になります。

この、FirstResponderに指定することは理解でき、Xcode(Interface Builder)上で配置した部品に対して実行する処理は昔からやっています。NSTextFieldに強制的に入力フォーカスを置いた上でバーコードリーダーからのテキスト入力を受け付けるといった処理です。

一方、プログラムから動的に生成したGUI部品に対して、Cocoaが用意しているAPIのどれを用いて、どのように指定すべきなのかで困りました。

親WindowのFirstResponderにしてみたり、アラートウィンドウのFirstResponderにしてみたり、いろいろ試した末に、

parentWin's setInitialFirstResponder:aView

で、Alert DialogのWindowをparentWinに、alert dialog上のテキストビューをaViewに代入した状態で実行して、入力キャレットをテキストビュー上に移動できました。本Xcode Projectは上記の1行の記述の検証を行うためのテストプロジェクトです。

–> Download Xcode Project

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— dialogCarret
—
— Created by Takaaki Naganoya on 2020/04/14.
— Copyright © 2020 Takaaki Naganoya. All rights reserved.
—

script AppDelegate
  property parent : class "NSObject"
  
  
property |NSURL| : a reference to current application’s |NSURL|
  
property NSFont : a reference to current application’s NSFont
  
property NSView : a reference to current application’s NSView
  
property NSAlert : a reference to current application’s NSAlert
  
property NSColor : a reference to current application’s NSColor
  
property NSTextView : a reference to current application’s NSTextView
  
property NSScrollView : a reference to current application’s NSScrollView
  
property NSRunningApplication : a reference to current application’s NSRunningApplication
  
  
— IBOutlets
  
property theWindow : missing value
  
  
property returnCode : 0
  
property resStr : ""
  
  
  
on applicationWillFinishLaunching:aNotification
    —
  end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
on clicked:sender
    set tmpStr to "TEST"
    
set paramObj to {myMessage:"Input Text", mySubMessage:"", mes1:(tmpStr), mesWidth:400, mesHeight:200}
    
my performSelectorOnMainThread:"dispTextViewWithAlertdialog:" withObject:paramObj waitUntilDone:true
    
set aResText to (my resStr) as string
    
    
display dialog aResText
  end clicked:
  
  
on dispTextViewWithAlertdialog:paramObj
    –Receive Parameters
    
set aMainMes to (myMessage of paramObj) as string –Main Message
    
set aSubMes to (mySubMessage of paramObj) as string –Sub Message
    
set mesStr to (mes1 of paramObj) as string –Text Input field 1 Label
    
set aWidth to (mesWidth of paramObj) as integer –TextView width
    
set aHeight to (mesHeight of paramObj) as integer –TextView height
    
    
set vNum to system attribute "sys2"
    
if vNum > 13 then
      set apRes to retLightOrDark() of me
      
if apRes = true then
        set textColor to (current application’s NSColor’s cyanColor())
      else
        set textColor to (current application’s NSColor’s blackColor())
      end if
    else
      set textColor to (current application’s NSColor’s blackColor())
    end if
    
    
    
— Create a TextView with Scroll View
    
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
    
set aView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
    
aView’s setDelegate:me
    
aView’s setRichText:true
    
aView’s useAllLigatures:true
    
aView’s setTextColor:textColor
    
aView’s setFont:(current application’s NSFont’s systemFontOfSize:90.0)
    
set aColor to current application’s NSColor’s colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.1
    
    
aView’s setBackgroundColor:aColor
    
aView’s setString:mesStr
    
aScroll’s setDocumentView:aView
    
aView’s enclosingScrollView()’s setHasVerticalScroller:true
    
    
— set up alert
    
set theAlert to NSAlert’s alloc()’s init()
    
tell theAlert
      its setMessageText:aMainMes
      
its setInformativeText:aSubMes
      
its addButtonWithTitle:"OK"
      
–its addButtonWithTitle:"Cancel"
      
its setAccessoryView:aScroll
      
set parentWin to its |window|()
    end tell
    
    
parentWin’s setAlphaValue:0.8
    
–parentWin’s setOpaque:(false)
    
parentWin’s setLevel:(current application’s NSScreenSaverWindowLevel)
    
    
parentWin’s setInitialFirstResponder:aView —Place Input Carret to Alert Dialog
    
    
— show alert in modal loop
    
NSRunningApplication’s currentApplication()’s activateWithOptions:0
    
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
    
    
if (my returnCode as number) = 1001 then
      —
    else
      set my resStr to aView’s |string|()
    end if
  end dispTextViewWithAlertdialog:
  
  
  
on doModal:aParam
    set (my returnCode) to aParam’s runModal()
  end doModal:
  
  
  
on retLightOrDark()
    return true
  end retLightOrDark
  
end script

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode dialog How To | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSColor NSFont NSMakeRect NSRunningApplication NSScrollView NSTextView | Leave a comment

display dialogで押されたボタンをIDで返す(1はじまり)

Posted on 2月 23, 2020 by Takaaki Naganoya

If you can think of something that a language would normally have applescript does not have it

— mc²than (@parrotgeek1) February 23, 2020

できると思うんだけどなー、と思って30分ぐらいでできました。全部同じtitleのボタンを複数作って、押されたボタンを左側から1はじまりでIDで返すAppleScriptです。

できたところで誰が褒めてくれるわけでもありませんが、できるものをできないというのは、、、、。

そもそも、同じtitleのボタンを複数出すのはGUI的にナンセンスな上に、Xcode上でAppleScriptアプリケーション作れば普通に作れるわけで、、、、

AppleScript名:display dialogで押されたボタンをIDで返す(1はじまり).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/02/23
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSAlert : a reference to current application’s NSAlert
property NSRunningApplication : a reference to current application’s NSRunningApplication

property theResult : 0
property returnCode : 0
property theDataSource : {}

display dialog "TEST" buttons {"OK", "OK", "OK", "OK", "OK", "OK", "OK"}

on display dialog aString as string buttons buttonList as list
  set paramObj to {myMessage:aString, myButtonList:buttonList}
  
–my dispAlert:paramObj –for debug
  
my performSelectorOnMainThread:"dispAlert:" withObject:(paramObj) waitUntilDone:true
  
return {button returned:my theResult}
end display dialog

on dispAlert:paramObj
  set aMainMes to (myMessage of paramObj) as string
  
set bList to (myButtonList of paramObj) as list
  
set bLen to length of bList
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
    
repeat with i in bList
      (its addButtonWithTitle:i)
    end repeat
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
set aBID to bLen – ((my returnCode) – 1000 + 1) + 1
  
set my theResult to aBID
end dispAlert:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.14savvy 10.15savvy NSAlert NSRunningApplication | Leave a comment

Numbersで書類Aのすべてのシートの表1を、書類Bの現在のシートの表1にまとめる

Posted on 1月 21, 2020 by Takaaki Naganoya

Numbersでオープン中の複数の書類があったときに、書類Aのすべてのシートの表1を、書類Bの現在のシートの表1にまとめるAppleScriptです。

さすがに、機械的な作業すぎて手で行う気にはなれなかったので、ありもののScriptを利用して作ってみました。

こういう用途のために作っておいたライブラリ「choose multiple list」を利用しています。


▲データ取得元


▲データ集約先


▲ダイアログで「データ取得元」と「データ集約先」を選択

BridgePlus内蔵のFrameworkがmacOS 10.14/10.15に邪魔されて認識されない環境では動かせないかもしれません。このあたり、SIP解除するしかないと思わせるものがあります。

ライブラリで複雑な選択を行えるUI部品を作っておいたおかげで、これだけ込み入った動作を行うAppleScriptを書きましたが、Cocoaの機能はほぼ使っていません(getDataFromNumbersDocは作り置きしておいた、頻出ルーチンなので使いまわしています)。choose multiple listライブラリは、こういう用途のために作っておいたものであり、まさにそのぴったりな用途であったといえるでしょう。

AppleScript名:書類Aのすべてのシートの表1を、書類Bの現在のシートの表1にまとめる.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/20
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.7" — High Sierra (10.13) or later
use framework "Foundation"
use scripting additions
use bPlus : script "BridgePlus"
use mulList : script "choose multiple list"

script spd
  property allData : {}
end script

set (allData of spd) to {}

set {aName, bName} to getTargetDocNames() of me

–「データ取得元」のNumbers書類のすべてのシートの表1から2D Listでデータ取得
tell application "Numbers"
  tell document aName
    set sList to name of every sheet
    
repeat with i in sList
      set tmp2DDat to getDataFromNumbersDoc(aName, i) of me
      
set (allData of spd) to (allData of spd) & tmp2DDat
    end repeat
  end tell
end tell

set aHeight to length of (allData of spd)
set aWidth to length of item 1 of (allData of spd)

–「データ集約先」のNumbers書類の現在のシートの表1にまとめた2D Listを展開する(巨大すぎると時間がかかるので、CSV書き出ししてオープンするなどの別の方法を採るべき)
tell application "Numbers"
  tell document bName
    tell active sheet
      set tRes to make new table with properties {row count:aHeight + 1, column count:aWidth}
    end tell
  end tell
end tell

fillCurrentTable(bName, (allData of spd)) of me

–Numbersの書類の現在のシートを、指定の2次元配列でfillする
on fillCurrentTable(docName, aList)
  set aLen to length of aList
  
set aWidth to length of first item of aList
  
  
tell application "Numbers"
    tell document docName
      tell active sheet
        tell table 1
          repeat with i from 1 to aLen
            tell row (i + 1)
              set aRowList to contents of item i of aList
              
repeat with ii from 1 to aWidth
                tell cell ii
                  set aTmpData to contents of item ii of aRowList
                  
ignoring application responses
                    set value to aTmpData
                  end ignoring
                end tell
              end repeat
            end tell
          end repeat
        end tell
      end tell
    end tell
  end tell
end fillCurrentTable

–Numbersでオープン中の書類の名称一覧からデータ取得元とデータ集約先の書類名をダイアログで選択
on getTargetDocNames()
  tell application "Numbers"
    set nList to name of every document
  end tell
  
  
set selList to {nList, nList}
  
set tList to {"データ取得元", "データ集約先"}
  
  
set {aRes, bRes} to choose multiple list selList main message "各Numbers書類の役割を選択してください" sub message "「データ取得元」のデータを順次、「データ集約先」の表1に連結します" with title lists tList height 140 width 400 return type item contents without allow same items
  
return {aRes, bRes}
end getTargetDocNames

–Numbersでオープン中の書類の選択中のシートの表1からデータを取得して2D Listに
on getDataFromNumbersDoc(aDocName, sheetName)
  
  
load framework
  
  
tell application "Numbers"
    if (count (every document)) = 0 then return false
    
    
tell document aDocName
      if (count (every sheet)) = 0 then return false
      
      
tell sheet sheetName
        tell table 1
          set colCount to column count
          
set rowCount to row count
          
set headerCount to header row count
          
set footerCount to footer row count
          
          
set dList to value of every cell of cell range
        end tell
      end tell
      
    end tell
  end tell
  
  
–Convert 1D List to 2D List
  
set bList to (current application’s SMSForder’s subarraysFrom:dList groupedBy:colCount |error|:(missing value)) as list
  
set sItem to 1 + headerCount
  
set eItem to rowCount – footerCount
  
set cList to items sItem thru eItem of bList
  
  
return cList
  
end getDataFromNumbersDoc

★Click Here to Open This Script 

Posted in dialog GUI list | Tagged 10.13savvy 10.14savvy 10.15savvy Numbers | Leave a comment

display drop dialog Script Library

Posted on 1月 16, 2020 by Takaaki Naganoya

指定UTIのファイルのFinderからのドラッグ&ドロップを受け付けるAppleScript Libraries「display drop dialog」をリリース、フリー配布いたします。

# 本ライブラリはEdama2さんのScriptに若干の改変を行い、sdefをつけてパラメータのエラー処理を行い、コードサインしてライブラリ化したものです

–> Download display drop dialog Script Library (To ~/Library/Script Libraries/)

本ライブラリは、指定UTIの書類のドラッグ&ドロップを受け付けるダイアログをAppleScriptから手軽に使えるようにするものです。macOS 10.14以降で動作します。

現在のAppleのTim Cook体制は、セキュリティ強化の美名のもとに「動かないコンピュータ」を目指し、各種機能を阻害する方向に進んでいます。

AppleScriptの世界はその中でも自由度が比較的高い状態にありますが、それでもmacOS 10.12以降では、AppleScriptドロップレットにFinderからドラッグ&ドロップしても、Finderの拡張属性(Xattr)がついたファイルは無視されたりと、なかなか困ります。

そこで、Finderからのドラッグ&ドロップ操作を通常の(Applet書き出ししていない)AppleScript、とくにScript Menuから呼び出す形式のAppleScriptで使えるようにすることを目的として本ライブラリを作成しました。

本ライブラリを用いることで、ドロップレットを作らなくても、Script Menuから呼び出した普通のAppleScriptにファイルのドラッグ&ドロップの受信機能を追加できます。セキュリティと機能性(ドラッグ&ドロップ)を両立させるため、すべてのScripterに欠かすことのできないライブラリになるものと期待しています。

本ライブラリのAppleScript用語辞書には実行イメージ(画面キャプチャ)およびサンプルScriptを入れてあり、実行時の様子やサンプルスクリプト(本Blogと同様のワンクリックで内容が転送されるリンクつき)を確認できるようになっています。

AppleScript名:accept AppleScript documents
use dropLib : script "display drop dialog"

set aMainMes to "Drop AppleScript"
set aSubMes to "Drag and Drop AppleScript files here"
set aUTI to "com.apple.applescript.script"
set execButtonTitle to "Execute"
set aRes to (display drop dialog aUTI main message aMainMes sub message aSubMes with initial folder "" OK button title execButtonTitle)
–> {alias "Machintosh HD:Users:me:Documents:display drop dialog:accept PNG images.scpt", alias "Machintosh HD:Users: me:Documentsaccept Markdown documents.scpt"}

★Click Here to Open This Script 

AppleScript名:accept Markdown documents
use dropLib : script "display drop dialog"

set aMainMes to "Drop Markdown Documents"
set aSubMes to "Drag and Drop Markdown documents here"
set aUTI to "net.daringfireball.markdown"
set execButtonTitle to "Execute"
set aRes to (display drop dialog aUTI main message aMainMes sub message aSubMes with initial folder "" OK button title execButtonTitle)
–> {alias "Machintosh HD:Users:me:Documents::–Book 11 「Blog Archives vol5 2013-2014」:2013:01:201301013.md", alias "Machintosh HD:Users:me:Documents::–Book 11 「Blog Archives vol5 2013-2014」:2013:01:201301012.md"}

★Click Here to Open This Script 

AppleScript名:accept PNG images with initial folder contents
use scripting additions
use dropLib : script "display drop dialog"

set aMainMes to "Drop PNG"
set aSubMes to "Drag and Drop png images here"
set aUTI to "public.png"
set execButtonTitle to "Execute"
set iFolder to path to pictures folder –At first, PNG files in this folder is displayed
set aRes to (display drop dialog aUTI main message aMainMes sub message aSubMes with initial folder iFolder OK button title execButtonTitle)
–> {alias "Machintosh HD:Users:me:Pictures:1015sedhelp2.png", alias "Machintosh HD:Users:me:Pictures:574G01.png", alias "Machintosh HD:Users:me:Pictures:macDown1.png", alias "Machintosh HD:Users:me:Pictures: 2017-09-27 19.33.53.png", alias "Machintosh HD:Users:me:Pictures:2018-11-01 10.54.06.png"

★Click Here to Open This Script 

Posted in dialog GUI PRODUCTS Script Libraries sdef | Tagged 10.14savvy 10.15savvy | Leave a comment

アラートダイアログ上にTable Viewを表示 v9_アイコンを表示、Finderからのドラッグ&ドロップ

Posted on 1月 15, 2020 by Takaaki Naganoya

Edama2さんからの投稿Scriptです。アラートダイアログ中に各種GUI部品を詰め込む「箱庭インタフェース」シリーズ。テーブルビューを表示して、Finderからのアプリケーションのドラッグ&ドロップを受け付けるAppleScriptです。

–> Download Editable & Executable Script Bundle Document

カスタムクラスの作り方がわかったので、懲りずにTable Viewネタです。
Thanks  Shane Stanley!

アプリケーションフォルダ直下のアプリを、NSValueTransformerを使ってコラムにローカライズ名とアイコンを表示するのと、finderからのドラッグ&ドロップでアイテムの追加です。
追加時に重複チェックをしているので試す時はユーティリティフォルダか別の場所にあるアプリで試してください。

本Scriptは自分も作りたいと思いつつも調査が進んでいなかったものですが、TableViewでFinderからのドラッグ&ドロップを受け付けます。

これを鍛えて再利用しやすい部品に育てることで、ドロップレットの代替品にできると思っています。macOS 10.12以降、Finder上のファイルをAppleScriptドロップレットにドラッグ&ドロップで処理させても、OS側がダウンロードファイルにつけた拡張属性(Xattr)「com.apple.quarantine」がついているとドロップレット側で処理できなかったりします(回避方法はありますけれども)。

このドロップレットという仕組みを使わずに、同様にドラッグ&ドロップによる処理を手軽にできる代替インタフェースとしてこのようなものを考えていた次第です。自分で作らなくてもEdama2さんが作ってくださったので助かりました、、、、

AppleScript名:アラートダイアログ上にTable Viewを表示 v9_アイコンを表示、Finderからのドラッグ&ドロップ
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppleScriptObjC"
use scripting additions

on run
  my main()
end run
on main()
  # Script Bundle内のResourcesフォルダを求める
  
set resourcePath to POSIX path of (path to me) & "Contents/Resources/"
  
set theBundle to current application’s NSBundle’s bundleWithPath:resourcePath
  
theBundle’s loadAppleScriptObjectiveCScripts()
  
  
#
  
set aFolder to path to applications folder
  
set aPath to aFolder’s POSIX path
  
set aURL to current application’s NSURL’s fileURLWithPath:aPath
  
  
# 指定フォルダの直下のファイルを取得
  
set filePaths to current application’s NSFileManager’s defaultManager’s ¬
    contentsOfDirectoryAtURL:aURL ¬
      includingPropertiesForKeys:{current application’s NSURLNameKey} ¬
      
options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) ¬
      
|error|:(missing value)
  
  
set filePaths to filePaths as list
  
set thisFileType to "com.apple.application-bundle"
  
  
set fileURLs to {}
  
repeat with anItem in filePaths
    
    
set aPath to anItem’s contents’s POSIX path
    
set aURL to (current application’s NSURL’s fileURLWithPath:aPath)
    
    
set {aResult, aUTI, aError} to (aURL’s getResourceValue:(reference) ¬
      forKey:(current application’s NSURLTypeIdentifierKey) ¬
      
|error|:(reference))
    
    
if (aUTI as text) is thisFileType then
      set fileURLs’s end to aURL
    end if
  end repeat
  
  
set optionRec to {fileURLs:fileURLs, fileType:thisFileType}
  
  
set aMainMes to "アプリケーションの選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
set dateObj to my chooseItemByTableView(aMainMes, aSubMes, optionRec)
end main

# アラートダイアログでtableviewを表示
on chooseItemByTableView(aMainMes, aSubMes, optionRec)
  ### set up view
  
set {theView, makeObj} to my makeContentView(optionRec)
  
  
set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, setView:theView, myOption:makeObj}
  
my performSelectorOnMainThread:"raizeAlert:" withObject:paramObj waitUntilDone:true
  
  
if (my _retrieve_data) is missing value then error number -128
  
return (my _retrieve_data)
end chooseItemByTableView

## retrieve date
on raizeAlert:paramObj
  set mesText to paramObj’s myMessage
  
set infoText to paramObj’s mySubMessage
  
set theView to paramObj’s setView
  
set aTableObj to paramObj’s myOption
  
  
global _retrieve_data
  
set _retrieve_data to missing value
  
  
### set up alert
  
tell current application’s NSAlert’s new()
    setMessageText_(mesText)
    
setInformativeText_(infoText)
    
addButtonWithTitle_("OK")
    
addButtonWithTitle_("Cancel")
    
setAccessoryView_(theView)
    
tell |window|()
      setInitialFirstResponder_(theView)
    end tell
    
#### show alert in modal loop
    
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
  end tell
  
  
### retrieve date
  
set aIndexSet to aTableObj’s selectedRowIndexes()
  
  
set chooseItems to ((aTableObj’s dataSource())’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list
  
set _retrieve_data to {}
  
repeat with anItem in chooseItems
    set _retrieve_data’s end to anItem’s fileURL
  end repeat
end raizeAlert:

## ContentView を作成
on makeContentView(paramObj)
  ## 準備
  
set fileURLs to (paramObj’s fileURLs) as list
  
set thisFileType to (paramObj’s fileType) as text
  
  
set keyrec to {column1:"Name"}
  
set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyrec)
  
  
set theDataSource to current application’s YKZArrayController’s new()
  
  
## NSTableView
  
tell current application’s NSTableView’s alloc()
    tell initWithFrame_(current application’s CGRectZero)
      setAllowsEmptySelection_(false)
      
setAllowsMultipleSelection_(true)
      
setDataSource_(theDataSource)
      
setDelegate_(theDataSource)
      
setDoubleAction_("doubleAction:")
      
setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask)
      
setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular)
      
setTarget_(theDataSource)
      
setUsesAlternatingRowBackgroundColors_(true)
      
      
set thisRowHeight to rowHeight() as integer
      
set theDataSource’s _table_view to it
    end tell
  end tell
  
  
# data sourceに追加
  
tell (theDataSource)
    awakeFromNib()
    
set its _file_type to thisFileType
    
    
repeat with aURL in fileURLs
      addObject_({fileURL:aURL})
    end repeat
    
    
setSelectionIndex_(0)
  end tell
  
  
## NSTableColumn
  
### Columnの並び順を指定する
  
set viewWidth to 320
  
set columnsCount to keyDict’s |count|()
  
repeat with colNum from 1 to columnsCount
    
    
set keyName to "column" & colNum as text
    
set aTitle to (keyDict’s objectForKey:keyName)
    
    
tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text))
      tell headerCell()
        setStringValue_(aTitle)
        
set thisHeaderHeight to cellSize()’s height
      end tell
      
      
### Columnの横幅を指定
      
setWidth_(viewWidth)
      
      
### バインディングのオプション
      
— ソートを無効にする
      
set anObj to (current application’s NSNumber’s numberWithBool:false)
      
set aKey to current application’s NSCreatesSortDescriptorBindingOption
      
set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey)
      
      
### 表示内容をNSArrayControllerにバインディング
      
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, theDataSource, "arrangedObjects", bOptions)
      
      (
theDataSource’s _table_view’s addTableColumn:it)
    end tell
    
  end repeat
  
  
## NSScrollView
  
### Viewの高さを計算
  
set rowCount to 12 –> 行数を固定
  
set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない
  
set vSize to current application’s NSMakeRect(0, 0, viewWidth, viewHeight)
  
  
### Viewを作成
  
tell current application’s NSScrollView’s alloc()
    tell initWithFrame_(vSize)
      setBorderType_(current application’s NSBezelBorder)
      
setDocumentView_(theDataSource’s _table_view)
      
–setHasHorizontalScroller_(true)
      
setHasVerticalScroller_(true)
      
set aScroll to it
    end tell
  end tell
  
  
return {aScroll, theDataSource’s _table_view}
end makeContentView

★Click Here to Open This Script 

AppleScript名:subClasses
#MARK: – NSValueTransformer
script YKZURLToIcon
  property parent : class "NSValueTransformer"
  
property allowsReverseTransformation : false –>逆変換
  
property transformedValueClass : a reference to current application’s NSImage –>クラス
  
  
#変換処理
  
on transformedValue:fileURL
    if fileURL is missing value then return
    
    
set appPath to fileURL’s |path|()
    
set iconImage to current application’s NSWorkspace’s sharedWorkspace’s iconForFile:appPath
    
return iconImage
  end transformedValue:
end script

script YKZURLToDisplayedName
  property parent : class "NSValueTransformer"
  
property allowsReverseTransformation : false –>逆変換
  
property transformedValueClass : a reference to current application’s NSString –>クラス
  
  
#変換処理
  
on transformedValue:fileURL
    if fileURL is missing value then return
    
    
set appPath to fileURL’s |path|()
    
set displayedName to current application’s NSFileManager’s defaultManager’s displayNameAtPath:appPath
    
return displayedName
  end transformedValue:
end script

#MARK: – Array Controller
script YKZArrayController
  property parent : class "NSArrayController"
  
  
#MARK: IBOutlets
  
property _table_view : missing value
  
#MARK:
  
property _data_type : do shell script "uuidgen"
  
property _file_type : ""
  
  
on awakeFromNib()
    –log "YKZArrayController – awakeFromNib"
    
tell _table_view
      registerForDraggedTypes_({current application’s NSFilenamesPboardType, _data_type})
      
setDraggingSourceOperationMask_forLocal_(current application’s NSDragOperationCopy, false)
    end tell
    
    
#Transformerの登録
    
set tNames to {}
    
set tNames’s end to "YKZURLToIcon"
    
set tNames’s end to "YKZURLToDisplayedName"
    
repeat with aTransformer in tNames
      set theTransformer to current application’s class aTransformer’s new()
      (
current application’s NSValueTransformer’s setValueTransformer:theTransformer forName:aTransformer)
    end repeat
  end awakeFromNib
  
  
#MARK: Data Source Overrides
  
on numberOfRowsInTableView:aTableView
    return my content()’s |count|()
  end numberOfRowsInTableView:
  
  
on tableView:aTableView viewForTableColumn:aColumn row:aRow
    –log "viewForTableColumn"
    
    
set aCellView to aTableView’s makeViewWithIdentifier:"YKZTableCellView" owner:me
    
–set isNull to aCellView’s isEqual:(current application’s NSNull’s |null|())
    
–log isNull as text
    
    
if aCellView is missing value then
      
      
set frameRect to current application’s NSMakeRect(0, 0, aColumn’s width, aTableView’s rowHeight())
      
set aCellView to current application’s YKZTableCellView’s alloc’s initWithFrame:frameRect
      
      
set anObj to "YKZURLToIcon"
      
set aKey to current application’s NSValueTransformerNameBindingOption
      
set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey)
      (
aCellView’s imageView())’s bind:(current application’s NSValueBinding) toObject:aCellView withKeyPath:"objectValue.fileURL" options:bOptions
      
      
set anObj to "YKZURLToDisplayedName"
      
set aKey to current application’s NSValueTransformerNameBindingOption
      
set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey)
      (
aCellView’s textField())’s bind:(current application’s NSValueBinding) toObject:aCellView withKeyPath:"objectValue.fileURL" options:bOptions
      
    end if
    
    
return aCellView
  end tableView:viewForTableColumn:row:
  
  
# テーブル内のセルが編集できるか
  
on tableView:aTableView shouldEditTableColumn:aColumn row:aRow
    return false
  end tableView:shouldEditTableColumn:row:
  
  
# テーブル内をダブルクリックしたらOKボタンを押す
  
on doubleAction:sender
    log "doubleAction"
    
## ヘッダをクリックした時は何もしない
    
if (sender’s clickedRow()) is -1 then return
    
    
set theEvent to current application’s NSEvent’s ¬
      keyEventWithType:(current application’s NSEventTypeKeyDown) ¬
        location:(current application’s NSZeroPoint) ¬
        
modifierFlags:0 ¬
        
timestamp:0.0 ¬
        
windowNumber:(sender’s |window|()’s windowNumber()) ¬
        
context:(current application’s NSGraphicsContext’s currentContext()) ¬
        
|characters|:return ¬
        
charactersIgnoringModifiers:(missing value) ¬
        
isARepeat:false ¬
        
keyCode:0
    current application’s NSApp’s postEvent:theEvent atStart:(not false)
  end doubleAction:
  
  
#MARK: Drag Operation Method
  
#ドラッグを開始(ペーストボードに書き込む)
  
on tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard
    –log "writeRowsWithIndexes"
    
set aData to current application’s NSKeyedArchiver’s archivedDataWithRootObject:rowIndexes
    
pboard’s declareTypes:{_data_type} owner:(missing value)
    
pboard’s setData:aData forType:_data_type
    
return true
  end tableView:writeRowsWithIndexes:toPasteboard:
  
#ドラッグ途中
  
on tableView:aTableView validateDrop:info proposedRow:row proposedDropOperation:operation
    –log "validateDrop"
    
#列の間にドラッグ
    
if (operation is current application’s NSTableViewDropAbove) then
      return current application’s NSDragOperationMove
    end if
    
#項目の上にドラッグ
    
set aTypes to info’s draggingPasteboard’s types()
    
if (aTypes’s containsObject:_data_type) as boolean then
      return current application’s NSDragOperationNone
    end if
    
return current application’s NSDragOperationNone
  end tableView:validateDrop:proposedRow:proposedDropOperation:
  
#ドラッグ終了
  
on tableView:aTableView acceptDrop:info row:row dropOperation:operation
    –log "acceptDrop"
    
#
    
set pboard to info’s draggingPasteboard()
    
set aTypes to pboard’s types()
    
    
if (aTypes’s containsObject:_data_type) as boolean then
      
      
set rowData to pboard’s dataForType:_data_type
      
set rowIndexes to current application’s NSKeyedUnarchiver’s unarchiveObjectWithData:rowData
      
      
if (rowIndexes’s firstIndex()) < row then
        set row to row – (rowIndexes’s |count|())
      end if
      
      
set aRange to current application’s NSMakeRange(row, rowIndexes’s |count|())
      
set aIndexSet to current application’s NSIndexSet’s indexSetWithIndexesInRange:aRange
      
      
set anObj to ((my content())’s objectsAtIndexes:rowIndexes)
      
my removeObjects:anObj
      
my insertObjects:anObj atArrangedObjectIndexes:aIndexSet
      
my rearrangeObjects()
      
    else
      set pathList to pboard’s propertyListForType:(current application’s NSFilenamesPboardType)
      
–log result as list
      
repeat with aPath in pathList
        set isAdd to addItem_({filePath:aPath, atIndex:row})
        
if isAdd then beep
      end repeat
      
    end if
    
return true
  end tableView:acceptDrop:row:dropOperation:
  
  
#追加する
  
on addItem:sender –> {filePath:aPath, atIndex:aIndex}
    –log "addItem:"
    
set isAdd to false
    
    
#missing valueの時は最後に追加
    
set {filePath:aPath, atIndex:aIndex} to sender
    
if aIndex is missing value then
      set aIndex to my content()’s |count|() as number
    end if
    
    
#URLに変換
    
set appURL to (current application’s NSURL’s fileURLWithPath:aPath)
    
#アプリケーション以外は追加しない
    
set {aResult, aUTI, aError} to (appURL’s getResourceValue:(reference) ¬
      forKey:(current application’s NSURLTypeIdentifierKey) ¬
      
|error|:(reference))
    log aUTI
    
if (aUTI as text) is not (my _file_type as text) then return isAdd
    
    
#すでにあるか確認
    
set bList to my content() as list
    
repeat with aRecord in bList
      if ((appURL’s isEqual:(aRecord’s fileURL)) as boolean) then
        set isAdd to true
        
exit repeat
      end if
    end repeat
    
    
#なければ追加
    
if not isAdd then
      set anObj to {fileURL:appURL}
      (
my insertObject:anObj atArrangedObjectIndex:aIndex)
    end if
    
return isAdd
  end addItem:
end script

#MARK: – NSTableCellView
script YKZTableCellView
  property parent : class "NSTableCellView"
  
  
on initWithFrame:rect
    –log "initWithFrame"
    
continue initWithFrame:rect
    
    
setAutoresizingMask_(current application’s NSViewWidthSizable)
    
    
tell current application’s NSImageView’s alloc
      tell initWithFrame_(current application’s NSMakeRect(0, 0, 16, 16))
        setImageScaling_(current application’s NSImageScaleProportionallyUpOrDown)
        
setImageAlignment_(current application’s NSImageAlignCenter)
        
        
my setImageView:it
        
my addSubview:it
      end tell
    end tell
    
    
tell current application’s NSTextField’s alloc
      tell initWithFrame_(current application’s NSMakeRect(20, 2, 200, 14))
        setBordered_(false)
        
setDrawsBackground_(false)
        
setEditable_(false)
        
        
my setTextField:it
        
my addSubview:it
      end tell
    end tell
    
    
return me
  end initWithFrame:
end script

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSArrayController NSBundle NSCreatesSortDescriptorBindingOption NSDictionary NSNumber NSScrollView NSTableCellView NSTableView NSURL NSValueBinding NSValueTransformer | Leave a comment

display text fields Library v1.3

Posted on 12月 31, 2019 by Takaaki Naganoya

「display text fields」AppleScriptライブラリをv1.3にアップデートしました。

–> Download display text fields_v13 (To ~/Library/Script Libraries/)

v1.3ではテキストフィールドの最大横幅の計算を修正し、フィールド数が増えたときにダイアログ上から項目がはみ出さないようにScroll Viewをつけるようにしました。

本来目的としていた用途に使ってみたらイマイチだった点を修正した格好です。AppleScriptで取得した各種アプリケーションのオブジェクトのプロパティ情報をダイアログ上で一覧表示して確認するというのが、自分がこのライブラリを作った目的です。

以下は、サンプルスクリプト「文字列で指定したAppleScriptの実行結果をテキストで取得する v2」についての説明です。

割とえげつない処理をしていますが、作りためておいたルーチンを引っ張り出してきただけなので、書くのにさほど時間はかけていません。

こうしたAppleScriptのプロパティ値をparseするには、スクリプトエディタの結果欄を文字列として取得するか(GUI Scripting経由でやったことがあります)、こうしてメモリ上にスクリプトエディタ+結果表示用のビューを生成してメモリ上でAppleScriptを実行して結果をテキストで取得するということになると思います。前者だとGUI Scriptingの実行権限が必要になるため、Cocoaの機能を利用したほうが手軽という状況です。

(途中で入れ替えた)v2では、macOS 10.15対応、ランタイム環境によってはうまく動かない「as anything」の使用をやめるなどの変更を加えました。

AppleScript名:文字列で指定したAppleScriptの実行結果をテキストで取得する v2.scpt
— Created 2016-01-08 by Takaaki Naganoya
— 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "OSAKit"
use framework "AppKit"
use tfLib : script "display text fields"

property OSAScript : a reference to current application’s OSAScript
property NSTextView : a reference to current application’s NSTextView
property OSAScriptView : a reference to current application’s OSAScriptView
property OSAScriptController : a reference to current application’s OSAScriptController

property myRes : ""

–OSのメジャーバージョンを数値で取得
set osVer to system attribute "sys2"

if osVer ≥ 15 then
  set srcStr to "tell application \"Music\"
  set aSel to first item of selection
  set aRes to (properties of aSel)
end tell"
else
  set srcStr to "tell application \"iTunes\"
  set aSel to first item of selection
  set aRes to (properties of aSel)
end tell"
end if

my performSelectorOnMainThread:"getResultStringFromScript:" withObject:(srcStr) waitUntilDone:true

set aRes to getListFromText(myRes) of me

set aList to {}
set bList to {}
set aLen to length of aRes

repeat with i from 1 to aLen
  set {aCon, bCon} to contents of item i of aRes
  
set the end of aList to aCon
  
set the end of bList to bCon
end repeat

confirm text fields main message "Track Info" sub message "Properties about selected track" key list aList value list bList

–Get AppleScript’s Result as string
on getResultStringFromScript:paramObj
  set srcStr to paramObj as string
  
set myRes to ""
  
  
set targX to 500 –View Width
  
set targY to 200 –View Height
  
  
set osaCon to OSAScriptController’s alloc()’s init()
  
set osaView to OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
  
set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
resView’s setRichText:true
  
resView’s useAllLigatures:true
  
  
osaCon’s setScriptView:osaView
  
osaCon’s setResultView:resView
  
  
osaView’s setString:srcStr
  
osaCon’s runScript:(missing value)
  
  
set myRes to resView’s |string|() as string
end getResultStringFromScript:

–スクリプトエディタのresult欄に返ってきたテキストをリストに変える
on getListFromText(aText)
  
  
script getListFromTextO
    property aaText : ""
    
property gList : {}
    
property outList : {}
    
property aG : ""
    
property valList : {}
  end script
  
  
copy aText to (aaText of getListFromTextO)
  
  
set (gList of getListFromTextO) to {}
  
set (outList of getListFromTextO) to {}
  
set (aG of getListFromTextO) to ""
  
set (valList of getListFromTextO) to {}
  
  
if (aaText of getListFromTextO) does not start with "{" and (aaText of getListFromTextO) does not end with "}" then
    return {}
  end if
  
  
set aLen to length of (aaText of getListFromTextO)
  
set (aG of getListFromTextO) to text 2 thru -2 of (aaText of getListFromTextO)
  
set (gList of getListFromTextO) to characters of (aG of getListFromTextO)
  
  
  
set sPos to 2 –1文字目は\"{\"なので2文字目からスキャンを開始する
  
set ePos to 2
  
  
set imdF to false –Immediate Data Flag(文字列中を示すダブルクォート内の場合にはtrueになる)
  
set listF to 0 –stacking段数が入る
  
  
set attrF to true –属性ラベルスキャン時にtrue、データ末尾スキャン時にfalse
  
  
  
repeat with i in (gList of getListFromTextO)
    
    
set j to contents of i
    
    
if attrF = true and imdF = false and listF = 0 then
      
      
–属性値部分の末尾検出
      
if j = ":" then
        if text sPos thru sPos of (aaText of getListFromTextO) = " " then
          set sPos to sPos + 1
        end if
        
set anOut to text sPos thru ePos of (aaText of getListFromTextO)
        
set sPos to ePos + 1
        
set the end of (valList of getListFromTextO) to anOut
        
set attrF to false –データのスキャンを開始する
        
set imdF to false
        
set listF to 0
      end if
      
    else if imdF = false and listF = 0 and j = "," then
      
      
–データ部分の末尾検出
      
set anOut to text sPos thru (ePos – 1) of (aaText of getListFromTextO)
      
set sPos to ePos + 1
      
set the end of (valList of getListFromTextO) to anOut
      
set the end of (outList of getListFromTextO) to (valList of getListFromTextO)
      
set (valList of getListFromTextO) to {}
      
      
set attrF to true –次が属性値ラベルであることを宣言
      
set imdF to false
      
set listF to 0
      
    else if j = "{" then
      if imdF = false then
        set listF to listF + 1 –1段スタックにpush
      end if
    else if j = "}" then
      if imdF = false then
        set listF to listF – 1 –1段スタックからpop
      end if
    else if j = "\"" then
      if imdF = true then
        set imdF to false
      else
        set imdF to true
      end if
    end if
    
    
set ePos to ePos + 1
    
  end repeat
  
  
–ラストのデータ部分を出力
  
try
    set the end of (valList of getListFromTextO) to text sPos thru (ePos – 1) of (aaText of getListFromTextO)
    
set the end of (outList of getListFromTextO) to (valList of getListFromTextO)
  on error
    false
  end try
  
  
return contents of (outList of getListFromTextO)
  
end getListFromText

★Click Here to Open This Script 

Posted in dialog GUI OSA | Tagged 10.13savvy 10.14savvy 10.15savvy iTunes NSTextView OSAScript OSAScriptController OSAScriptView | Leave a comment

アラートダイアログをタイトル付き表示

Posted on 12月 27, 2019 by Takaaki Naganoya

アラートダイアログで、ウィンドウタイトルを表示させるAppleScriptです。

AppleScriptには登場当初からダイアログの表示命令「display dialog」が備わっていました。久しぶりにClassic MacOSのdisplay dialogコマンドの表示を見ると、あまりの素朴さに驚かされます。

display dialogは手軽であるもののモーダルなダイアログなので、その後のMac OS Xへの移行時にはいまひとつ合わない感じがしていました。完全にMac OS Xの流儀で書き直されたのが、「display alert」ダイアログです。こちらもモーダル表示ですが、見た目は少しMac OS Xらしく変更されました。

display alertは、さまざまなオプションを指定できる一方で手軽さがいまひとつで、やはりdisplay dialog命令のほうが圧倒的に利用されてきたという経緯があります(自分もほとんどdisplay alertは使いません)。

# display alertダイアログに表示されるボタンはTouchBarに表示されますが、display dialogダイアログ上のボタンはTouchBarには表示されません

非モーダル表示なUIについては、display notificationで通知表示が使えるようになり、これで一応の解決を見たというところでしょうか。こういう動作をする通知ダイアログは、Newt On Projectの中で「どうしても欲しい」という要望が出て、2000年代初頭にすでに仲間内で作ってもらったものがあり、AppleScriptから半透明のダイアログ(画面右側からスライドアニメーション表示)を表示させていました。

その一方で、display youtubeとかdisplay tableといった、ダイアログに各種GUI部品を詰め込んで簡易表示させるインタフェース「箱庭インタフェース」が今年局所的なブームを起こし、地図は表示させるわテーブルは表示させるわ、世界地図を表示して国選択を行わせるわで「計算結果を手軽に表示できたり、ファイルパスをわかりやすく選択できて便利」という評価をいただいています。

で、この「箱庭インタフェース」はもれなくalert dialogのインタフェースを用いています。display alertで表示されるものと同じGUI部品ですね。


▲HIGにサイコーに反していろいろいじくりまくっているpickup colorダイアログ。アイコン部分を色プレビュー領域に。でも、強烈に使いやすい

ちょっとした結果表示や項目選択のためにアラートダイアログ上にGUI部品を並べるやりかたは、「アリ」だと思っています。

そんな中、知り合い筋から「alertダイアログにタイトルは出せないのか?」と問い合わせがありました。アラートダイアログでタイトル表示が可能なことは知っていましたが、

Human Interface Guidelineに合わない仕様なので、タイトル表示は使ってきませんでした。

ただ、「箱庭インタフェース」シリーズでここまで無茶苦茶をやっておいて、いまさら他人に「HIGに反する」とかいっても説得力がまったくありません。

HIGのガイドラインに反するといっても、実際に使い勝手がよくなったり、顧客や上司からの無茶振りをかわすためであればアラートダイアログにタイトルを追加するというのも、アリなのかもしれません。自分はやりませんけれども。

AppleScript名:アラートダイアログをタイトル付き表示
— Created 2019-12-26 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSRunningApplication : a reference to current application’s NSRunningApplication

property returnCode : 0

set paramObj to {myMessage:"アラート表示", mySubMessage:"アラートダイアログを表示します", myTitle:"たいとる?"}
set dRes to displayAlert(paramObj) of me

on displayAlert(paramObj)
  my performSelectorOnMainThread:"displayAlertDialog:" withObject:(paramObj) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
return true
end displayAlert

on displayAlertDialog:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTitle to myTitle of paramObj
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
set myWin to its |window|
  end tell
  
  
myWin’s setTitle:aTitle
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
return (aScrollWithTable’s documentView’s selectedRow()) + 1
end displayAlertDialog:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSAlert NSRunningApplication | 2 Comments

アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択

Posted on 12月 25, 2019 by Takaaki Naganoya

アラートダイアログ上にTable Viewを表示するGUI部品を提供するAppleScriptです。Edama2さんから投稿していただいたものです。

箱庭テーブルビューシリーズは、macOS標準装備の「choose from list」コマンドでは選択肢が増えたときに使いにくいので、その代替品としてTable Viewを用いたポップアップしない単項目の選択UIを実装したものでした。


▲最初のバージョン。みなぎる使い捨て感

それが途中から「Myriad Tablesが使えない場面(Framework入りの高機能ライブラリのため、利用できないケースが発生)に使う使い捨て部品」に進化。

コードサイズの投げやりなまでの小ささと、いつもどおりのプレーンな読み味から改造のためのベースキットとして活用されまくり、派生品や亜種が内外でいろいろ発生しています(きっと知らないところで改変版が作られているはず)。

sdefをつけてライブラリ化したり、色付きダイアログなどいろいろキワモノが派生しましたが、実用性を考慮して半透明ウィンドウではなく通常ウィンドウとして表示する方向に変わりつつあります。

Version Author Date Description
v1 Piyomaru 2019/2/14 For choose from list without popup menu. One field selection UI.
v2 Piyomaru 2019/2/14
v3 Piyomaru 2019/2/14
v4 Piyomaru 2019/7/11 Table user interface for item selection
v5_e KniazidisR 2019/11/2 Separate display dialog stage into making and displaying
v5_m Piyomaru 2019/12/23 Filtering by multiple field
v6 Piyomaru 2019/12/24 Search Field Added
v7 Edama2 2019/12/240 Vertical & horizontal resizing
v8 Edama2 2019/12/25 Record sorting & Field re-ordering

以下、v8についてのedama2さんからの説明文です。

コラムのヘッダのクリックでテーブルのソート
ドラッグ&ドロップによる行の並び替え
行の複数選択

ができるようになりましたが、ヘッダのソート状態のときにD&Dの並び替えはできません。
飛び飛びに複数行選択しても並び替えできますが、たまにズレている気がします...(汗)

ArrayControllerを使用したのが機能過剰と思われたかもしれませんが、まずXCode上で一度組んだものを逆移植しているのでそんなに深い意味はないです。
でもソート方法を試行錯誤して調べていたら、Cocoa bindingをコーディングから設定できることがわかった(よく考えれば当たり前ですが)のが、何か壁を一枚破った気がしました。

Cocoa bindingがプログラムから動的に実行できることは、調査して知っていましたが、実際にAppleScriptのコードとして記述されたものは見たことがありませんでした(Mac Scripterのフォーラムを漁ると出てくるのかも?)。

### 表示内容をNSArrayControllerにバインディング
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value)

ここの部分の記述は、クリスマスプレゼントとして楽しませていただきます。Cocoa Bindingなんて、Xcode上でGUI部品とイベント受信部分のハンドラとの接続をただ漫然とヒモでつなぐ作業をしていただけだったので、Cocoa Binding Referenceなんてはじめて細部にわたって眺めましたわー。


▲起動状態。フィールドの横サイズそろえ、レコード数により表UIの縦サイズが調整されている


▲Specsフィールドを中央に持ってきた。ちゃんとUIが細かいアニメーション動作を行う


▲Priceフィールドを先頭に移動させてみた


▲Priceでソートさせてみた


▲Specsでソートさせてみた


▲選択行をドラッグ&ドロップで移動。複数行も移動可能。ただし、フィールド指定のソート状態を有効にすると行移動が効かないという


▲Commandキーを押しながら複数行選択


▲ドラッグ&ドロップで移動


▲移動後

AppleScript名:アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択.scpt
on run
  
  
set aTableList to {}
  
set aTableList’s end to {|Name|:"MacBook Air", Price:"119,800円", Specs:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}
  
set aTableList’s end to {|Name|:"MacBook Pro 13", Price:"139,800円", Specs:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}
  
set aTableList’s end to {|Name|:"MacBook Pro 15", Price:"258,800円", Specs:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"Mac mini", Price:"122,800円", Specs:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"iMac 21.5", Price:"120,800円", Specs:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}
  
set aTableList’s end to {|Name|:"iMac 4K 21.5", Price:"142,800円", Specs:"3.6GHzクアッドコアIntel Core i3"}
  
set aTableList’s end to {|Name|:"iMac 5K 27", Price:"198,800円", Specs:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}
  
set optionRec to {aTableList:aTableList}
  
set optionRec to optionRec & {aSortOrder:{column1:"Name", column2:"Price", column3:"Specs"}}
  
  
set aMainMes to "項目の選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
set dateObj to my chooseData(aMainMes, aSubMes, optionRec)
end run

# アラートダイアログでtableviewを表示
on chooseData(aMainMes, aSubMes, optionRec)
  script MyDialog
    property parent : AppleScript
    
use AppleScript
    
use scripting additions
    
use framework "Foundation"
    
property _retrieve_data : missing value
    
on make
      set aClass to me
      
script
        property parent : aClass
      end script
    end make
    
## ダイアログの呼び出し
    
on call(aMainMes, aSubMes, optionRec)
      set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myOption:optionRec}
      
parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true
      
if (my _retrieve_data) is missing value then error number -128
      
return (my _retrieve_data)
    end call
    
## ダイアログの生成
    
on raize:paramObj
      ###
      
set mesText to paramObj’s myMessage
      
set infoText to paramObj’s mySubMessage
      
set paramObj to paramObj’s myOption
      
### set up view
      
set {theView, makeObj} to my makeContentView(paramObj)
      
### set up alert
      
tell current application’s NSAlert’s new()
        setMessageText_(mesText)
        
setInformativeText_(infoText)
        
addButtonWithTitle_("OK")
        
addButtonWithTitle_("Cancel")
        
setAccessoryView_(theView)
        
tell |window|()
          setInitialFirstResponder_(theView)
        end tell
        
#### show alert in modal loop
        
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
      end tell
      
### retrieve date
      
my retrieveData(makeObj)
    end raize:
    
    
## ContentView を作成
    
property _data_source : missing value
    
property _data_type : (current date) as text –do shell script "uuidgen" –>ユニークな文字列ならなんでもいい?
    
on makeContentView(paramObj)
      ## 準備
      
set aList to (paramObj’s aTableList) as list
      
set keyRec to (paramObj’s aSortOrder) as record
      
set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyRec)
      
      
set my _data_source to current application’s NSArrayController’s new()
      
repeat with anItem in aList
        set aDict to (current application’s NSDictionary’s dictionaryWithDictionary:anItem)
        ((
my _data_source)’s addObject:aDict)
      end repeat
      (
my _data_source)’s setSelectionIndex:0
      
      
## NSTableView
      
tell current application’s NSTableView’s alloc()
        tell initWithFrame_(current application’s CGRectZero)
          setAllowsEmptySelection_(false)
          
setAllowsMultipleSelection_(true)
          
setDataSource_(me)
          
setDelegate_(me)
          
setDoubleAction_("doubleAction:")
          
setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask)
          
setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular)
          
setTarget_(me)
          
setUsesAlternatingRowBackgroundColors_(true)
          
          
registerForDraggedTypes_({my _data_type})
          
setDraggingSourceOperationMask_forLocal_(current application’s NSDragOperationCopy, false)
          
          
set thisRowHeight to rowHeight() as integer
          
set aTableObj to it
        end tell
      end tell
      
      
## NSTableColumn
      
### Columnの並び順を指定する
      
set viewWidth to 0
      
set columnsCount to keyDict’s |count|()
      
repeat with colNum from 1 to columnsCount
        
        
set keyName to "column" & colNum as text
        
set aTitle to (keyDict’s objectForKey:keyName)
        
        
tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text))
          tell headerCell()
            setStringValue_(aTitle)
            
set thisHeaderHeight to cellSize()’s height
          end tell
          
          
### ソートの設定
          
set sortDescriptor to (current application’s NSSortDescriptor’s sortDescriptorWithKey:aTitle ascending:true selector:"compare:")
          
setSortDescriptorPrototype_(sortDescriptor)
          
          
### 表示内容をNSArrayControllerにバインディング
          
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value)
          
          
set aTableColumn to it
        end tell
        
        
### Columnの横幅を調整
        
tell aTableObj
          addTableColumn_(aTableColumn)
          
–reloadData()
          
set colWidthMax to 0
          
set rowCount to numberOfRows()
          
          
repeat with rowNum from 1 to rowCount
            tell preparedCellAtColumn_row_(colNum – 1, rowNum – 1)
              set thisWidthSize to cellSize()’s width
              
–log result
              
if thisWidthSize is greater than or equal to colWidthMax then set colWidthMax to thisWidthSize
            end tell
          end repeat
        end tell
        
–log colWidthMax
        
        
set colWidthMax to colWidthMax + 5 –> 5は余白
        
tell aTableColumn
          setWidth_(colWidthMax)
        end tell
        
        
set viewWidth to viewWidth + colWidthMax
      end repeat
      
      
## NSScrollView
      
### Viewの高さを計算
      
set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない
      
set vSize to current application’s NSMakeRect(0, 0, viewWidth + 10, viewHeight) –> 10を足すといい感じの横幅になる
      
      
### Viewを作成
      
tell current application’s NSScrollView’s alloc()
        tell initWithFrame_(vSize)
          setBorderType_(current application’s NSBezelBorder)
          
setDocumentView_(aTableObj)
          
–setHasHorizontalScroller_(true)
          
–setHasVerticalScroller_(true)
          
set aScroll to it
        end tell
      end tell
      
      
return {aScroll, aTableObj}
    end makeContentView
    
    
## retrieve date
    
on retrieveData(aTableObj)
      set aIndexSet to aTableObj’s selectedRowIndexes()
      
set my _retrieve_data to ((my _data_source)’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list –setSelectionIndexes:aIndexSet
      
log result
    end retrieveData
    
    
    
# NSTableViewDatasource
    
on numberOfRowsInTableView:aTableView
      –log "numberOfRowsInTableView:"
      
return (my _data_source)’s content()’s |count|()
    end numberOfRowsInTableView:
    
# NSTableViewDelegate
    
on tableView:aTableView objectValueForTableColumn:aColumn row:aRow
    
end tableView:objectValueForTableColumn:row:
    
# テーブル内のセルが編集できるか
    
on tableView:aTableView shouldEditTableColumn:aColumn row:aRow
      return false
    end tableView:shouldEditTableColumn:row:
    
# テーブル内をダブルクリックしたらOKボタンを押す
    
on doubleAction:sender
      –log "doubleAction"
      
–log sender’s |className|() as text
      
## ヘッダをクリックした時は何もしない
      
if (sender’s clickedRow()) is -1 then return
      
      
set theEvent to current application’s NSEvent’s ¬
        keyEventWithType:(current application’s NSEventTypeKeyDown) ¬
          location:(current application’s NSZeroPoint) ¬
          
modifierFlags:0 ¬
          
timestamp:0.0 ¬
          
windowNumber:(sender’s |window|()’s windowNumber()) ¬
          
context:(current application’s NSGraphicsContext’s currentContext()) ¬
          
|characters|:return ¬
          
charactersIgnoringModifiers:(missing value) ¬
          
isARepeat:false ¬
          
keyCode:0
      current application’s NSApp’s postEvent:theEvent atStart:(not false)
    end doubleAction:
    
    
# Drag Operation Method
    
## ドラッグを開始(ペーストボードに書き込む)
    
on tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard
      –log "writeRowsWithIndexes"
      
set aData to current application’s NSKeyedArchiver’s archivedDataWithRootObject:rowIndexes
      
pboard’s declareTypes:{my _data_type} owner:(missing value)
      
pboard’s setData:aData forType:(my _data_type)
      
return true
    end tableView:writeRowsWithIndexes:toPasteboard:
    
## ドラッグ途中
    
on tableView:aTableView validateDrop:info proposedRow:row proposedDropOperation:operation
      –log "validateDrop"
      
### 列の間にドラッグ
      
if (operation is current application’s NSTableViewDropAbove) then
        return current application’s NSDragOperationMove
      end if
      
### 項目の上にドラッグ
      
set aTypes to info’s draggingPasteboard’s types()
      
if (aTypes’s containsObject:(my _data_type)) as boolean then
        return current application’s NSDragOperationNone
      end if
      
return current application’s NSDragOperationNone
    end tableView:validateDrop:proposedRow:proposedDropOperation:
    
## ドラッグ終了
    
on tableView:aTableView acceptDrop:info row:row dropOperation:operation
      –log "acceptDrop"
      
set pboard to info’s draggingPasteboard()
      
      
set rowData to pboard’s dataForType:(my _data_type)
      
set rowIndexes to current application’s NSKeyedUnarchiver’s unarchiveObjectWithData:rowData
      
      
if (rowIndexes’s firstIndex()) < row then
        set row to row – (rowIndexes’s |count|())
      end if
      
      
set aRange to current application’s NSMakeRange(row, rowIndexes’s |count|())
      
set aIndexSet to current application’s NSIndexSet’s indexSetWithIndexesInRange:aRange
      
      
tell my _data_source
        set anObj to content()’s objectsAtIndexes:rowIndexes
        
removeObjects_(anObj)
        
insertObjects_atArrangedObjectIndexes_(anObj, aIndexSet)
        
rearrangeObjects()
      end tell
      
return true
    end tableView:acceptDrop:row:dropOperation:
  end script
  
  
##
  
tell (make MyDialog)
    return call(aMainMes, aSubMes, optionRec)
  end tell
end chooseData

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり

Posted on 12月 24, 2019 by Takaaki Naganoya

先日「macOS Native」のイベント後に秋葉原で顔見知りのScripter3人でカレー食べて帰ってきました。その際に、アラートダイアログ上に作成した箱庭User Interface類の話で盛り上がっていました。「意外と使える」「自分でもいろいろ作ってる」「公開してよ」といったような。

本Scriptはその際の話を受けてEdama2さんが作っている試作品の、さらに試作品になるわけですが、プログラム内容が自分とは別の価値体系で清書し直されており、あいかわらずロジックとコードの美しさにうならされます。また、「本来倒すべき敵」に向けての実装が行われているので、単にテーブルビューを表示するだけには少々機能過剰になっています。

以下、Edama2さんによる投稿内容です。

タイトルは「アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり」

主な変更点は
表示する内容に合わせてviewのサイズを調整
レコードのラベルをヘッダのタイトルにする
Table ViewをダブルクリックするとOKボタンを押す
setAllowsEmptySelection: をfalseにして1行目を選択するようにした

データ作成部分が詰め込み効率優先ではなく可読性を高めた作りになっているのが、Edama2さんらしいところです。また、tellブロックをCocoa Scriptingにも多用しているのも特徴です。

ロジックが読みやすい一方で、Cocoa Framework相手にここまで見やすく書くのは、相当の努力が必要なはずで、Cocoa Scripting時に省いてしまいがちな「清書」という1手間が入っていることを感じさせます。

本Scriptはハンドラ(サブルーチン)が前の方に配置される記法です。AppleScriptはもともとハンドラ宣言部を前に書く言語なので、世界的に見るとこちらが主流です(Shane Stanleyですらハンドラ宣言部は前に書きます)。逆に、自分が他の言語に合わせてハンドラ宣言部を後ろに配置するのは、全体から見ると異端といえます(他の言語と書き方が違いすぎるのはどうかと思って、ハンドラ宣言部を後ろに書いています)。

たしかに、自分の箱庭テーブルビューダイアログではカラム幅とテーブルの縦サイズは一切手をつけていなかったので、縦横幅高さの自動調整が入ると、格段に見やすくなります。

Cocoaの機能を呼び出すAppleScriptにおいても、書きこなれてきたことによりハンドラ内にScript文を書いてさらに内部にハンドラを記述するといったような、論理分割やら階層構造化が本Scriptにおいて多用されています。そのあたり、applescript-stdlib(2015)で見かけましたが、本Scriptのように控えめな利用だと安心できます。

本Script内には継続記号(「¬」)が多用されていますが、macOS日本語ユーザー環境上でScript Debuggerを使っていると、構文確認時にこれがひっかかってエラーになることがあります(というか、実際にウチでこれを編集してなっています)。継続記号がひっかかって構文確認(コンパイル)できないようであれば、いっそ継続記号をすべて削除すべきでしょう。継続記号に「可読性を上げる」以外の機能はないため、削除しても無害です。

AppleScript名:アラートダイアログ上にTable Viewを表示 v7_サイズ調整あり.scpt
on run
  
  
set aTableList to {}
  
set aTableList’s end to {|Name|:"MacBook Air", Price:"119,800円", Specs:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}
  
set aTableList’s end to {|Name|:"MacBook Pro 13", Price:"139,800円", Specs:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}
  
set aTableList’s end to {|Name|:"MacBook Pro 15", Price:"258,800円", Specs:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"Mac mini", Price:"122,800円", Specs:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"iMac 21.5", Price:"120,800円", Specs:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}
  
set aTableList’s end to {|Name|:"iMac 4K 21.5", Price:"142,800円", Specs:"3.6GHzクアッドコアIntel Core i3"}
  
set aTableList’s end to {|Name|:"iMac 5K 27", Price:"198,800円", Specs:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}
  
set optionRec to {aTableList:aTableList}
  
set optionRec to optionRec & {aSortOrder:{column1:"Name", column2:"Price", column3:"Specs"}}
  
  
set aMainMes to "項目の選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
set dateObj to my chooseData(aMainMes, aSubMes, optionRec)
end run

# アラートダイアログでtableviewを表示
on chooseData(aMainMes, aSubMes, optionRec)
  script MyDialog
    property parent : AppleScript
    
use AppleScript
    
use scripting additions
    
use framework "Foundation"
    
property _retrieve_data : missing value
    
on make
      set aClass to me
      
script
        property parent : aClass
      end script
    end make
    
## ダイアログの呼び出し
    
on call(aMainMes, aSubMes, optionRec)
      set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myOption:optionRec}
      
parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true
      
if (my _retrieve_data) is missing value then error number -128
      
return (my _retrieve_data)
    end call
    
## ダイアログの生成
    
on raize:paramObj
      ###
      
set mesText to paramObj’s myMessage
      
set infoText to paramObj’s mySubMessage
      
set paramObj to paramObj’s myOption
      
### set up view
      
set {theView, makeObj} to my makeContentView(paramObj)
      
### set up alert
      
tell current application’s NSAlert’s new()
        setMessageText_(mesText)
        
setInformativeText_(infoText)
        
addButtonWithTitle_("OK")
        
addButtonWithTitle_("Cancel")
        
setAccessoryView_(theView)
        
tell |window|()
          setInitialFirstResponder_(theView)
        end tell
        
#### show alert in modal loop
        
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
      end tell
      
### retrieve date
      
my retrieveData(makeObj)
    end raize:
    
    
## ContentView を作成
    
property _data_source : missing value
    
on makeContentView(paramObj)
      ## 準備
      
set aList to (paramObj’s aTableList) as list
      
set keyRec to (paramObj’s aSortOrder) as record
      
set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyRec)
      
      
set my _data_source to current application’s NSArrayController’s new()
      
repeat with anItem in aList
        set aDict to (current application’s NSDictionary’s dictionaryWithDictionary:anItem)
        ((
my _data_source)’s addObject:aDict)
      end repeat
      
      
## NSTableView
      
tell current application’s NSTableView’s alloc()
        tell initWithFrame_(current application’s CGRectZero)
          setAllowsEmptySelection_(false)
          
setDataSource_(me)
          
setDelegate_(me)
          
setDoubleAction_("doubleAction:")
          
setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask)
          
setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular)
          
setTarget_(me)
          
setUsesAlternatingRowBackgroundColors_(true)
          
          
set thisRowHeight to rowHeight() as integer
          
set aTableObj to it
        end tell
      end tell
      
      
## NSTableColumn
      
### Columnの並び順を指定する
      
set viewWidth to 0
      
set columnsCount to keyDict’s |count|()
      
repeat with colNum from 1 to columnsCount
        
        
set keyName to "column" & colNum as text
        
set aTitle to (keyDict’s objectForKey:keyName)
        
        
tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text))
          tell headerCell()
            setStringValue_(aTitle)
            
set thisHeaderHeight to cellSize()’s height
          end tell
          
          
set aTableColumn to it
        end tell
        
        
### Columnの横幅を調整
        
tell aTableObj
          addTableColumn_(aTableColumn)
          
–reloadData()
          
set colWidthMax to 0
          
set rowCount to numberOfRows()
          
          
repeat with rowNum from 1 to rowCount
            tell preparedCellAtColumn_row_(colNum – 1, rowNum – 1)
              set thisWidthSize to cellSize()’s width
              
–log result
              
if thisWidthSize is greater than or equal to colWidthMax then set colWidthMax to thisWidthSize
            end tell
          end repeat
        end tell
        
–log colWidthMax
        
        
set colWidthMax to colWidthMax + 5 –> 5は余白
        
tell aTableColumn
          setWidth_(colWidthMax)
        end tell
        
        
set viewWidth to viewWidth + colWidthMax
      end repeat
      
      
## NSScrollView
      
### Viewの高さを計算
      
set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない
      
set vSize to current application’s NSMakeRect(0, 0, viewWidth + 10, viewHeight) –> 10を足すといい感じの横幅になる
      
      
### Viewを作成
      
tell current application’s NSScrollView’s alloc()
        tell initWithFrame_(vSize)
          setBorderType_(current application’s NSBezelBorder)
          
setDocumentView_(aTableObj)
          
–setHasHorizontalScroller_(true)
          
–setHasVerticalScroller_(true)
          
set aScroll to it
        end tell
      end tell
      
      
return {aScroll, aTableObj}
    end makeContentView
    
    
## retrieve data
    
on retrieveData(aTableObj)
      set aRow to aTableObj’s selectedRow()
      
log result
      
if aRow is -1 then
        set my _retrieve_data to {}
      else
        set my _retrieve_data to ((my _data_source)’s arrangedObjects()’s objectAtIndex:aRow) as record
      end if
    end retrieveData
    
    
    
# NSTableViewDatasource
    
on numberOfRowsInTableView:aTableView
      –log "numberOfRowsInTableView:"
      
return (my _data_source)’s content()’s |count|()
    end numberOfRowsInTableView:
    
# NSTableViewDelegate
    
on tableView:aTableView objectValueForTableColumn:aColumn row:aRow
      –log "objectValueForTableColumn"
      
set keyName to aColumn’s headerCell()’s title()
      
set aDict to (my _data_source)’s arrangedObjects()’s objectAtIndex:aRow
      
set aStr to aDict’s objectForKey:keyName
      
–log result as text
      
return aStr
    end tableView:objectValueForTableColumn:row:
    
# テーブル内のセルが編集できるか
    
on tableView:aTableView shouldEditTableColumn:aColumn row:aRow
      return false
    end tableView:shouldEditTableColumn:row:
    
# テーブル内をダブルクリックしたらOKボタンを押す
    
on doubleAction:sender
      –log "doubleAction"
      
–log sender’s |className|() as text
      
set theEvent to current application’s NSEvent’s ¬
        keyEventWithType:(current application’s NSEventTypeKeyDown) ¬
          location:(current application’s NSZeroPoint) ¬
          
modifierFlags:0 ¬
          
timestamp:0.0 ¬
          
windowNumber:(sender’s |window|()’s windowNumber()) ¬
          
context:(current application’s NSGraphicsContext’s currentContext()) ¬
          
|characters|:return ¬
          
charactersIgnoringModifiers:(missing value) ¬
          
isARepeat:false ¬
          
keyCode:0
      current application’s NSApp’s postEvent:theEvent atStart:(not false)
    end doubleAction:
  end script
  
  
##
  
tell (make MyDialog)
    return call(aMainMes, aSubMes, optionRec)
  end tell
end chooseData

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy | 1 Comment

アラートダイアログ上にTable Viewを表示 v6_サーチフィールドつき

Posted on 12月 24, 2019 by Takaaki Naganoya

検索フィールドつきのTable View表示ダイアログを表示するAppleScriptです。項目選択を行う簡易ユーザーインタフェース(ダイアログ)で、選択肢の数が多い場合に備えて項目のキーワード抽出機能を付加したものです。

サーチフィールドの実装は無理かと思っていたのですが、単体でアラートダイアログ上に表示してみたら、予想外にキーボード入力イベントと検索語のクリアのイベントを簡単に拾えたので、Table Viewと組み合わせて試してみました。

AppleScript名:アラートダイアログ上にTable Viewを表示 v6_サーチフィールドつき
— Created 2019-12-23 by Takaaki Naganoya
— 2019 Piyomaru Software
set aRes to displayCondTable() of filterTableDialogView
–> {field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)", field2:198800, field1:"iMac 5K 27"}

script filterTableDialogView
  use scripting additions
  
use framework "Foundation"
  
use framework "AppKit"
  
property parent : AppleScript
  
  
property NSView : a reference to current application’s NSView
  
property NSAlert : a reference to current application’s NSAlert
  
property NSIndexSet : a reference to current application’s NSIndexSet
  
property NSPredicate : a reference to current application’s NSPredicate
  
property NSScrollView : a reference to current application’s NSScrollView
  
property NSTableView : a reference to current application’s NSTableView
  
property NSSearchField : a reference to current application’s NSSearchField
  
property NSTableColumn : a reference to current application’s NSTableColumn
  
property NSMutableArray : a reference to current application’s NSMutableArray
  
property NSRunningApplication : a reference to current application’s NSRunningApplication
  
property NSModalPanelWindowLevel : a reference to current application’s NSModalPanelWindowLevel
  
  
property theResult : 0
  
property returnCode : 0
  
property theDataSource : {}
  
property tView : missing value
  
property aDataList : {}
  
  
  
on displayCondTable()
    set (my theResult) to 0 –initialize
    
    
set aDataList to {{field1:"MacBook Air", field2:119800, field3:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}, {field1:"MacBook Pro 13", field2:139800, field3:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}, {field1:"MacBook Pro 15", field2:258800, field3:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}, {field1:"Mac mini", field2:122800, field3:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}, {field1:"iMac 21.5", field2:120800, field3:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}, {field1:"iMac 4K 21.5", field2:142800, field3:"3.6GHzクアッドコアIntel Core i3"}, {field1:"iMac 5K 27", field2:198800, field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}, {field1:"iMac Pro", field2:558800, field3:"3.2GHz Intel Xeon W Turbo Boost使用時最大4.2GHz 19MBキャッシュ"}, {field1:"Mac Pro 2019", field2:599800, field3:"3.5GHz 8コアIntel Xeon Wプロセッサ(Turbo Boost使用時最大4.0GHz)"}}
    
    
set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", aTableList:aDataList, aSortOrder:{"field1", "field2", "field3"}}
    
    
–my chooseItemByTableView:paramObj –for debug
    
my performSelectorOnMainThread:"chooseItemByTableView:" withObject:paramObj waitUntilDone:true
    
    
return (my theResult)
  end displayCondTable
  
  
  
  
on chooseItemByTableView:paramObj
    set aMainMes to myMessage of paramObj
    
set aSubMes to mySubMessage of paramObj
    
set aTList to (aTableList of paramObj) as list
    
set labelSortList to (aSortOrder of paramObj) as list
    
    
set aWidth to 800
    
set aHeight to 300
    
    
–Viewをつくる
    
set parentView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
    
set aTextInput to makeNSSearchField(0, aHeight – 20, 300, 20) of me
    
    
    
set aScroll to makeTableView(aTList, aWidth, aHeight – 80, labelSortList) of me
    
    
parentView’s setSubviews:{aTextInput, aScroll}
    
    
— set up alert  
    
set theAlert to NSAlert’s alloc()’s init()
    
tell theAlert
      its setMessageText:aMainMes
      
its setInformativeText:aSubMes
      
its addButtonWithTitle:"OK"
      
its addButtonWithTitle:"Cancel"
      
its setAccessoryView:(parentView)
      
      
set myWindow to its |window|
    end tell
    
    
myWindow’s setLevel:(NSModalPanelWindowLevel)
    
    
— show alert in modal loop
    
NSRunningApplication’s currentApplication()’s activateWithOptions:0
    
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
    
if (my returnCode) = 1001 then error number -128
    
    
set tmpResult to (aScroll’s documentView’s selectedRow()) + 1
    
set (my theResult) to contents of item tmpResult of ((my theDataSource) as list)
  end chooseItemByTableView:
  
  
  
on doModal:aParam
    set (my returnCode) to (aParam’s runModal()) as number
  end doModal:
  
  
  
  
  
–TableView Event Handlers
  
on numberOfRowsInTableView:aView
    return (my theDataSource)’s |count|()
  end numberOfRowsInTableView:
  
  
  
on tableView:aView objectValueForTableColumn:aColumn row:aRow
    set aRec to (my theDataSource)’s objectAtIndex:(aRow as number)
    
set aTitle to (aColumn’s headerCell()’s title()) as string
    
set aRes to (aRec’s valueForKey:aTitle)
    
return aRes
  end tableView:objectValueForTableColumn:row:
  
  
  
  
on makeTableView(aDicList, aWidth, aHeight, labelSortList)
    set aOffset to 40
    
set theDataSource to NSMutableArray’s alloc()’s init()
    
theDataSource’s addObjectsFromArray:aDicList
    
    
–TextField
    
set aTextInput to makeNSSearchField(0, 0, 300, 20) of me
    
    
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
    
set tView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
    
    
set aLen to length of labelSortList
    
    
repeat with i in labelSortList
      set j to contents of i
      
set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:j)
      (
aColumn’s setWidth:(aWidth div aLen))
      (
aColumn’s headerCell()’s setStringValue:j)
      (
tView’s addTableColumn:aColumn)
    end repeat
    
    
tView’s setDelegate:me
    
tView’s setDataSource:me
    
tView’s reloadData()
    
    
aScroll’s setDocumentView:tView
    
tView’s enclosingScrollView()’s setHasVerticalScroller:true
    
aScroll’s setVerticalLineScroll:(30.0 as real)
    
    
–1行目を選択
    
set aIndexSet to NSIndexSet’s indexSetWithIndex:0
    
tView’s selectRowIndexes:aIndexSet byExtendingSelection:false
    
    
–強制的にトップにスクロール
    
set aDBounds to aScroll’s documentView()’s |bounds|()
    
if class of aDBounds = list then
      –macOS 10.13 or later
      
set maxHeight to item 2 of item 1 of aDBounds
    else
      –macOS 10.10….10.12
      
set maxHeight to height of |size| of aDBounds
    end if
    
    
set aPT to current application’s NSMakePoint(0.0, -1 * (maxHeight as real))
    
aScroll’s documentView()’s scrollPoint:aPT
    
    
return aScroll
  end makeTableView
  
  
  
on alertShowHelp:aNotification
    display dialog "Help Me!" buttons {"OK"} default button 1 with icon 1
    
return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする
  end alertShowHelp:
  
  
  
on makeNSSearchField(xPos as integer, yPos as integer, myWidth as integer, myHeight as integer)
    set aSearchF to NSSearchField’s alloc()’s initWithFrame:(current application’s NSMakeRect(xPos, yPos, myWidth, myHeight))
    
aSearchF’s setDelegate:(me)
    
return aSearchF
  end makeNSSearchField
  
  
  
–検索クエリ入力時
  
on searchFieldDidStartSearching:(aField)
    set aStr to (aField’s stringValue()) as string
    
set predicStr to "field1 contains[cd] ’" & aStr & "’ OR field3 contains[cd] ’" & aStr & "’"
    
set filteredArray to filterRecListByLabel1((my aDataList), predicStr) of me
    
set (my theDataSource) to filteredArray
    
tView’s reloadData()
  end searchFieldDidStartSearching:
  
  
  
–Search Fieldの「x」ボタンを押したイベント
  
on searchFieldDidEndSearching:(aField)
    set (my theDataSource) to NSMutableArray’s arrayWithArray:(my aDataList)
    
tView’s reloadData()
  end searchFieldDidEndSearching:
  
  
  
–リストに入れたレコードを、指定の属性ラベルの値で抽出
  
on filterRecListByLabel1(aRecList as list, aPredicate as string)
    set aArray to NSMutableArray’s arrayWithArray:aRecList
    
set aPredicate to NSPredicate’s predicateWithFormat:aPredicate
    
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
    
return filteredArray
  end filterRecListByLabel1
  
end script

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSIndexSet NSModalPanelWindowLevel NSMutableArray NSPredicate NSRunningApplication NSScrollView NSSearchField NSTableColumn NSTableView NSView | 8 Comments

アラートダイアログ上にTable Viewを表示 v5_複数キー抽出つき

Posted on 12月 23, 2019 by Takaaki Naganoya

アラートダイアログ上にTableViewを表示し、複数キーの組み合わせによって項目抽出。選択項目のデータを返すAppleScriptです。

あくまで試作品なので、あちらこちらに「お可愛らしい」実装が転がっています。NSBoxがまだまともに使いこなせていないあたりとか(Xcode上で部品として配置するのは楽勝ですが、プログラムから動的に生成して使用するのは別なので)。

抽出キー数は可変で外部から指定できるとよいのですが、本Scriptでは固定です。また、各抽出条件も外部から供給するのが本来あるべき姿ですが、ハードコーディングしています。

あと、TableViewの下側に謎の余白があるのは、めんどくさくなってそのまま放置しています。これで外部から各種パラメータを指定できるようにして、sdefつけて1命令で呼び出せるようにしておけば、レコード数の多い選択項目から条件抽出しつつ項目選択できてよいでしょう。

プログラム内容自体は、高度でもなければ高尚でもありません。実にダラダラとGUI部品のパラメータを設定しているだけです。

AppleScript名:アラートダイアログ上にTable Viewを表示 v5_複数キー抽出つき
— Created 2019-12-22 by Takaaki Naganoya
— 2019 Piyomaru Software
set aRes to displayCondTable() of filterTableDialogView
–> {field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)", field2:198800, field1:"iMac 5K 27"}

script filterTableDialogView
  use scripting additions
  
use framework "Foundation"
  
use framework "AppKit"
  
property parent : AppleScript
  
  
property NSBox : a reference to current application’s NSBox
  
property NSView : a reference to current application’s NSView
  
property NSAlert : a reference to current application’s NSAlert
  
property NSColor : a reference to current application’s NSColor
  
property NSIndexSet : a reference to current application’s NSIndexSet
  
property NSPredicate : a reference to current application’s NSPredicate
  
property NSScrollView : a reference to current application’s NSScrollView
  
property NSTableView : a reference to current application’s NSTableView
  
property NSTableColumn : a reference to current application’s NSTableColumn
  
property NSPopUpButton : a reference to current application’s NSPopUpButton
  
property NSMutableArray : a reference to current application’s NSMutableArray
  
property NSRunningApplication : a reference to current application’s NSRunningApplication
  
property NSCompoundPredicate : a reference to current application’s NSCompoundPredicate
  
property NSModalPanelWindowLevel : a reference to current application’s NSModalPanelWindowLevel
  
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn
  
  
  
property theResult : 0
  
property returnCode : 0
  
property theDataSource : {}
  
property tView : missing value
  
property aDataList : {}
  
property aSel : 0
  
property bSel : 0
  
property cSel : 0
  
property predList : {}
  
property a1Button : missing value
  
property a2Button : missing value
  
property a3Button : missing value
  
  
on displayCondTable()
    set (my theResult) to 0 –initialize
    
    
set aDataList to {{field1:"MacBook Air", field2:119800, field3:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}, {field1:"MacBook Pro 13", field2:139800, field3:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}, {field1:"MacBook Pro 15", field2:258800, field3:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}, {field1:"Mac mini", field2:122800, field3:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}, {field1:"iMac 21.5", field2:120800, field3:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}, {field1:"iMac 4K 21.5", field2:142800, field3:"3.6GHzクアッドコアIntel Core i3"}, {field1:"iMac 5K 27", field2:198800, field3:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}, {field1:"iMac Pro", field2:558800, field3:"3.2GHz Intel Xeon W Turbo Boost使用時最大4.2GHz 19MBキャッシュ"}, {field1:"Mac Pro 2019", field2:599800, field3:"3.5GHz 8コアIntel Xeon Wプロセッサ(Turbo Boost使用時最大4.0GHz)"}}
    
    
set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", aTableList:aDataList, aSortOrder:{"field1", "field2", "field3"}}
    
    
–my chooseItemByTableView:paramObj –for debug
    
my performSelectorOnMainThread:"chooseItemByTableView:" withObject:paramObj waitUntilDone:true
    
    
return (my theResult)
  end displayCondTable
  
  
on chooseItemByTableView:paramObj
    set aMainMes to myMessage of paramObj
    
set aSubMes to mySubMessage of paramObj
    
set aTList to (aTableList of paramObj) as list
    
set labelSortList to (aSortOrder of paramObj) as list
    
    
set aWidth to 800
    
set aHeight to 400
    
set my aSel to 0
    
set my bSel to 0
    
    
–Viewをつくる
    
set parentView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
    
    
    
–BOX Aをつくる
    
set aBox to (NSBox’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aHeight – 60, aWidth, 60)))
    (
aBox’s setTitle:("Filter Condition"))
    
    
–このあたり、項目数に合わせてUIを可変で生成するように(本Scriptは試作品なので、決め打ちでUI生成)
    
set a1Button to (NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 200, 30)) pullsDown:false)
    
a1Button’s removeAllItems()
    (
a1Button’s addItemsWithTitles:{"▼Select Item", "MacBook", "iMac", "mini", "Air", "Pro"})
    
a1Button’s setTarget:(me)
    
a1Button’s setAction:("mySelector:")
    
a1Button’s setEnabled:(true)
    
    
set a2Button to (NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(220, 0, 200, 30)) pullsDown:false)
    
a2Button’s removeAllItems()
    (
a2Button’s addItemsWithTitles:{"▼Select Item", "150000", "200000", "300000", "400000", "600000"})
    
a2Button’s setTarget:(me)
    
a2Button’s setAction:("mySelector:")
    
a2Button’s setEnabled:(true)
    
    
set a3Button to (NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(440, 0, 200, 30)) pullsDown:false)
    
a3Button’s removeAllItems()
    (
a3Button’s addItemsWithTitles:{"▼Select Item", "デュアルコア", "クアッドコア", "6コア", "8コア"})
    
a3Button’s setTarget:(me)
    
a3Button’s setAction:("mySelector:")
    
a3Button’s setEnabled:(true)
    
    
    (
aBox’s addSubview:a1Button)
    (
aBox’s addSubview:a2Button)
    (
aBox’s addSubview:a3Button)
    
    
    
–BOX Bをつくる
    
set bBox to (NSBox’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight – 80)))
    (
bBox’s setTitle:("Data"))
    
    
set aScroll to makeTableView(aTList, aWidth, aHeight – 150, labelSortList) of me
    
    (
bBox’s addSubview:aScroll)
    
    
parentView’s setSubviews:{aBox, bBox}
    
    
— set up alert  
    
set theAlert to NSAlert’s alloc()’s init()
    
tell theAlert
      its setMessageText:aMainMes
      
its setInformativeText:aSubMes
      
its addButtonWithTitle:"OK"
      
its addButtonWithTitle:"Cancel"
      
its setAccessoryView:(parentView)
      
      
set myWindow to its |window|
    end tell
    
    
myWindow’s setLevel:(NSModalPanelWindowLevel)
    
    
— show alert in modal loop
    
NSRunningApplication’s currentApplication()’s activateWithOptions:0
    
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
    
if (my returnCode) = 1001 then error number -128
    
    
set tmpResult to (aScroll’s documentView’s selectedRow()) + 1
    
set (my theResult) to contents of item tmpResult of ((my theDataSource) as list)
  end chooseItemByTableView:
  
  
  
on doModal:aParam
    set (my returnCode) to (aParam’s runModal()) as number
  end doModal:
  
  
  
  
  
–TableView Event Handlers
  
on numberOfRowsInTableView:aView
    return (my theDataSource)’s |count|()
  end numberOfRowsInTableView:
  
  
on tableView:aView objectValueForTableColumn:aColumn row:aRow
    set aRec to (my theDataSource)’s objectAtIndex:(aRow as number)
    
set aTitle to (aColumn’s headerCell()’s title()) as string
    
set aRes to (aRec’s valueForKey:aTitle)
    
return aRes
  end tableView:objectValueForTableColumn:row:
  
  
  
  
on makeTableView(aDicList, aWidth, aHeight, labelSortList)
    set aOffset to 40
    
set theDataSource to NSMutableArray’s alloc()’s init()
    
theDataSource’s addObjectsFromArray:aDicList
    
    
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
    
set tView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
    
    
set aLen to length of labelSortList
    
    
repeat with i in labelSortList
      set j to contents of i
      
set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:j)
      (
aColumn’s setWidth:(aWidth div aLen))
      (
aColumn’s headerCell()’s setStringValue:j)
      (
tView’s addTableColumn:aColumn)
    end repeat
    
    
tView’s setDelegate:me
    
tView’s setDataSource:me
    
tView’s reloadData()
    
    
aScroll’s setDocumentView:tView
    
tView’s enclosingScrollView()’s setHasVerticalScroller:true
    
aScroll’s setVerticalLineScroll:(30.0 as real)
    
    
–1行目を選択
    
set aIndexSet to NSIndexSet’s indexSetWithIndex:0
    
tView’s selectRowIndexes:aIndexSet byExtendingSelection:false
    
    
–強制的にトップにスクロール
    
set aDBounds to aScroll’s documentView()’s |bounds|()
    
if class of aDBounds = list then
      –macOS 10.13 or later
      
set maxHeight to item 2 of item 1 of aDBounds
    else
      –macOS 10.10….10.12
      
set maxHeight to height of |size| of aDBounds
    end if
    
    
set aPT to current application’s NSMakePoint(0.0, -1 * (maxHeight as real))
    
aScroll’s documentView()’s scrollPoint:aPT
    
    
return aScroll
  end makeTableView
  
  
  
on alertShowHelp:aNotification
    display dialog "Help Me!" buttons {"OK"} default button 1 with icon 1
    
return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする
  end alertShowHelp:
  
  
  
on mySelector:aObject
    set aIndex to (aObject’s indexOfSelectedItem()) as number
    
filterByMultipleCondition() of me
  end mySelector:
  
  
  
  
on filterByMultipleCondition()
    set prediCatesArray to {}
    
    
set aInd to (my a1Button’s indexOfSelectedItem()) as number
    
set bInd to (my a2Button’s indexOfSelectedItem()) as number
    
set cInd to (my a3Button’s indexOfSelectedItem()) as number
    
    
if {aInd, bInd, cInd} = {0, 0, 0} then
      set (my theDataSource) to NSMutableArray’s arrayWithArray:(my aDataList)
      
tView’s reloadData()
      
return
    end if
    
    
–このあたり、複数条件をハードコーディングしているのは超絶頭悪い。項目数の増減に対応できるべき
    
if aInd > 0 then
      set aTitle to (my a1Button’s title()) as string
      
set the end of prediCatesArray to "field1 contains ’" & aTitle & "’"
    end if
    
    
if bInd > 0 then
      set bTitle to (my a2Button’s title()) as string
      
set the end of prediCatesArray to "field2.integerValue < " & bTitle
    end if
    
    
if cInd > 0 then
      set cTitle to (my a3Button’s title()) as string
      
set the end of prediCatesArray to "field3 contains ’" & cTitle & "’"
    end if
    
    
–データ 抽出
    
set tmpList to filterDictArrayByLabel3((my aDataList), prediCatesArray) of me
    
    
–データ 再表示
    
set (my theDataSource) to NSMutableArray’s arrayWithArray:(tmpList)
    
tView’s reloadData()
  end filterByMultipleCondition
  
  
  
  
–リストに入れたレコードを、指定の属性ラベルの値で抽出(複数PredicatesをANDで合成)
  
on filterDictArrayByLabel3(origArray as list, aPredicateList as list)
    set aArray to NSMutableArray’s arrayWithArray:origArray
    
set predArray to NSMutableArray’s new()
    
    
repeat with i in aPredicateList
      (predArray’s addObject:(NSPredicate’s predicateWithFormat:(contents of i)))
    end repeat
    
    
set pred to current application’s NSCompoundPredicate’s andPredicateWithSubpredicates:predArray
    
set filteredArray to aArray’s filteredArrayUsingPredicate:pred
    
    
return filteredArray
  end filterDictArrayByLabel3
end script

★Click Here to Open This Script 

Posted in dialog GUI list Record search | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSAlertSecondButtonReturn NSBox NSColor NSCompoundPredicate NSIndexSet NSModalPanelWindowLevel NSMutableArray NSPopUpButton NSPredicate NSRunningApplication NSScrollView NSTableColumn NSTableView NSView | 1 Comment

macOS 10.14にchoose fileのバグ?

Posted on 12月 13, 2019 by Takaaki Naganoya

macOS史上最低、どうしてあの状態でリリースしたのか理解に苦しむmacOS 10.13の後なので、どうしてもかなり贔屓目に見てしまうmacOS 10.14ですが、やはりバグはありました。

2019/12/13追記 デフォルト設定値の問題で、階層表示は行えることを確認しました。

choose file with showing package contents

このコマンドを実行した場合には、パッケージ内容を表示しつつパッケージ内のファイルを選択できるはずですが、macOS 10.14ではこれがパッケージ内のファイルは選択できるものの、階層表示できていません。こんなchoose file with showing package contentsの挙動なんて見たことがありません。


▲macOS 10.13.6上の動作


▲macOS 10.14.6上の動作。一応、バンドルを選択するとその内部のフォルダに移動することは可能だが、階層表示されない


▲macOS 10.15.2上の動作

追記 edama2さんから「こちらではそういうことはないよ?」というツッコミを送っていただきました。再度確認したところ、choose fileダイアログの項目分けの初期値がmacOS 10.14では「なし」ではなく「名前」になっているために、自分のデフォルト設定では階層表示されていなかったようです。


▲自分のmacOS 10.14.6環境の初期状態。項目分けが「名前」になっていた


▲自分のmacOS 10.14.6環境でも、項目分けを「なし」に変更すると階層表示された

Posted in Bug dialog file | Tagged 10.14savvy | Leave a comment

choose multiple list lib

Posted on 12月 10, 2019 by Takaaki Naganoya

choose from listの複数リスト版。複数のポップアップメニューをダイアログ上で選択するUser Interfaceを提供する、「choose multiple list」AppleScriptライブラリです。macOS 10.13以降対応です。


▲選択リスト数は可変

–> Download chooseMultiList(To ~/Library/Script Libraries)

AppleScript name:sample 1.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/10
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — High Sierra (10.13) or later
use framework "Foundation"
use scripting additions
use mulList : script "choose multiple list"

set selList to {{"Red", "Blue", "Yellow", "Brown", "White", "Cyan", "Grey"}, {"Red", "Blue", "Yellow", "Brown", "White", "Cyan", "Grey"}}
set tList to {"1st Segments", "2nd Segments"}

set aRes to choose multiple list selList main message "Select Items Demo" sub message "Select each selection. Same selection items *NOT* allowed" with title lists tList height 140 width 400 return type item contents without allow same items
–> {"Red", "Yellow"}

★Click Here to Open This Script 

AppleScript name:sample 2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/10
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — High Sierra (10.13) or later
use framework "Foundation"
use scripting additions
use mulList : script "choose multiple list"

set selList to {{"Red", "Blue", "Yellow", "Brown", "White", "Cyan", "Grey"}, {"Red", "Blue", "Yellow", "Brown", "White", "Cyan", "Grey"}}
set tList to {"1st Segments", "2nd Segments"}

set aRes to choose multiple list selList main message "Select Items Demo" sub message "Select each selection. Same selection items allowed" with title lists tList height 140 width 400 return type item numbers with allow same items
–> {1, 1}

★Click Here to Open This Script 

テキストエディタ上でオープン中のテキストのdiff表示を行う場合のファイル選択のために作成したものです。


▲もっと汎用的に差分表示用の部品として活用するために、AppleScript用語辞書の添付が切実に望まれるApple製アプリケーション第1位のFileMerge

AppleScript名:CotEditor –> FileMergeでDiff表示.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/12/10
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — High Sierra (10.13) or later
use framework "Foundation"
use scripting additions
use mulList : script "choose multiple list"

tell application "CotEditor"
  set dCount to count every document
  
if dCount < 2 then
    display dialog "A few documents…" with title "Error" buttons {"OK"} default button 1
    
return
  else if dCount = 2 then
    –オープン中の書類が2つある場合
    
set aPath to file of document 1
    
set bPath to file of document 2
    
    
set aPOSIX to POSIX path of aPath
    
set bPOSIX to POSIX path of bPath
  else
    –オープン中の書類が2つ以上存在している場合
    
set dList to {}
    
set adList to file of every document
    
    
repeat with i in adList
      set the end of dList to POSIX path of i
    end repeat
    
    
set selList to {dList, dList}
    
set tList to {"Document #1", "Document #2"}
    
    
set aRes to choose multiple list selList main message "Select two files to display diff" sub message "Select each file. Same selection items NOT allowed" with title lists tList height 140 width 700 return type item contents without allow same items
    
    
copy aRes to {aPOSIX, bPOSIX}
    
  end if
end tell

tell application "FileMerge" to activate
do shell script "/usr/bin/opendiff " & quoted form of aPOSIX & " " & quoted form of bPOSIX & " &"

★Click Here to Open This Script 

Posted in dialog GUI list Script Libraries | Tagged 10.13savvy 10.14savvy 10.15savvy CotEditor FileMerge | Leave a comment

Post navigation

  • Older posts
  • Newer posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • CotEditorで2つの書類の行単位での差分検出
  • macOS 13.6.5 AS系のバグ、一切直らず
  • macOS 15, Sequoia
  • 初心者がつまづきやすい「log」コマンド
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Adobe AcrobatをAppleScriptから操作してPDF圧縮
  • メキシカンハットの描画
  • 与えられた文字列の1D Listのすべての順列組み合わせパターン文字列を返す v3(ベンチマーク用)
  • 2023年に書いた価値あるAppleScript
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • Numbersで選択範囲のセルの前後の空白を削除
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • AppleScriptによる並列処理
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • NaturalLanguage.frameworkでNLEmbeddingの処理が可能な言語をチェック
  • AppleScript入門③AppleScriptを使った「自動化」とは?

Tags

10.11savvy (1102) 10.12savvy (1243) 10.13savvy (1392) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (188) 14.0savvy (138) 15.0savvy (116) CotEditor (64) Finder (51) iTunes (19) Keynote (115) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (75) Pages (54) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC