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

タグ: NSArray

macOS 11、NSSortDescriptorでNSArrayを作ると100%クラッシュ

Posted on 6月 25, 2021 by Takaaki Naganoya

ものすごくありふれた「レコードをリストに入れたデータ」を高速でソートするために用いているサブルーチン「sortRecListByLabel」がmacOS 11でクラッシュするという情報を得て、調査してみたところたしかに100%クラッシュすることを確認しました。

具体的にどこでクラッシュするとかいえば、NSSortDescriptorでNSArrayを作る箇所、

set sortDesc to NSSortDescriptor's alloc()'s initWithKey:bLabelStr ascending:ascendF
set sortDescArray to NSArray's arrayWithObjects:sortDesc--This line cause crash!!!

ここです。

クラッシュを回避するためには、明示的にNSSortDescriptorでNSArrayを作成せず、Scripting Bridge経由で暗黙のlist→NSArray変換が行われるように書き換えること。こうすればクラッシュは発生しませんでした。

→ 訂正:もともとarrayWithObjectsでNSArrayを作る際には末尾にnull(missing value)が必要になるなど、昨今ではあまり使うことが推奨されない(廃止されつつある)やり方でした。NSArray arrayWithArray:などで作ればとくに問題ないことが発覚。よく見ると、ASOC初期の古いコードでそういうやり方をしていたものがあったようです。これまで「たまたま動いていた」というのが真相のようで、、、、(Thanks Shane!)

AppleScript名:asoc_レコードのリストをソート v3
— Created 2021-06-24 by Takaaki Naganoya
— 2021 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

script orig
  property aList : {}
end script

set (aList of orig) to {}

using terms from scripting additions
  repeat 100 times
    –乱数発生にrandom numberをmacOS 11+M1 Macで呼び出すと遅いので注意
    
set the end of aList of orig to {labelA:"a", labelB:(random number from 1 to 100)}
  end repeat
end using terms from

set bList to sortRecListByLabel((aList of orig), "labelB", false) of me –昇順ソート
return bList

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList as list, aLabelStr as string, ascendF as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set sortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
–set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc–macOS 11でクラッシュ
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:{sortDesc}
  
set bList to (sortedArray) as anything
  
return bList
end sortRecListByLabel

★Click Here to Open This Script 

Posted in list | Tagged 11.0savvy NSArray NSSortDescriptor | Leave a comment

AMChartsで円グラフをダイアログ上に表示 v3

Posted on 6月 27, 2020 by Takaaki Naganoya

アラートダイアログ上にWkWebViewを作成し、その上でAMChartsによる円グラフを表示するAppleScriptです。

ダイアログでテーマ選択したのちに、グラフ表示を行います。

—> Download ampiechartSample(AppleScript bundle with Script Library and HTMLs)

AppleScript名:AMChartsで円グラフをダイアログ上に表示 v3.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/06/25
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use webD : script "webDialogLib"

set themeList to {"dark", "dataviz", "material", "kelly", "frozen", "moonrisekingdom", "spiritedaway"}

set aSel to (choose from list themeList with prompt "Select Theme")
if aSel = false then
  set aTheme to false
  
set aHTML to "index_notheme.html"
else
  set aTheme to contents of first item of aSel
  
if aTheme = "dark" then
    set aTheme to false
    
set aHTML to "index_dark.html"
  else
    set aHTML to "index.html"
  end if
end if

set aList to {{label:"ひよこ王国", value:403}, {label:"ぴよぴよ連邦", value:301}, {label:"ぴよランド", value:101}, {label:"ぴよー", value:65}}
set bList to sortRecListByLabel(aList, {"value"}, {false}) of me –降順ソート

–https://www.amcharts.com/demos/pie-chart/
set mePath to path to me
set resPath to (mePath as string) & "Contents:Resources:" & aHTML
set myStr to (read (resPath as alias) as «class utf8») as string

set jsonStr to array2DToJSONArray(bList) of me as string

if aTheme = false then
  set aString to current application’s NSString’s stringWithFormat_(myStr, jsonStr) as string
else
  set aString to current application’s NSString’s stringWithFormat_(myStr, aTheme, aTheme, jsonStr) as string
end if

set paramObj to {myMessage:"Simple Pie Chart", mySubMessage:"This is a AMCharts test", htmlStr:aString, jsDelimiters:{"<script>", "</script>"}, viewSize:{1000, 550}}

webD’s displayWebDialog(paramObj)

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)
  
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList as list, aLabelStr as list, ascendF as list)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  
set aCount to length of aLabelStr
  
set sortDescArray to current application’s NSMutableArray’s new()
  
repeat with i from 1 to aCount
    set aLabel to (item i of aLabelStr)
    
set aKey to (item i of ascendF)
    
set sortDesc to (current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabel ascending:aKey)
    (
sortDescArray’s addObject:sortDesc)
  end repeat
  
  
return (aArray’s sortedArrayUsingDescriptors:sortDescArray) as list
end sortRecListByLabel

★Click Here to Open This Script 

Posted in dialog list Sort | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSArray NSJSONSerialization NSMutableArray NSSortDescriptor NSString | Leave a comment

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

Posted on 6月 1, 2020 by Takaaki Naganoya

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

ガントチャートの表示が行えるのは、なかなか便利そうです。ガントチャート系でAppleScriptから操作が行えるのは、Omni Planぐらいなので、ガントチャート表示用部品が増えるのはいいことです。

# ただ、ガントチャートのデータをもとに自動処理したことはありません。人間との間で行う対話処理が重要なので、自動処理で「あとはよろしくぅ!」というワークフローがそんなに思いつきません(予定が変わった担当者に自動で連絡しておくぐらい?)

何らかのデータ処理を行なった末にガントチャートで結果を表示する……という流れになるとは思うのですが、結果に納得できない場合にデータ修正できるようになっていないと、「なんですかそれは?」という話になりかねません。

なので、表UIでデータを入力し、その内容を反映させたガントチャートを表示。表UIとガントチャートの間で自由に行き来できる必要があります。Google Chartsではこのように複数のグラフUIを組み合わせる(並べて表示、再描画を連動)こともできるため、そういう組み合わせで利用するための「下調べ」といったところでしょう。

AppleScript名:アラートダイアログ上にWebViewでGoogle Chartを表示(Gantt Chart).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 "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 : ""

–Sample Data
–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’:[’gantt’]});
google.charts.setOnLoadCallback(drawChart);

function daysToMilliseconds(days) {
return days * 24 * 60 * 60 * 1000;
}

function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn(’string’, ’Task ID’);
data.addColumn(’string’, ’Task Name’);
data.addColumn(’date’, ’Start Date’);
data.addColumn(’date’, ’End Date’);
data.addColumn(’number’, ’Duration’);
data.addColumn(’number’, ’Percent Complete’);
data.addColumn(’string’, ’Dependencies’);

data.addRows([
[’Research’, ’Find sources’,
new Date(2015, 0, 1), new Date(2015, 0, 5), null, 100, null],
[’Write’, ’Write paper’,
null, new Date(2015, 0, 9), daysToMilliseconds(3), 25, ’Research,Outline’],
[’Cite’, ’Create bibliography’,
null, new Date(2015, 0, 7), daysToMilliseconds(1), 20, ’Research’],
[’Complete’, ’Hand in paper’,
null, new Date(2015, 0, 10), daysToMilliseconds(1), 0, ’Cite,Write’],
[’Outline’, ’Outline paper’,
null, new Date(2015, 0, 6), daysToMilliseconds(1), 100, ’Research’]
]);

var options = {
height: 275
};

var chart = new google.visualization.Gantt(document.getElementById(’chart_div’));

chart.draw(data, options);
}
</script>
</head>
<body>
  <div id=\"chart_div\"></div>
</body>
</html>"

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

set paramObj to {myMessage:"Google Gantt Chart Test", mySubMessage:"This is a simple Gantt Chart using google charts", htmlStr:myStr}
–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 1000
  
set aHeight to 300
  
  
–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
  
  
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

★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

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

Posted on 5月 29, 2020 by Takaaki Naganoya

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

Mindmap風の図を表示するので、純粋にMindmapの表示部品として使えるとよさそうですが、そういうわけでもないようです。図を表示するだけです。

また、Wordtree中の途中のノードを選択するとツリーが閉じたりしますが、どのノードを選択したかといった情報を(現状では)取得できるわけでもないため、超多項目の選択User Interfaceに使いたい場合に「使えそうで使えない」という浮世離れした部品になっています。

一応、JavaScript内ではノードのクリックイベントを拾うことができましたが、selectionで得られる属性値が「word」「color」「weight」といった、個別にノードを特定できる値ではないものばかりなので(idぐらい取れてもいいんやで)、選択UIとして使うのはつらそうです。さらに、WkWebView内のJavaScriptからその「外側の外側」にいるAppleScriptにイベントや値を渡す方法がさっぱり見つかりません。JavaScript側からローカルのファイル……に書けそうな気配はないので、cookie経由で値を受け渡すぐらいでしょうか。


▲Appleのサンプルコード「TreeView」(Written in Objective-C)。これがもっと使いまわしやすい部品として書きこなれていればいいのですが、、、、

# Mindmap風の選択UIは、なかなか手を加えてすぐに使いまわしやすい部品になっていなくて、、

プログラム自体は他のScriptとの共有部品ばかりで、新規に起こしたものはほとんどありません。


▲選択肢をツリーで描画してクリック選択でき、選択した内容をもとに処理できるという意味ではGraphViz+Dot言語が難易度低くていい感じ?(実際に使っています)


▲これもゆくゆくは多項目表示の部品として利用? 制約条件(不自由さ)を設けることで発想につなげようというコンセプトなので、自由なデータ表現の部品にはならないという見方も

AppleScript名:アラートダイアログ上にWebViewでGoogle Chartを表示(WordTree1).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 "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 : ""

–Sample Data
set aList to {{"Phrases"}, ¬
  {"cats are better than dogs"}, ¬
  {
"cats eat kibble"}, ¬
  {
"cats are better than hamsters"}, ¬
  {
"cats are awesome"}, ¬
  {
"cats are people too"}, ¬
  {
"cats eat mice"}, ¬
  {
"cats meowing"}, ¬
  {
"cats in the cradle"}, ¬
  {
"cats eat mice"}, ¬
  {
"cats in the cradle lyrics"}, ¬
  {
"cats eat kibble"}, ¬
  {
"cats for adoption"}, ¬
  {
"cats are family"}, ¬
  {
"cats eat mice"}, ¬
  {
"cats are better than kittens"}, ¬
  {
"cats are evil"}, ¬
  {
"cats are weir"}, ¬
  {
"cats eat mice"}}

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’:[’wordtree’]});
google.charts.setOnLoadCallback(drawChart);

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

var options = {
wordtree: {
format: ’implicit’,
word: ’cats’
}
};

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

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

set paramObj to {myMessage:"Google Word Tree Test", mySubMessage:"This is a simple Word Tree 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 540
  
  
–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
  
  
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

★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

アラートダイアログ上に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

NSURLSessionでREST API呼び出しv4.4

Posted on 3月 29, 2020 by Takaaki Naganoya

REST API呼び出しに欠かせない部品をアップデートしました。

–> GET method REST API v4.3
–> GET method REST API v4.1
–> GET method REST API v4

従来、NSURLConnectionを使って同期処理を行っていましたが、これがDepreceted扱いになりました。すぐになくなることはありませんが、いつかなくなる可能性があります。

このため、NSURLConnectionから動作原理の異なるNSURLSessionを使うよう移行する必要があります(curlコマンドを利用するというルートもあります)。

とはいえ、blocks構文の使えないAppleScriptでこのNSURLSessionを使えるようにするのは大変です。非同期通信を行うNSURLSessionを非同期処理のしにくいAppleScriptで、同期処理っぽく書かなくてはなりません。

NSURLSessionによる通信では、サーバーとの間で何回かのやりとりがあって、データを小分けに複数回受信する必要がありました。NSMutableDataというオブジェクトの存在を知ったのでいい感じに処理できるようになってきました。

NSURLSessionのメリットとして喧伝されている非同期処理は、AppleScriptから呼び出しているかぎりはそれほどメリットになりません。もう少し使い込めば活用できるかもしれませんが、まだそういう段階にはありません。

NSURLSessionの導入によって得られるメリットで最大のものは、キャッシュ機構です。NSURLConnectionでもキャッシュ機構は利用できましたが、NSURLSessionではよりAPIに深く統合されているものと理解しました。

キャッシュの効き方については、設定で何段階かに設定できるとAppleのオンラインドキュメントに書かれています。本Scriptでは、キャッシュ優先でキャッシュが存在している場合にはキャッシュ内のデータを返し、キャッシュが存在していない場合にはWebサーバーに問い合わせを行うように設定しています。

キャッシュが効いている状況なら、NSURLConnectionで1.8秒ぐらいの処理がNSURLSessionで0.1秒程度と大幅に速く処理できます。キャッシュが効いていない状況(初回実行時)だと、NSURLSessionのほうがやや処理に時間がかかるぐらいです。

処理結果について、NSURLConnection版とNSURLSession版で結果の比較を行ってみましたが、とくに差異は見られませんでした。

本サンプルScriptでは、Wikipedia(英語版)へのキーワードの問い合わせを行います。NSURLConnection版では複数回実行しても同じぐらいの速度で実行されますが、NSURLSession版では2度目から早く終わります(Script Debugger上で動かすと秒以下の精度の時間がわかります)。

macOS 10.14上で作ってmacOS 10.14.6/10.13.6上で動作確認しています(スクリプトエディタ、Script Debuggerで動作確認)。ただし、macOS 10.15上だとクラッシュします。Xcodeのプロジェクトに入れてみましたが、同様にmacOS 10.15上ではクラッシュします。

ただし、自分のmacOS 10.14.6環境はSIP解除しているのと、macOS 10.15環境のクラッシュログにSIP関連のメッセージが残っているので、OSバージョンではなくSIPのためかもしれません(macOS 10.14.6でもSIPを有効にするとクラッシュするかもしれない & macOS 10.15.xでもSIPを無効にするとクラッシュしないかもしれない)。

AppleScript名:GET method REST API v4.4_wikipedia
— Created 2019-05-02 by Takaaki Naganoya
— Modified 2020-03-29 by Takaaki Naganoya
— 2020 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSURLSession : a reference to current application’s NSURLSession
property NSMutableData : a reference to current application’s NSMutableData
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration

property retData : missing value
property retCode : 0
property retHeaders : 0
property drecF : false

set aQueryKeyTitle to "Steve Jobs" –キーワードの正規化が必要("戦場の絆"→"機動戦士ガンダム 戦場の絆" )
set reqURLStr to "https://en.wikipedia.org/w/api.php"
set aRec to {action:"parse", page:aQueryKeyTitle, |prop|:"wikitext", format:"json"}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL, 10) of me
set bRes to (aRes’s valueForKeyPath:"parse.wikitext.*") as string
return bRes

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(reqURLStr as string, timeoutSec as integer)
  set (my retData) to NSMutableData’s alloc()’s init()
  
set (my retCode) to 0
  
set (my retHeaders) to {}
  
set (my drecF) to false
  
  
set aURL to |NSURL|’s URLWithString:reqURLStr
  
set aRequest to NSMutableURLRequest’s requestWithURL:aURL
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setTimeoutInterval:timeoutSec
  
aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"
  
aRequest’s setValue:"AppleScript/Cocoa" forHTTPHeaderField:"User-Agent"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
set aConfig to NSURLSessionConfiguration’s defaultSessionConfiguration()
  
aConfig’s setRequestCachePolicy:(current application’s NSURLRequestReturnCacheDataElseLoad)
  
set aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(missing value)
  
set aTask to aSession’s dataTaskWithRequest:aRequest
  
  
aTask’s resume() –Start URL Session
  
  
repeat (10 * timeoutSec) times
    if (my drecF) = true then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
return my parseSessionResults()
end callRestGETAPIAndParseResults

on URLSession:tmpSession dataTask:tmpTask didReceiveData:tmpData
  (my retData)’s appendData:tmpData
end URLSession:dataTask:didReceiveData:

on URLSession:tmpSession dataTask:tmpTask didCompleteWithError:tmpError
  set (my drecF) to true
end URLSession:dataTask:didCompleteWithError:

on URLSession:tmpSession dataTask:tmpTask willCacheResponse:cacheRes completionHandler:aHandler
  set (my drecF) to true
end URLSession:dataTask:willCacheResponse:completionHandler:

on parseSessionResults()
  set resStr to NSString’s alloc()’s initWithData:(my retData) encoding:(NSUTF8StringEncoding)
  
set jsonString to NSString’s stringWithString:(resStr)
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
return aJsonDict
end parseSessionResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to filteredArray as {list, missing value}
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

AppleScript名:GET method REST API_wikipedia
— Created 2016-03-03 by Takaaki Naganoya
— 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aQueryKeyTitle to "Steve Jobs" –キーワードの正規化が必要("戦場の絆"→"機動戦士ガンダム 戦場の絆" )
set reqURLStr to "https://en.wikipedia.org/w/api.php"
set aRec to {action:"parse", page:aQueryKeyTitle, |prop|:"wikitext", format:"json"}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL, 10) of me
set aRESTres to json of aRes
set bRes to (aRESTres’s valueForKeyPath:"parse.wikitext.*") as string

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL, timeoutSec)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setTimeoutInterval:timeoutSec
  
aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"
  
aRequest’s setValue:"AppleScript/Cocoa" forHTTPHeaderField:"User-Agent"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to (dRes’s statusCode()) as integer
  
  
–Get Response Header
  
set resHeaders to (dRes’s allHeaderFields()) as record
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to filteredArray as list
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

Posted in Internet REST API URL | Tagged 10.13savvy 10.14savvy NSArray NSJSONSerialization NSMutableData NSMutableDictionary NSMutableURLRequest NSPredicate NSString NSURL NSURLComponents NSURLQueryItem NSURLSession NSURLSessionConfiguration NSUTF8StringEncoding | 2 Comments

Bundle IDで指定したアプリケーションを「隠す」

Posted on 2月 16, 2020 by Takaaki Naganoya

Bundle IDで指定したアプリケーションを隠すAppleScriptです。

アプリケーションの指定

macOS上のアプリケーションは一意に判別できるID「Bundle ID」を持っています。このBundle IDは各アプリケーションのバンドル内のInfo.plistに書かれています。

アプリケーションの名称については、ローカライズ名称を指定できるので、英語環境、フランス語環境、日本語環境で異なるアプリケーション名がFinder上で見えるようにできるので、名称ではなくBundle IDで区別するのが一般的です。

言語環境ごとに名称が変わる代表的なアプリケーションには、「リマインダー」(本当の名称はReminders)、「メモ」(本当の名称はNotes)、「写真」(本当の名称はPhotos)などがあります。

アプリケーションプロセスの隠し方

各アプリケーションのアプリケーションメニューに「隠す」というメニュー項目があり、これを実行することで隠すことができます。AppleScriptからこれに該当する操作は、System Eventsに対して指定のアプリケーションプロセスの可視属性をfalseに設定するというものです。

野蛮かつ強引にメニューを操作するという方法(GUI Scripting)も使えますが、速度や信頼性の面でおすすめしません。

AppleScript名:指定アプリケーションプロセスを隠す(System Events)
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/02/16
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

set pRes to hideAProcessByBUndleID("com.apple.Safari") of me

–指定プロセスを隠す
on hideAProcessByBUndleID(aBundleID)
  tell application "System Events"
    set targProcList to every process whose bundle identifier is aBundleID
    
if targProcList = {} then return false
    
    
set targProc to first item of targProcList
    
tell targProc
      set visible to false
    end tell
  end tell
  
return true
end hideAProcessByBUndleID

★Click Here to Open This Script 

macOS 10.10以降では通常のAppleScriptでCocoaの機能が使えるようになったため、こうしたOS側の機能を用いてもよいでしょう。


▲セキュリティダイアログの表示例

むしろ、macOS 10.14以降では各種アプリケーションへの初回命令時にはセキュリティ・ダイアログが表示されて止まるため、Cocoaの機能を利用できたほうがメリットが大きいといえるぐらいです。

AppleScript名:指定アプリケーションプロセスを隠す(Cocoa)
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/02/16
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set pRes to hideAProcessByBUndleID("com.apple.Safari") of me

–指定プロセスを隠す
on hideAProcessByBUndleID(aBundleID)
  set appArray to current application’s NSRunningApplication’s runningApplicationsWithBundleIdentifier:aBundleID
  
if appArray’s |count|() > 0 then
    set appItem to appArray’s objectAtIndex:0
    
set aRes to (appItem’s hide()) as boolean
    
return aRes
  else
    return false
  end if
end hideAProcessByBUndleID

★Click Here to Open This Script 

Posted in System | Tagged 10.14savvy 10.15savvy NSArray NSRunningApplication System Events | Leave a comment

iTunes Libraryの再生回数をジャンルごとに集計、ジャンル名名寄せ付き

Posted on 12月 2, 2019 by Takaaki Naganoya

iTunesLibrary.framework経由でiTunes/Music.appのライブラリのジャンルごとの再生回数を集計するAppleScriptです。

7,000曲程度入っているライブラリで、ジャンル名の名寄せも含め、開発環境(MacBook Pro Retina 2012, Core i7 2.66GHz@macOS 10.14.6)で0.7秒程度で終了します。2,500曲程度入っているMac mini 2014 Core i5 2.6GHz@macOS 10.15.1で0.6秒程度です。

macOS 10.14まで(iTunes.app)と、macOS 10.15以降(Music.app)でも同様にiTunesLibrary.framework経由でライブラリへのアクセスが行えます。

AppleScript名:iTunes Libraryの再生回数をジャンルごとに集計、ジャンル名名寄せ付き
— Created 2019-11-10 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "iTunesLibrary"
–https://developer.apple.com/reference/ituneslibrary/itlibmediaitem?language=objc

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSScanner : a reference to current application’s NSScanner
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSMutableCharacterSet : a reference to current application’s NSMutableCharacterSet

–ジャンル名寄せリスト(2要素から構成される2D List)
set genreNayoseList to {{"World", "ワールド"}, {"Anime", "アニメ"}, {"Electronic", "エレクトロニック"}, {"R&B/ソウル", "R&B/ソウル"}, {"Kayokyoku", "歌謡曲"}, {"Electronic", "エレクトロニック"}, {"Vocal", "ヴォーカル"}, {"Classical", "クラシック"}, {"Dance", "ダンス"}, {"Soundtrack", "サウンドトラック"}, {"Rock", "ロック"}}

set library to current application’s ITLibrary’s libraryWithAPIVersion:"1.0" |error|:(missing value)
if library is equal to missing value then return

set aRes1 to (library’s applicationVersion()) as string –>  "12.10.1.37" @ macOS 10.15
set aRes2 to (library’s apiMinorVersion()) –> 1
set aRes3 to (library’s apiMajorVersion()) –> 1

set playLists to library’s allPlaylists()
set gArray to library’s allMediaItems()’s genre
set aRes to countItemsByItsAppearance(gArray) of me

set bRes to genreNayoseAndUnify(aRes, genreNayoseList) of me
–> {{genreName:"サウンドトラック", numberOfTimes:1965}, {numberOfTimes:1209, genreName:"Podcast"}, {genreName:"ロック", numberOfTimes:1128}, {genreName:"クラシック", numberOfTimes:705}, {numberOfTimes:517, genreName:"ポップ"}, {genreName:"アニメ", numberOfTimes:533}, {numberOfTimes:383, genreName:"J-Pop"}, {numberOfTimes:292, genreName:"Pop"}, {numberOfTimes:279, genreName:"社会/文化"}, {numberOfTimes:252, genreName:missing value}, {genreName:"ワールド", numberOfTimes:246}, {numberOfTimes:187, genreName:"ジャズ"}, {genreName:"エレクトロニック", numberOfTimes:168}, {numberOfTimes:125, genreName:"R&B"}, {numberOfTimes:104, genreName:"ニューエイジ"}, {numberOfTimes:81, genreName:"Unclassifiable"}, {genreName:"歌謡曲", numberOfTimes:60}, {numberOfTimes:57, genreName:"Children’s"}, {numberOfTimes:54, genreName:"オルタナティブ"}, {numberOfTimes:38, genreName:"Holiday"}, {numberOfTimes:32, genreName:"Data"}, {numberOfTimes:31, genreName:"イージーリスニング"}, {genreName:"ヴォーカル", numberOfTimes:31}, {numberOfTimes:19, genreName:"iTunes U"}, {numberOfTimes:17, genreName:"フォーク"}, {numberOfTimes:15, genreName:"ブルース"}, {numberOfTimes:15, genreName:"ディズニー"}, {numberOfTimes:15, genreName:"シンガーソングライター"}, {numberOfTimes:14, genreName:"Easy Listening"}, {numberOfTimes:14, genreName:"ラテン"}, {numberOfTimes:14, genreName:"Electronica/Dance"}, {numberOfTimes:14, genreName:"個人ジャーナル"}, {genreName:"ダンス", numberOfTimes:12}, {numberOfTimes:10, genreName:"アクション/アドベンチャー"}, {numberOfTimes:9, genreName:"J-POP"}, {numberOfTimes:9, genreName:"New Age"}, {numberOfTimes:7, genreName:"演歌"}, {numberOfTimes:6, genreName:"少年"}, {numberOfTimes:6, genreName:"青年"}, {numberOfTimes:6, genreName:"キッズ/ファミリー"}, {numberOfTimes:5, genreName:"Video"}, {numberOfTimes:5, genreName:"プログラミング"}, {numberOfTimes:4, genreName:"ホリデー"}, {numberOfTimes:4, genreName:"カントリー"}, {numberOfTimes:4, genreName:"科学/医学"}, {numberOfTimes:3, genreName:"ビジネス"}, {numberOfTimes:3, genreName:"コメディ"}, {numberOfTimes:3, genreName:"Game Music"}, {numberOfTimes:3, genreName:"Latin"}, {genreName:"R&B/ソウル", numberOfTimes:5}, {numberOfTimes:2, genreName:"#NIPPONSEI @ IRC.MIRCX.COM"}, {numberOfTimes:2, genreName:"Technology"}, {numberOfTimes:2, genreName:"ヒップホップ/ ラップ"}, {numberOfTimes:2, genreName:"ヒップホップ/ラップ"}, {numberOfTimes:2, genreName:"日本"}, {numberOfTimes:2, genreName:"ドラマ"}, {numberOfTimes:1, genreName:"社会科学"}, {numberOfTimes:1, genreName:"コンピュータ/テクノロジー"}, {numberOfTimes:1, genreName:"Tech ニュース"}, {numberOfTimes:1, genreName:"科学/自然"}, {numberOfTimes:1, genreName:"その他"}, {numberOfTimes:1, genreName:"児童書フィクション"}, {numberOfTimes:1, genreName:"レゲエ"}, {numberOfTimes:1, genreName:"Lifestyle & Home"}, {numberOfTimes:1, genreName:"ホリデーミュージック"}, {numberOfTimes:1, genreName:"マネジメント/リーダーシップ"}, {numberOfTimes:1, genreName:"インストゥルメンタル"}, {numberOfTimes:1, genreName:"SF/ファンタジー"}, {numberOfTimes:1, genreName:"146"}, {numberOfTimes:1, genreName:"健康/フィットネス"}, {numberOfTimes:1, genreName:"148"}, {numberOfTimes:1, genreName:"NHK FM(東京)"}, {numberOfTimes:1, genreName:"Seattle Pacific University – Latin"}, {numberOfTimes:1, genreName:"チルドレン・ミュージック"}, {numberOfTimes:1, genreName:"名作"}, {numberOfTimes:1, genreName:"Folk"}}

–ジャンルのリストを出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to 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:(NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{"genreName", "numberOfTimes"})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to NSSortDescriptor’s sortDescriptorWithKey:"numberOfTimes" ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

on genreNayoseAndUnify(aList as list, genreNayoseList as list)
  set gList to FlattenList(genreNayoseList) of me
  
  
set didProc to {}
  
  
set a2List to {}
  
  
repeat with i in aList
    set aGenre to genreName of i
    
    
if (aGenre is in gList) and (aGenre is not in didProc) then
      
      
repeat with ii in genreNayoseList
        set jj to contents of ii
        
        
if aGenre is in jj then
          copy jj to {g1, g2}
          
          
          
if chkAlphabet(g1) of me = true then
            set targG to g2
            
set targG2 to g1
          else
            set targG to g1
            
set targG2 to g2
          end if
          
          
          
set s1Res to searchByGenreName(aList, targG) of me
          
set s2Res to searchByGenreName(aList, targG2) of me
          
          
          
set s3Res to addMutipleLists({s1Res, s2Res}) of me
          
          
set tmpClass to class of s3Res
          
if tmpClass = list then
            set s3Res to contents of first item of s3Res
          end if
          
          
set outRec to {genreName:targG, numberOfTimes:s3Res}
          
set the end of didProc to g1
          
set the end of didProc to g2
          
          
exit repeat
        end if
        
      end repeat
      
      
set the end of a2List to outRec
    else
      if (aGenre is not in didProc) then
        set the end of a2List to contents of i
      end if
    end if
  end repeat
  
  
return a2List
end genreNayoseAndUnify

on searchByGenreName(aList as list, aGenreName as string)
  set predicatesStr to "genreName == ’" & aGenreName & "’"
  
set anArray to (NSArray’s arrayWithArray:aList)
  
set aPred to (NSPredicate’s predicateWithFormat:predicatesStr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred)
  
if (bRes as list) = {} then return {}
  
  
set bbRes to first item of bRes
  
return (numberOfTimes of bbRes) as list
end searchByGenreName

on addMutipleLists(s1List)
  script spdAdd
    property s1List : {}
    
property s3List : {}
  end script
  
  
copy s1List to (s1List of spdAdd) –init
  
  
set s1Len to length of first item of (s1List of spdAdd)
  
set (s3List of spdAdd) to makeZero1DList(s1Len, 0) of me
  
  
repeat with i in (s1List of spdAdd)
    set tmpLen to length of i
    
if tmpLen is not equal to s1Len then return false
    
    
repeat with ii from 1 to s1Len
      set tmp1 to contents of item ii of (s3List of spdAdd)
      
set tmp2 to contents of item ii of i
      
      
set item ii of (s3List of spdAdd) to (tmp1 + tmp2)
    end repeat
  end repeat
  
  
return (s3List of spdAdd)
end addMutipleLists

–指定要素を指定回数追加したリストを作成する
on makeZero1DList(itemMax, itemElem)
  set allData to {}
  
repeat itemMax times
    set the end of allData to itemElem
  end repeat
  
return allData
end makeZero1DList

–By Paul Berkowitz
–2009年1月27日 2:24:08:JST
–Re: Flattening Nested Lists
on FlattenList(aList)
  set oldDelims to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {"????"}
  
set aString to aList as text
  
set aList to text items of aString
  
set AppleScript’s text item delimiters to oldDelims
  
return aList
end FlattenList

— アルファベットのみか調べて返す
on chkAlphabet(checkString)
  set aStr to NSString’s stringWithString:checkString
  
set allCharSet to NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "a", 26))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "A", 26))
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabet

on chkCompareString:checkString baseString:baseString
  set aScanner to NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

★Click Here to Open This Script 

Posted in list | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSArray NSCountedSet NSDictionary NSMutableArray NSMutableCharacterSet NSPredicate NSScanner NSSortDescriptor NSString | Leave a comment

指定のフォントとサイズに該当するテキストを抽出する v3

Posted on 12月 1, 2019 by Takaaki Naganoya

指定のRTFファイルから書式情報を取得し、フォント名とフォントサイズのペアをスタイルを反映させたポップアップメニューで選択。RTFの内容を解析したのち、ダイアログが表示されます(ここの所要時間は読ませるRTFのサイズ次第)。ポップアップメニューで書式を選ぶと、すぐさま抽出した箇所をすべてまとめて表示します。

–> Watch Demo Movie

–> Download Script Bundle with Sample data

ダイアログ表示前に書式ごとの抽出を完了し、個別のTabViewに抽出後の文字データを展開しておくので、ポップアップメニューから選択すると、展開ずみのデータを入れてあるTabViewに表示を切り替えるだけ。切り替え時に計算は行わないため、すぐに抽出ずみの文字データが表示されるという寸法です。

今回は短いサンプルデータを処理してみましたが、サンプル抽出時にあらかじめ範囲指定するなどして、データ処理規模を限定することで処理時間を稼ぐこともできることでしょう。

テキストエディットでオープン中のRTF書類のパスを取得して、それを処理対象にしてもいいでしょう。

ただし、やっつけで作ったのでスクリプトエディタ上でCommand-Control-Rで実行しないと動きません(メインスレッド実行をプログラムで強制しても、途中でクラッシュしてしまいます)。それほど時間もかけずに作ったやっつけプログラムなので、あまり原因追求も行えずそのままです。

AppleScript名:アラートダイアログ+Tab View v3.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/16
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use rtfLib : script "readStyledTextLib"
use parseLib : script "parseAttrLib"

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 NSMenu : a reference to current application’s NSMenu
property NSArray : a reference to current application’s NSArray
property NSTabView : a reference to current application’s NSTabView
property NSPredicate : a reference to current application’s NSPredicate
property NSTextView : a reference to current application’s NSTextView
property NSMenuItem : a reference to current application’s NSMenuItem
property NSTabViewItem : a reference to current application’s NSTabViewItem
property NSPopUpButton : a reference to current application’s NSPopUpButton
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSKernAttributeName : a reference to current application’s NSKernAttributeName
property NSLigatureAttributeName : a reference to current application’s NSLigatureAttributeName
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSStrokeWidthAttributeName : a reference to current application’s NSStrokeWidthAttributeName
property NSUnderlineStyleAttributeName : a reference to current application’s NSUnderlineStyleAttributeName
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName

property TabPosition : 4 —0=Top, 1=Left, 2=Bottom, 3=Right, 4=None

property returnCode : 0

property aTabV : missing value
property selectedNum : {}

set aFile to choose file of type {"public.rtf"}

set aStyledStr to readRTFfile(aFile) of rtfLib
set paramObj to {myMessage:"Select Style", mySubMessage:"Select style you want to filter", viewWidth:800, viewHeight:400, myStyledStr:aStyledStr}

my dispTabViewWithAlertdialog:paramObj –for debug
–my performSelectorOnMainThread:"dispTabViewWithAlertdialog:" withObject:paramObj waitUntilDone:true
return selectedNum

on dispTabViewWithAlertdialog:paramObj
  –Receive Parameters
  
set aMainMes to (myMessage of paramObj) as string –Main Message
  
set aSubMes to (mySubMessage of paramObj) as string –Sub Message
  
set aWidth to (viewWidth of paramObj) as integer –TextView width
  
set aHeight to (viewHeight of paramObj) as integer –TextView height
  
set aStyledStr to (myStyledStr of paramObj)
  
  
set attrResTmp to getAttributeRunsFromAttrString(aStyledStr) of parseLib
  
set attrList to attributes of attrResTmp
  
set menuStyle to menuList of attrResTmp
  
  
set selectedNum to {}
  
  
set aView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
  
–Ppopup Buttonをつくる
  
set a1Button to NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aHeight – 20, 400, 20)) pullsDown:false
  
a1Button’s removeAllItems()
  
  
–WYSIWHG popup menuをつくる
  
set a1Menu to NSMenu’s alloc()’s init()
  
repeat with i from 1 to (length of menuStyle)
    set {tmpFont, tmpSize} to contents of item i of menuStyle
    
set aTitle to (tmpFont & " " & tmpSize as string) & " point"
    
set aMenuItem to (NSMenuItem’s alloc()’s initWithTitle:aTitle action:"actionHandler:" keyEquivalent:"")
    
    
set attributedTitle to makeRTFfromParameters(aTitle, tmpFont, tmpSize, NSColor’s blackColor()) of me
    
    (
aMenuItem’s setEnabled:true)
    (
aMenuItem’s setTarget:me)
    (
aMenuItem’s setTag:(i as string))
    (
aMenuItem’s setAttributedTitle:(attributedTitle))
    (
a1Menu’s addItem:aMenuItem)
  end repeat
  
  
–Ppopup Buttonにmenuをつける
  
a1Button’s setMenu:a1Menu
  
  
–Make Tab View
  
set aTabV to NSTabView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight – 30))
  
aTabV’s setTabViewType:(TabPosition)
  
  
  
–TabViewに中身を入れる
  
repeat with i from 1 to (length of menuStyle)
    set {tmpFont, tmpSize} to contents of item i of menuStyle
    
set tmpKey to (tmpFont as string) & "/" & (tmpSize as string)
    
set tmpArry to filterRecListByLabel1(attrList, "styleKey2 == ’" & tmpKey & "’") of me
    
set tmpStr to (tmpArry’s valueForKeyPath:"stringVal") as list
    
    
set aTVItem to (NSTabViewItem’s alloc()’s initWithIdentifier:(i as string))
    (
aTVItem’s setLabel:(i as string))
    
    
    
set aTextView to (NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth – 10, aHeight – 10)))
    (
aTextView’s setRichText:true)
    
set tmpAttr to makeRTFfromParameters(tmpStr as string, tmpFont, tmpSize, NSColor’s blackColor()) of me
    (
aTextView’s textStorage()’s appendAttributedString:tmpAttr)
    
    (
aTVItem’s setView:aTextView)
    
    (
aTabV’s addTabViewItem:aTVItem)
  end repeat
  
  
  
aView’s setSubviews:{a1Button, aTabV}
  
aView’s setNeedsDisplay:true
  
  
  
— set up alert
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    –for Messages
    
its setMessageText:(aMainMes)
    
its setInformativeText:(aSubMes)
    
    
–for Buttons
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
    
–Add Accessory View
    
its setAccessoryView:(aView)
    
    
–for Help Button
    
its setShowsHelp:(true)
    
its setDelegate:(me)
  end tell
  
  
  
— 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 selectedNum to contents of item ((a1Button’s indexOfSelectedItem()) + 1) of menuStyle
end dispTabViewWithAlertdialog:

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

on alertShowHelp:aNotification
  display dialog "Help Me!" buttons {"OK"} default button 1 with icon 1
  
return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする
end alertShowHelp:

–Popup Action Handler
on actionHandler:sender
  set aTag to tag of sender as integer
  
aTabV’s selectTabViewItemAtIndex:(aTag – 1)
end actionHandler:

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, aFontName as string, aFontSize as real, aColor)
  –フォント
  
set aVal1 to NSFont’s fontWithName:(aFontName) |size|:aFontSize
  
set aKey1 to (NSFontAttributeName)
  
  
–色
  
set aVal2 to aColor
  
set aKey2 to (NSForegroundColorAttributeName)
  
  
–カーニング
  
set aVal3 to 0.0
  
set akey3 to (NSKernAttributeName)
  
  
–アンダーライン
  
set aVal4 to 0
  
set akey4 to (NSUnderlineStyleAttributeName)
  
  
–リガチャ
  
–set aVal5 to 2 –全てのリガチャを有効にする
  
–set akey5 to ( NSLigatureAttributeName)
  
  
–枠線(アウトライン)
  
–set aVal6 to outlineNum
  
–set akey6 to ( NSStrokeWidthAttributeName)
  
  
  
set keyList to {aKey1, aKey2, akey3, akey4}
  
set valList to {aVal1, aVal2, aVal3, aVal4}
  
set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
end makeRTFfromParameters

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList as list, aPredicate as string)
  set aArray to NSArray’s arrayWithArray:aRecList
  
set aPredicate to NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to filteredArray
  
return bList
end filterRecListByLabel1

★Click Here to Open This Script 

Posted in Color dialog Font GUI Require Control-Command-R to run RTF | Tagged 10.14savvy 10.15savvy NSAlert NSArray NSColor NSFont NSFontAttributeName NSForegroundColorAttributeName NSKernAttributeName NSLigatureAttributeName NSMenu NSMenuItem NSMutableAttributedString NSMutableDictionary NSPopUpButton NSPredicate NSRunningApplication NSStrokeWidthAttributeName NSTabView NSTabViewItem NSTextView NSUnderlineStyleAttributeName NSView | 1 Comment

ジャンル名名寄せ v2

Posted on 11月 27, 2019 by Takaaki Naganoya

iTunes/Musicのライブラリに入っている楽曲データのジャンル分けのゆらぎ吸収を行う、ジャンル名のいわゆる「名寄せ」を行うAppleScriptです。


▲1つのアルバム中でもジャンル名に表記ゆれがあるのは勘弁してほしい

iTunes/Musicライブラリ中の全楽曲の最終再生日時を24時間の各時で集計を行い、ジャンルごとに再生時間に偏りがないかどうかを分析するため、さらにジャンルごろに集計してみました。

「名寄せ」(なよせ)は昔から情報処理に見られる泥臭い地道な処理で、データ上の表記ゆれを吸収するための処理です。地名などの固有名詞で多く見られます(念のためにWikipediaで調べたら、異なる業界では割とブラックな言葉として用いられている模様)。

このジャンル名が英語で入っていたり日本語で入っていたりと、ゆらぎが発生しているので、同様のジャンル名であれば同じものとして合算して集計してみました。

また、英語で表記されたジャンル名ではなく日本語で表記されたものを採用(=すべてアルファベットで書かれたものを不採用)しています。まだ、それほどいじめ抜いていない(自分の環境でしかテストしていない)ので、ジャンル名の名寄せリストはもう少し鍛えておく必要があるものと思っています。

AppleScript名:ジャンル名名寄せ v2
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/24
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5" — El Capitan (10.11) or later
use framework "Foundation"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSScanner : a reference to current application’s NSScanner
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableCharacterSet : a reference to current application’s NSMutableCharacterSet

–ジャンル名寄せリスト(2要素から構成される2D List)
set genreNayoseList to {{"World", "ワールド"}, {"Anime", "アニメ"}, {"Electronic", "エレクトロニック"}, {"R&B/ソウル", "R&B/ソウル"}, {"Kayokyoku", "歌謡曲"}, {"Electronic", "エレクトロニック"}, {"Vocal", "ヴォーカル"}, {"Classical", "クラシック"}, {"Dance", "ダンス"}, {"Soundtrack", "サウンドトラック"}}

–名寄せ対象リスト(genreNameを名寄せ)
set aList to {{genreName:"シンガーソングライター", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 1, 5, 0, 0, 0, 0}}, {genreName:"ロック", playingList:{2, 2, 0, 0, 0, 1, 0, 0, 4, 3, 9, 5, 11, 11, 22, 19, 48, 17, 28, 15, 2, 11, 13, 1}}, {genreName:"Soundtrack", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"World", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"演歌", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ホリデーミュージック", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ディズニー", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0}}, {genreName:"J-Pop", playingList:{4, 0, 1, 1, 2, 1, 5, 3, 2, 3, 27, 19, 14, 26, 17, 20, 28, 32, 40, 25, 10, 10, 6, 6}}, {genreName:"ヒップホップ/ ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Electronic", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"アニメ", playingList:{7, 1, 0, 0, 7, 2, 0, 0, 0, 9, 23, 45, 17, 10, 17, 50, 48, 65, 53, 27, 12, 16, 23, 45}}, {genreName:"ポップ", playingList:{3, 0, 0, 0, 0, 0, 0, 3, 1, 6, 2, 9, 7, 0, 11, 7, 8, 10, 7, 5, 3, 1, 0, 1}}, {genreName:"Kayokyoku", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"ヒップホップ/ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Dance", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"ダンス", playingList:{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0}}, {genreName:"R&B/ソウル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Anime", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0}}, {genreName:"チルドレン・ミュージック", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"Classical", playingList:{1, 1, 0, 0, 2, 1, 8, 1, 0, 0, 11, 5, 2, 0, 4, 9, 13, 2, 2, 0, 0, 0, 0, 0}}, {genreName:"ニューエイジ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0}}, {genreName:"Pop", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"ヴォーカル", playingList:{0, 0, 0, 0, 0, 1, 0, 0, 0, 6, 7, 0, 0, 1, 6, 0, 0, 3, 1, 0, 3, 0, 0, 0}}, {genreName:"サウンドトラック", playingList:{23, 31, 0, 0, 0, 2, 1, 0, 7, 7, 27, 29, 26, 25, 28, 48, 66, 76, 56, 16, 8, 31, 6, 10}}, {genreName:"歌謡曲", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 2, 4, 5, 5, 4, 6, 6, 7, 1, 0, 0, 0}}, {genreName:"ホリデー", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}}, {genreName:"Folk", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}}, {genreName:"ジャズ", playingList:{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 16, 7, 3, 9, 0, 5, 10, 20, 10, 2, 1, 0, 0}}, {genreName:"エレクトロニック", playingList:{0, 1, 3, 0, 0, 0, 0, 1, 4, 13, 8, 13, 2, 18, 6, 13, 10, 16, 25, 7, 8, 0, 2, 10}}, {genreName:"ワールド", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 5, 5, 7, 6, 0, 0, 0, 0, 0}}, {genreName:"インストゥルメンタル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"Vocal", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"クラシック", playingList:{0, 0, 1, 1, 0, 3, 2, 0, 4, 3, 13, 11, 24, 1, 21, 14, 24, 38, 21, 7, 5, 8, 4, 5}}, {genreName:"オルタナティブ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 0, 6, 9, 0}}, {genreName:"R&B/ソウル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}

set bList to genreNayoseAndUnify(aList, genreNayoseList) of me
–> {{genreName:"シンガーソングライター", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 1, 5, 0, 0, 0, 0}}, {genreName:"ロック", playingList:{2, 2, 0, 0, 0, 1, 0, 0, 4, 3, 9, 5, 11, 11, 22, 19, 48, 17, 28, 15, 2, 11, 13, 1}}, {genreName:"サウンドトラック", playingList:{23, 31, 0, 0, 0, 2, 1, 0, 7, 7, 28, 29, 26, 25, 28, 48, 66, 77, 56, 16, 8, 31, 6, 10}}, {genreName:"ワールド", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 5, 5, 8, 6, 0, 0, 0, 0, 0}}, {genreName:"演歌", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ホリデーミュージック", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ディズニー", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0}}, {genreName:"J-Pop", playingList:{4, 0, 1, 1, 2, 1, 5, 3, 2, 3, 27, 19, 14, 26, 17, 20, 28, 32, 40, 25, 10, 10, 6, 6}}, {genreName:"ヒップホップ/ ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"エレクトロニック", playingList:{0, 1, 3, 0, 0, 0, 0, 1, 4, 13, 8, 13, 2, 19, 6, 13, 10, 16, 25, 7, 8, 0, 2, 10}}, {genreName:"アニメ", playingList:{7, 1, 0, 0, 7, 2, 0, 0, 0, 9, 23, 52, 17, 10, 17, 50, 49, 67, 54, 27, 12, 16, 23, 45}}, {genreName:"ポップ", playingList:{3, 0, 0, 0, 0, 0, 0, 3, 1, 6, 2, 9, 7, 0, 11, 7, 8, 10, 7, 5, 3, 1, 0, 1}}, {genreName:"歌謡曲", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 2, 4, 5, 5, 4, 6, 7, 7, 1, 0, 0, 0}}, {genreName:"ヒップホップ/ラップ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"ダンス", playingList:{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 0}}, {genreName:"R&B/ソウル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"チルドレン・ミュージック", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {genreName:"クラシック", playingList:{1, 1, 1, 1, 2, 4, 10, 1, 4, 3, 24, 16, 26, 1, 25, 23, 37, 40, 23, 7, 5, 8, 4, 5}}, {genreName:"ニューエイジ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12, 0, 0, 0, 0}}, {genreName:"Pop", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, {genreName:"ヴォーカル", playingList:{0, 0, 0, 0, 0, 1, 0, 0, 0, 6, 7, 0, 0, 1, 6, 1, 0, 3, 1, 0, 3, 0, 0, 0}}, {genreName:"ホリデー", playingList:{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}}, {genreName:"Folk", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}}, {genreName:"ジャズ", playingList:{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 16, 7, 3, 9, 0, 5, 10, 20, 10, 2, 1, 0, 0}}, {genreName:"インストゥルメンタル", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}}, {genreName:"オルタナティブ", playingList:{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 0, 6, 9, 0}}}

on genreNayoseAndUnify(aList as list, genreNayoseList as list)
  set gList to FlattenList(genreNayoseList) of me
  
  
set didProc to {}
  
  
set a2List to {}
  
  
repeat with i in aList
    set aGenre to genreName of i
    
    
if (aGenre is in gList) and (aGenre is not in didProc) then
      
      
repeat with ii in genreNayoseList
        set jj to contents of ii
        
        
if aGenre is in jj then
          copy jj to {g1, g2}
          
          
          
if chkAlphabet(g1) of me = true then
            set targG to g2
            
set targG2 to g1
          else
            set targG to g1
            
set targG2 to g2
          end if
          
          
set s1Res to searchByGenreName(aList, targG) of me
          
set s2Res to searchByGenreName(aList, targG2) of me
          
          
set s3Res to addMutipleLists({s1Res, s2Res}) of me
          
          
set outRec to {genreName:targG, playingList:s3Res}
          
set the end of didProc to g1
          
set the end of didProc to g2
          
          
exit repeat
        end if
        
      end repeat
      
      
set the end of a2List to outRec
    else
      if (aGenre is not in didProc) then
        set the end of a2List to contents of i
      end if
    end if
  end repeat
  
  
return a2List
end genreNayoseAndUnify

on searchByGenreName(aList as list, aGenreName as string)
  set predicatesStr to "genreName == ’" & aGenreName & "’"
  
set anArray to (NSArray’s arrayWithArray:aList)
  
set aPred to (NSPredicate’s predicateWithFormat:predicatesStr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred)
  
  
set bbRes to first item of bRes
  
return (playingList of bbRes) as list
end searchByGenreName

on addMutipleLists(s1List)
  script spdAdd
    property s1List : {}
    
property s3List : {}
  end script
  
  
copy s1List to (s1List of spdAdd) –init
  
  
set s1Len to length of first item of (s1List of spdAdd)
  
set (s3List of spdAdd) to makeZero1DList(s1Len, 0) of me
  
  
repeat with i in (s1List of spdAdd)
    set tmpLen to length of i
    
if tmpLen is not equal to s1Len then return false
    
    
repeat with ii from 1 to s1Len
      set tmp1 to contents of item ii of (s3List of spdAdd)
      
set tmp2 to contents of item ii of i
      
      
set item ii of (s3List of spdAdd) to (tmp1 + tmp2)
    end repeat
  end repeat
  
  
return (s3List of spdAdd)
end addMutipleLists

–指定要素を指定回数追加したリストを作成する
on makeZero1DList(itemMax, itemElem)
  set allData to {}
  
repeat itemMax times
    set the end of allData to itemElem
  end repeat
  
return allData
end makeZero1DList

–By Paul Berkowitz
–2009年1月27日 2:24:08:JST
–Re: Flattening Nested Lists
on FlattenList(aList)
  set oldDelims to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {"????"}
  
set aString to aList as text
  
set aList to text items of aString
  
set AppleScript’s text item delimiters to oldDelims
  
return aList
end FlattenList

— アルファベットのみか調べて返す
on chkAlphabet(checkString)
  set aStr to NSString’s stringWithString:checkString
  
set allCharSet to NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "a", 26))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "A", 26))
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabet

on chkCompareString:checkString baseString:baseString
  set aScanner to NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

★Click Here to Open This Script 

Posted in list Record | Tagged 10.13savvy 10.14savvy 10.15savvy NSArray NSMutableCharacterSet NSPredicate NSScanner NSString | Leave a comment

テキストエディットで指定のフォントとサイズに該当するテキストを抽出する

Posted on 11月 22, 2019 by Takaaki Naganoya

macOS標準搭載のテキストエディタ、「テキストエディット」(TextEdit)をコントロールして、最前面のウィンドウの書類から、指定のフォント名とサイズに該当するテキストを抽出するAppleScriptです。

テキストエディットはApple純正のアプリケーションの割にはAppleScriptサポート機能がよくできていて、選択範囲(selection)を取得できないことをのぞけば、けっこう使えます。

テキストエディットのAppleScript系の機能で最も重要なものは、書類からの書式取得機能(attribute run)です。


▲最前面の書類がリッチテキストフォーマットになっている必要があります


▲Github上のGPUImageのフィルタ紹介文からコピペしてきた文章


▲この書類のこのレベルの見出しだけを抽出したい場合にはHelvetica Neue Bold 20pointを指定


▲この書類のこのレベルの見出しだけを抽出したい場合にはHelvetica Neue Bold 16pointを指定

Webブラウザなどからコピペしてきたスタイル付きテキストから、特定のフォントや文字サイズの箇所を抽出したい場合に使います。

実行すると、リッチテキストで使用されているフォント名とサイズの一覧を生成し、ダイアログでフォント名の選択、

そして、文字サイズの選択を実行。

すると、該当箇所のみ抽出して返します。さらに改行で区切ったテキストに変換するとかいった処理を行なってもよいでしょう。

RTFのファイルを処理するのであれば、テキストエディットを使わなくてもCocoaの機能を用いて同様の処理を行えますが、割と書き捨てレベルの瑣末なScriptなのでテキストエディット経由で書式情報を取得するように組んでみました。

AppleScript名:指定のフォントとサイズに該当するテキストを抽出する
— Created 2019-11-22 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

script spd
  property fontList : {}
  
property fontSizes : {}
  
property textList : {}
  
property outList : {}
end script

set (outList of spd) to {}
set (fontList of spd) to {}
set (fontSizes of spd) to {}
set (textList of spd) to {}

–TextEdit書類から書式情報を根こそぎ取得
tell application "TextEdit"
  tell text of front document
    set (fontList of spd) to font of every attribute run –フォント名
    
set (fontSizes of spd) to size of every attribute run –文字サイズ
    
set (textList of spd) to character of every attribute run –文字
  end tell
end tell

–取得したフォント名一覧から重複部分を除去してどれを対象にするかユーザーに問い合わせ
set fRes to uniquify1DList((fontList of spd), true) of me
set targFont to choose from list fRes
set aFont to contents of first item of targFont

–取得したフォントサイズ一覧から重複部分を除去してどれを対象にするかユーザーに問い合わせ
set sRes to uniquify1DList((fontSizes of spd), true) of me
set targSize to choose from list sRes
set aSize to contents of first item of targSize

–取得した条件にもとづいて、根こそぎ取得した書式と照合を行いつつループ
repeat with i from 1 to length of (fontList of spd)
  set tmpFont to contents of item i of (fontList of spd)
  
set tmpSize to contents of item i of (fontSizes of spd)
  
  
–文字サイズをざっくり判定することで「大、中、小」といったおおまかな切り分けも可能
  
if {tmpFont, tmpSize} = {aFont, aSize} then
    set tmpCon to (contents of item i of (textList of spd)) as string
    
if tmpCon does not contain "•" then –ごみ取り
      set the end of (outList of spd) to tmpCon
    end if
  end if
end repeat

return (outList of spd)

–1D/2D Listをユニーク化
on uniquify1DList(theList as list, aBool as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
if aBool = true then
    return bArray as list
  else
    return bArray
  end if
end uniquify1DList

★Click Here to Open This Script 

Posted in Font list RTF Text | Tagged 10.13savvy 10.14savvy 10.15savvy NSArray TextEdit | Leave a comment

指定アプリケーションの指定ロケールのフォルダ内の該当キーワードを含むstringsファイル情報を抽出する

Posted on 11月 13, 2019 by Takaaki Naganoya

Bundle IDで指定したアプリケーションからすべてのLocaleを取得し、ダイアログ選択したLocale内のすべてのstringsファイルを読み込んでNSDictionary化して指定のキーワードを含むものを抽出するAppleScriptです。

指定キーワードが含まれていた場合には、

キー, 値, stringsファイルのパス

をリストで返します。調査のための下調べを行うものです。

AppleScript名:指定アプリケーションの指定ロケールのフォルダ内の該当キーワードを含むstringsファイル情報を抽出する
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSBundle : a reference to current application’s NSBundle
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSWorkspace : a reference to current application’s NSWorkspace
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants

set targID to "com.apple.Finder"
set targWords to "のコピー"

set bRes to getLocalizationsFromBundleID(targID) of me
–>  {"de", "he", "en_AU", "ar", "el", "ja", "en", "uk", "es_419", "zh_CN", "es", "da", "it", "sk", "pt_PT", "ms", "sv", "cs", "ko", "Base", "no", "hu", "zh_HK", "tr", "pl", "zh_TW", "en_GB", "vi", "ru", "fr_CA", "fr", "fi", "id", "nl", "th", "pt", "ro", "hr", "hi", "ca"}

–Select Localization
set curLang to first item of (choose from list bRes)

–Get All strings file path within a bundle
set aPath to retPathFromBundleID(targID) of me
set aPath to aPath & "/Contents/Resources/" & curLang & ".lproj"
set sList to getFilepathListByUTI(aPath, "com.apple.xcode.strings-text", "POSIX") of me

–Get Every Key & Value pair then filter target keyword
set aMDict to NSMutableDictionary’s new()
set hitList to {}
repeat with ii in sList
  set jj to contents of ii
  
set aDict to (NSDictionary’s alloc()’s initWithContentsOfFile:jj)
  (
aMDict’s addEntriesFromDictionary:aDict)
  
  
set kList to aMDict’s allKeys() as list
  
set vList to aMDict’s allValues() as list
  
  
set kCount to 1
  
repeat with i in vList
    set j to contents of i
    
if j contains targWords then
      set the end of hitList to {contents of item kCount of kList, j, jj}
    end if
    
set kCount to kCount + 1
  end repeat
  
end repeat

return hitList
–> {{"PW5_V2", "^0項目のコピーの準備中", "/System/Library/CoreServices/Finder.app/Contents/Resources/Japanese.lproj/Localizable.strings"}, …}

on getLocalizationsFromBundleID(aBundleID)
  set aRes to retPathFromBundleID(aBundleID) of me
  
if aRes = false then error "Wrong Bundle ID."
  
return getSpecifiedAppFilesLocalizationListWithDuplication(aRes) of me
end getLocalizationsFromBundleID

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を許容
on getSpecifiedAppFilesLocalizationListWithDuplication(appPOSIXpath)
  set aURL to (|NSURL|’s fileURLWithPath:appPOSIXpath)
  
set aBundle to NSBundle’s bundleWithURL:aURL
  
set locList to aBundle’s localizations()
  
return locList as list
end getSpecifiedAppFilesLocalizationListWithDuplication

on retPathFromBundleID(aBundleID)
  set aURL to NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
if aURL = missing value then return false –Error
  
return aURL’s |path|() as string
end retPathFromBundleID

on getFilepathListByUTI(aFolPOSIX, aUTI as string, aFileType as string)
  script spdFile
    property urlList : {}
  end script
  
  
if aFileType is not in {"file", "POSIX"} then return {}
  
  
set aFM to NSFileManager’s defaultManager()
  
set aFolExt to (aFM’s fileExistsAtPath:aFolPOSIX isDirectory:true) as boolean
  
if aFolExt = false then return {} –フォルダ自体が存在しなければヌルリストを返す
  
  
set aURL to |NSURL|’s fileURLWithPath:aFolPOSIX
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer)
  
set urlArray to (aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value))
  
if urlArray = missing value then return {}
  
  
set (urlList of spdFile) to urlArray as list
  
set newList to {}
  
  
repeat with i in (urlList of spdFile)
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, aUTI)
    
    
if utiRes is not equal to {} then
      if aFileType = "POSIX" then
        set the end of newList to j
      else if aFileType = "file" then
        set the end of newList to POSIX file j
      end if
    end if
    
  end repeat
  
  
return newList
end getFilepathListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

★Click Here to Open This Script 

Posted in file File path UTI | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy Finder NSArray NSBundle NSDictionary NSDirectoryEnumerationSkipsHiddenFiles NSDirectoryEnumerationSkipsPackageDescendants NSFileManager NSMutableDictionary NSPredicate NSURL NSURLTypeIdentifierKey NSWorkspace | Leave a comment

指定アプリケーション内の全Localeの指定stringsファイル内の指定キーの値を取得する

Posted on 11月 13, 2019 by Takaaki Naganoya

Bundle IDで指定したアプリケーションのバンドル中(/Contents/Resources/)のすべてのLocalizationに存在する指定のstringsファイル内で、指定キーの値を取得するAppleScriptです。

macOSの日本語環境では、Finder上でコピーしたファイルのファイル名の後方に「のコピー」といった文字列が追記されますが、他のLocaleでどのような文字列が使用されているか調査するために作成したものです。

AppleScript名:指定アプリケーション内の全Localeの指定stringsファイル内の指定キーの値を取得する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/12
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSBundle : a reference to current application’s NSBundle
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSWorkspace : a reference to current application’s NSWorkspace
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

set targID to "com.apple.Finder"
set targFile to "Localizable.strings"
set targKey to "N4_V2"
set vList to getAValInASpecifiedStringFileOfAllLocaleInABundle(targID, targFile, targKey) of me
–> {{"he", "עותק של ^=1 ^=0"}, {"en_AU", "^=1 copy ^=0"}, {"ar", "نسخة ^=0 من ^=1"}, {"el", "^=1 αντίγραφο ^=0"}, {"uk", "^=1 копія ^=0"}, {"English", "^=1 copy ^=0"}, {"es_419", "Copia de ^=1 ^=0"}, {"zh_CN", "^=1的副本 ^=0"}, {"Dutch", "^=1 kopie ^=0"}, {"da", "^=1-kopi ^=0"}, {"sk", "^=1 – kópia ^=0"}, {"pt_PT", "^=1 – cópia ^=0"}, {"German", "^=1 Kopie ^=0"}, {"ms", "salinan ^=0 ^=1"}, {"sv", "^=1 (kopia ^=0)"}, {"cs", "^=1 (kopie ^=0)"}, {"ko", "^=1 복사본 ^=0"}, {"no", "^=1-kopi ^=0"}, {"hu", "^=1 másolat ^=0"}, {"zh_HK", "^=1 副本 ^=0"}, {"Spanish", "^=1 copia ^=0"}, {"tr", "^=1 kopyası ^=0"}, {"pl", "^=1-kopia ^=0"}, {"zh_TW", "^=1 拷貝 ^=0"}, {"en_GB", "^=1 copy ^=0"}, {"French", "^=1 copie ^=0"}, {"vi", "Bản sao ^=1 ^=0"}, {"ru", "^=1 — копия ^=0"}, {"fr_CA", "^=1 copie ^=0"}, {"fi", "^=1 kopio ^=0"}, {"id", "Salinan ^=1 ^=0"}, {"th", "^=1 สำเนา ^=0"}, {"pt", "^=1 – cópia ^=0"}, {"ro", "^=1 – copie ^=0"}, {"Japanese", "^=1のコピー^=0"}, {"hr", "^=1 kopija ^=0"}, {"hi", "^=1 कॉपी ^=0"}, {"Italian", "^=1 copia ^=0"}, {"ca", "^=1 còpia ^=0"}}

on getAValInASpecifiedStringFileOfAllLocaleInABundle(targID, targFile, targKey)
  –Get App Path
  
set aPath to retPathFromBundleID(targID) of me
  
  
–Get all Localizations
  
set bRes to getLocalizationsFromBundleID(targID) of me
  
  
set hitList to {}
  
  
–Loop with Localizations in an application bundle
  
repeat with iii in bRes
    set jjj to contents of iii
    
set allPath to aPath & "/Contents/Resources/" & jjj & ".lproj/" & targFile
    
set aDict to (NSDictionary’s alloc()’s initWithContentsOfFile:allPath)
    
if aDict is not equal to missing value then
      set aVal to (aDict’s valueForKeyPath:(targKey))
      
if aVal is not equal to missing value then
        set the end of hitList to {jjj, aVal as string}
      end if
    end if
  end repeat
  
  
return hitList
end getAValInASpecifiedStringFileOfAllLocaleInABundle

on getLocalizationsFromBundleID(aBundleID)
  set aRes to retPathFromBundleID(aBundleID) of me
  
if aRes = false then error "Wrong Bundle ID."
  
return getSpecifiedAppFilesLocalizationListWithDuplication(aRes) of me
end getLocalizationsFromBundleID

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を許容
on getSpecifiedAppFilesLocalizationListWithDuplication(appPOSIXpath)
  set aURL to (|NSURL|’s fileURLWithPath:appPOSIXpath)
  
set aBundle to NSBundle’s bundleWithURL:aURL
  
set locList to aBundle’s localizations()
  
return locList as list
end getSpecifiedAppFilesLocalizationListWithDuplication

on retPathFromBundleID(aBundleID)
  set aURL to NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
if aURL = missing value then return false –Error
  
return aURL’s |path|() as string
end retPathFromBundleID

★Click Here to Open This Script 

Posted in System Text | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy Finder NSArray NSBundle NSDictionary NSFileManager NSMutableDictionary NSPredicate NSURL NSURLTypeIdentifierKey NSWorkspace | Leave a comment

CotEditorの最前面のドキュメントの選択範囲を伏せ字に

Posted on 10月 22, 2019 by Takaaki Naganoya

CotEditorの最前面のドキュメントの選択範囲を、簡易形態素解析ルーチンeasyJparseを用いて、いい感じに伏せ字にするAppleScriptです。

–> Download makeSelectionToFuseji(Code-Signed AppleScript applet with libraries in its bundle, co-work with CotEditor)

easyJparseは日本語のコマンド解析用にでっちあげた作った超簡易形態素解析プログラムです。単語(形態素)ごとに分割しますが、品詞まではわかりません。コマンド解釈用ではあるものの、少し他の用途にも使えないかと思い、このような用途に使ってみました。

# 本Scriptは、CotEditor用のScript Pack v2.0に収録されています


▲CotEditorの選択範囲を伏せ字にする。形態素解析して単語化して、単語単位で伏せ字にするかの判断を実行

テキストエディタ上で伏せ字処理というのは、個人的によく使います。たいていは、オリジナルの文章に対して同様の分量の文章を作らなくてはならないようなケースで、文字数の感覚をつかむために使います。一種のダミーレイアウトのようなものです。

本スクリプトのような伏せ字処理については、ニーズがあるんだかないんだか不明なものですが、とりあえず掲載してみました。自分で使ってみたところ、たしかに面白いものの、実用性については未知数という印象です。

(minusList of parseSPD) に入れている語群は、どこかからか拾ってきたもののようではあるものの、すでに何か方向性を見失っているような気がしないではありません。

AppleScript名:選択範囲を伏せ字に(簡易形態素解析でそれっぽく).scptd
— Created 2018-09-26 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.5" — El Capitan (10.11) or later
use framework "Foundation"
use scripting additions
use jParser : script "easyJParse"

property NSArray : a reference to current application’s NSArray
property NSMutableSet : a reference to current application’s NSMutableSet
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

property fuesejiChar : "□"

script parseSPD
  property pList : {}
  
property p2List : {}
  
property oneLine : {}
  
property outStr : ""
  
property minusList : {}
end script

–伏せ字化しない助詞などの単語リスト。名詞だけを残すように整備。単語(形態素)単位で照合する
set (minusList of parseSPD) to {"", " ", "ー", "あ", "で", "も", "に", "と", "の", "は", "へ", "さ", "が", "せ", "か", "た", "だ", "だっ", "ば", "つ", "な", "い", "き", "お", "ら", "る", "れ", "なっ", "それ", "これ", "あれ", "どれ", "この", "どの", "あの", "その", "まで", "こと", "もの", "いつ", "いく", "たち", "ただ", "たい", "そう", "いる", "よう", "れる", "ない", "なら", "なる", "なけれ", "から", "する", "たら", "たり", "だけ", "って", "られ", "的", "化", "いくら", "そんな", "どんな", "あんな", "者", "陰", "時", "事", "こんな", "つれ", "けど", "ああ", "ある", "あっ", "あり", "しかし", "きっと", "すっかり", "例えば", "たとえば", "さっぱり", "たとえ", "だろう", "かつ", "ところ", "まるで", "だが", "全て", "すべて", "なり", "いい", "つれ", "つけ", "ながら", "せいぜい", "そうそう", "さらに", "もっと", "まだ", "なく", "し", "を", "て", "いけ", "行く", "また", "まま", "まぁ", "『", "』", "、", "。", "。。", "……。", "【", "】", "「", "」", "(", ")", "最近", "今度", "中", "チカチカ", "グラグラ", "ふわふわ", "少し", "ついで", "より", "っぽい", "ぐらい", "何", "とき", "ため", "そっくり", "そして", "やがて", "じきに", "すぐ", "今", "次", "できる", "出来る", "いや", "そう", "おそらく", "いえ", "らしい", "とも", "ほぼ", "つい", "もう", "きっかけ", "ころ", "頃", "早々", "そこ", "どこ", "なんか", "じゃ", "くれ", "ください", "こそ", "あいつ", "だれ", "誰", "おぼしき", "らしき", "らしい", "しか", "でき", "よっ", "確か", "どう", "こう", "そう", "ああ", "くる", "ざま", "ごとく", "きれ", "はず", "さらに", "さらなる", "更なる", "など", "ごと", "とても", "たく", "いう", "とっ", "いっ", "えっ", "おっ", "ここ", "そこ", "どこ", "なかっ", "ごく", "やる", "ゆい", "ふと", "たび", "ほど", "もた", "よし", "ぜひ", "いら", "よい", "ま", "み", "む", "め", "も", "や", "けれど", "だけど", "したがっ", "すごく", "そもそも", "ほしい", "なれる", "すぎ", "もふもふ", "モフモフ", "さん", "おと", "とー", "えっと", "け", "っけ", "なん", "よ", "ね", "しっくり", "くれる", "くれた", "なぜ", "まあ", "まぁ", "ん", "なんて", "!」"}

set (pList of parseSPD) to {}
set (p2List of parseSPD) to {}
set (oneLine of parseSPD) to {}
set (outStr of parseSPD) to {}

tell application "CotEditor"
  tell front document
    –選択部分が存在しているかどうかチェック
    
set aCon to contents of selection
    
if aCon = "" then return
    
    
set (pList of parseSPD) to paragraphs of aCon
  end tell
end tell

–伏せ字にする対象単語を、助詞などを消し込むことでピックアップ
repeat with i in (pList of parseSPD)
  if length of i > 1 then
    –簡易形態素解析
    
set tempList to parseJ(i) of jParser
    
    
–簡易形態素解析したリストと助詞などのリストの差分を計算
    
set cList to clacListDiff(tempList, (minusList of parseSPD)) of me
    
    
set (oneLine of parseSPD) to {}
    
repeat with ii in tempList
      set aLen to length of ii
      
if ii is in cList then
        –伏せ字化する場合
        
set bCon to multipleChar(fuesejiChar, aLen) of me
        
      else
        –そのまま出力する場合
        
set bCon to contents of ii
      end if
      
set the end of (oneLine of parseSPD) to bCon
    end repeat
    
    
–1つの文章ぶんの単語を連結
    
set cStr to retDelimedText((oneLine of parseSPD), "") of me
  else
    set cStr to ""
  end if
  
  
set the end of (p2List of parseSPD) to cStr
  
end repeat

–すべての文章を連結して配列からテキストに
set (outStr of parseSPD) to retDelimedText((p2List of parseSPD), return) of me

tell application "CotEditor"
  tell front document
    set contents of selection to (outStr of parseSPD)
  end tell
end tell

–指定文字を指定回数繰り返して連結して出力
on multipleChar(aChar as string, aLen as integer)
  set aList to {}
  
repeat aLen times
    set the end of aList to aChar
  end repeat
  
  
return retDelimedText(aList, "") of me
end multipleChar

–1D Listを要素間に指定デリミタをはさんで文字列化
on retDelimedText(aList as list, aDelim as string)
  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 retDelimedText

–2つの1D Listの差分を計算
on clacListDiff(aList as list, bList as list)
  set aSet to NSMutableSet’s setWithArray:aList
  
set bSet to NSMutableSet’s setWithArray:bList
  
  
aSet’s minusSet:bSet –補集合
  
set aRes to aSet’s allObjects() as list
  
  
return aRes
end clacListDiff

★Click Here to Open This Script 

Posted in list Natural Language Processing Text | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy CotEditor NSArray NSMutableSet NSSortDescriptor | 1 Comment

シーケンシャル値の1D Listを作成してシャッフル

Posted on 10月 15, 2019 by Takaaki Naganoya

開始値から終了値までのシーケンシャルな値の入った1次元配列(1D List)を作成し、シャッフルするAppleScriptです。

もともとは、Shane Stanleyによる乱数シャッフルルーチンがあり、これにシーケンシャル値の作成部分を追加。最終的にはこのシャッフルした値をもとに文字列の入ったリストのシャッフルを行いました。そのための部品です。

# 結局、本ルーチンは不要だったので、そのままそっくり書き捨てになりました。文字列リストのシャッフルに、インデックスをシャッフルしないでも、処理対象データそのものをシャッフルさせればよかったので、不要ということに、、、、

途中まではCocoaの機能を用いて連番リストを作りかけていたのですが、自分の考えた範囲ではCocoaの機能を用いてメリットが生まれるような要素数(数万以上)のデータ処理を行う可能性が低そうだったので、連番生成はループで行っています。

AppleScript名:シーケンシャル値の1D Listを作成してシャッフル.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/10/15
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use framework "Foundation"
use framework "GameplayKit" — requires macOS 10.12
use scripting additions

set sList to makeShuffledSequantialNumList(1, 2014) of me
–> {1949, 1356, 376, 1222, 1089, 550, …..}

on makeShuffledSequantialNumList(fromNum, toNum)
  script spdL
    property nList : {}
  end script
  
  
set (nList of spdL) to {}
  
  
repeat with i from fromNum to toNum by 1
    set the end of (nList of spdL) to i
  end repeat
  
  
set anArray to current application’s NSArray’s arrayWithArray:(nList of spdL)
  
set newArray to anArray’s shuffledArray() as list — requires macOS 10.12
  
  
return newArray
end makeShuffledSequantialNumList

★Click Here to Open This Script 

Posted in list | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSArray | Leave a comment

指定アプリケーションの指定言語のstringsファイルの内容をすべて取り出す

Posted on 9月 21, 2019 by Takaaki Naganoya

バンドルIDで指定したアプリケーションのResourcesフォルダ中の、指定ロケールのstringsファイルをすべて読み込んでkeyを取り出すAppleScriptです。

アプリケーション内の指定のローカライズしたメッセージ(テキスト)の内容を取り出すことはできますが、そのためにはキーを知っておく必要があります。そのキーを取り出してローカライズされた文字列を取り出すため、キーを調査してみました。

これをそのまま何かのツールなりScriptに使うというものではなく、いわゆる「下調べ」のためのScriptの部品です。

ほぼ、ありもののルーチンを再利用しただけのプログラムでできていますが、ありものはありもので、数千本のAppleScriptのストックからそれを探し出してくるのも一苦労です。

アプリケーションバンドル内のResourcesフォルダ以下の(各lprojフォルダ以下の)stringsファイルの中身はまんま(シリアライズした)NSDictionaryなので、そのままDictionaryに読み込んでallkey()などのメソッドを実行できます。昔のstringsファイルはテキストファイルだったような気がしますが、テキストファイルだとparseしないといけないんで、ビルド時にparseしておいてDictionaryの状態でファイルに書いておくのは正しいと思います。

Xcode 8あたりから、アプリケーションのローカライズのルールが変更になったようで、基本となる言語環境がBase.lprojになり、英語はen.lprojといった1ローカライズ言語という位置付けに変更になりました。

NSLocalizedStringFromTableInBundleを使ってアクセスしようかとも思ったのですが、こちらはまだ目的を達成できていません。


▲Xcode 11上で実験的に作成したAppleScriptアプリケーションのバンドル構造(左)。ローカライズしていない状態だとEnglishではなくBase.lprojが作成される。右はKeynote.app/Contents/Resources/ja.lprojフォルダの内容。stringsファイルが大量に存在している

AppleScript名:指定アプリケーションの指定言語のstringsファイルの内容をすべて取り出す.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSBundle : a reference to current application’s NSBundle
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSWorkspace : a reference to current application’s NSWorkspace
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants

set targID to "com.apple.iWork.Keynote"

set bRes to getLocalizationsFromBundleID(targID) of me
–>  {"de", "he", "en_AU", "ar", "el", "ja", "en", "uk", "es_419", "zh_CN", "es", "da", "it", "sk", "pt_PT", "ms", "sv", "cs", "ko", "Base", "no", "hu", "zh_HK", "tr", "pl", "zh_TW", "en_GB", "vi", "ru", "fr_CA", "fr", "fi", "id", "nl", "th", "pt", "ro", "hr", "hi", "ca"}

set curLang to first item of (choose from list bRes)
set aPath to retPathFromBundleID(targID) of me

set aPath to aPath & "/Contents/Resources/" & curLang & ".lproj"
set sList to getFilepathListByUTI(aPath, "com.apple.xcode.strings-text", "POSIX") of me

set aMDict to NSMutableDictionary’s new()

repeat with i in sList
  set j to contents of i
  
set aDict to (NSDictionary’s alloc()’s initWithContentsOfFile:j)
  (
aMDict’s addEntriesFromDictionary:aDict)
end repeat

return aMDict’s allKeys() as list
–> {"Interactive Bubble Chart", "Rehearse Slideshow", "PMT_ARGUMENT_1", "ACCRINTM_ARGUMENT_4_MODE_0", "Truck_378",….}

on getLocalizationsFromBundleID(aBundleID)
  set aRes to retPathFromBundleID(aBundleID) of me
  
if aRes = false then error "Wrong Bundle ID."
  
return getSpecifiedAppFilesLocalizationListWithDuplication(aRes) of me
end getLocalizationsFromBundleID

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を許容
on getSpecifiedAppFilesLocalizationListWithDuplication(appPOSIXpath)
  set aURL to (|NSURL|’s fileURLWithPath:appPOSIXpath)
  
set aBundle to NSBundle’s bundleWithURL:aURL
  
set locList to aBundle’s localizations()
  
return locList as list
end getSpecifiedAppFilesLocalizationListWithDuplication

on retPathFromBundleID(aBundleID)
  set aURL to NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
if aURL = missing value then return false –Error
  
return aURL’s |path|() as string
end retPathFromBundleID

on getFilepathListByUTI(aFolPOSIX, aUTI as string, aFileType as string)
  script spdFile
    property urlList : {}
  end script
  
  
if aFileType is not in {"file", "POSIX"} then return {}
  
  
set aFM to NSFileManager’s defaultManager()
  
set aFolExt to (aFM’s fileExistsAtPath:aFolPOSIX isDirectory:true) as boolean
  
if aFolExt = false then return {} –フォルダ自体が存在しなければヌルリストを返す
  
  
set aURL to |NSURL|’s fileURLWithPath:aFolPOSIX
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer)
  
set urlArray to (aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value))
  
if urlArray = missing value then return {}
  
  
set (urlList of spdFile) to urlArray as list
  
set newList to {}
  
  
repeat with i in (urlList of spdFile)
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, aUTI)
    
    
if utiRes is not equal to {} then
      if aFileType = "POSIX" then
        set the end of newList to j
      else if aFileType = "file" then
        set the end of newList to POSIX file j
      end if
    end if
    
  end repeat
  
  
return newList
end getFilepathListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

★Click Here to Open This Script 

Posted in file Record URL UTI | Tagged 10.12savvy 10.13savvy 10.14savvy NSArray NSBundle NSDictionary NSDirectoryEnumerationSkipsHiddenFiles NSDirectoryEnumerationSkipsPackageDescendants NSFileManager NSMutableDictionary NSPredicate NSURL NSURLTypeIdentifierKey NSWorkspace | Leave a comment

指定のAppleScriptテキストを構文確認して指定の構文要素が入っていないかチェック

Posted on 7月 31, 2019 by Takaaki Naganoya

指定のテキストをAppleScriptとみなして構文確認して、指定の構文要素が入っていないかチェックするAppleScriptです。

AppleScriptにeval関数はないので、文字列として与えたAppleScriptをその場で実行する「run script」コマンドをevalがわりに使っています。

文字列をそのままrun scriptするのは(一応いろいろ保護機能はあるものの)、セキュリティ的にリスクの高い操作になってしまいます。


▲こんなのがrun scriptコマンドで実行されてしまったらと思うとゾッとします。ためしに実行してみたら、実行権限がないので実行できないエラーになりました

そこで、文字列をいったんAppleScriptとして評価(構文確認)し、コマンドなどの危険と思われる構文要素が入っていないかどうかチェックする処理を書いて使っています。

これら、「コマンド」および「追加コマンド」といった構文要素が入っていたらtrueを返します。

本Scriptをライブラリ化して組み込むことで、少ない記述量で対象の文字列をそのままrun scriptしてよいかどうか判定できるようにしています。

–> Download checkDanger(with Library in its bundle)


▲実際にテキストをAppleScriptとして評価して構文要素を検出し、このようにコマンド部分(AppleScriptのdo shell scriptコマンド)が入っていることを検知できる

AppleScript名:指定のAppleScriptテキストを構文確認して指定の構文要素が入っていないかチェック.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/31
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use framework "AppKit"
use framework "OSAKit"
use attrLib : script "asAttrRecLib"

property NSArray : a reference to current application’s NSArray
property OSANull : a reference to current application’s OSANull
property NSString : a reference to current application’s NSString
property OSAScript : a reference to current application’s OSAScript
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSUnarchiver : a reference to current application’s NSUnarchiver
property OSALanguage : a reference to current application’s OSALanguage
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property OSALanguageInstance : a reference to current application’s OSALanguageInstance

script spd
  property fArray : {}
  
property outList : {}
  
property resStr : {}
end script

on run
  set theSource to "do shell script \"rm -f *.*\""
  
set aRes to chkTargLexicalElementsFromCompiledScriptAttribute(theSource, {9, 14}) of me –{Command, External Command}
  
–> true : 指定の構文要素が入っていた
  
  
set theSource to "{{aName:\"piyoko\", aVal:100}, {aName:\"piyomaru\", aVal:80}, {aName:\"piyoo\", aVal:10}, {aName:\"Gundamo\", aVal:10}}"
  
set bRes to chkTargLexicalElementsFromCompiledScriptAttribute(theSource, {9, 14}) of me –{Command, External Command}
  
–> false : 指定の構文要素は入っていなかった
end run

–AppleScriptのソーステキストをコンパイルして、指定の構文要素が含まれているかチェックする
on chkTargLexicalElementsFromCompiledScriptAttribute(theSource as string, targLexicalElements as list)
  
  
–AppleScriptの構文色分け情報を取得
  
set ccRes to getAppleScriptSourceColors() of me
  
set cRes to chkASLexicalFormatColorConfliction(ccRes) of me –構文色分けの重複色チェック
  
if cRes = false then error "There is some duplicate(s) color among AppleScript’s lexical color settings"
  
  
–検索用の色情報を作成
  
set colTargList to {}
  
repeat with i in targLexicalElements
    set commentCol to contents of item i of ccRes –Variable and Sub-routine name
    
set searchVal to (redValue of commentCol as string) & " " & (greenValue of commentCol as string) & " " & (blueValue of commentCol as string) –for filtering
    
set the end of colTargList to searchVal
  end repeat
  
  
  
–与えられたテキストをAppleScriptとして評価し構文色分け情報をもとにStyle Runs的なDictionary化して返す
  
set aRitch to compileASSourcetextAndReturnStyledText(theSource) of me
  
  
  
–書式付きテキストからstyle runs的な書式リストを作成
  
set anAttrList to getAttributeRunsFromAttrString(aRitch) of attrLib
  
  
set bList to filterRecListByLabelAndSublist(anAttrList, "colorStr IN %@", colTargList) of me
  
  
return not (bList as list = {}) as boolean
  
end chkTargLexicalElementsFromCompiledScriptAttribute

on retDelimedText(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 retDelimedText

——-

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  — get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceNSColorsDict()
  — get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set aDict to NSMutableDictionary’s alloc()’s init()
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set colIndStr to (rVal as string) & " " & (gVal as string) & " " & (bVal as string)
    
set tmpColDat to {numList:{rVal, gVal, bVal}, strInd:colIndStr}
    
–(aDict’s addObject:tmpColDat forKey:theColor)
    (
aDict’s addObject:{rVal, gVal, bVal} forKey:theColor)
  end repeat
  
  
return aDict
end getAppleScriptSourceNSColorsDict

–NSColorからRGBの値を取り出す
on retColListFromNSColor(aCol, aMAX as integer)
  set aSpace to (aCol’s colorSpaceName()) as string
  
–set aSpace to (aCol’s type()) as integer
  
  
if aSpace is in {"NSDeviceRGBColorSpace", "NSCalibratedRGBColorSpace"} then
    copy aCol to cCol
  else
    –RGB以外の色空間の色は、RGBに変換
    
–CMYKとグレースケールは同一メソッドでRGBに変換できることを確認済み
    
set cCol to aCol’s colorUsingColorSpaceName:"NSCalibratedRGBColorSpace"
    
if cCol = missing value then error "Color Space Conversion Error (" & aSpace & " to NSCalibratedRGBColorSpace)"
  end if
  
  
set aRed to round ((cCol’s redComponent()) * aMAX) rounding as taught in school
  
set aGreen to round ((cCol’s greenComponent()) * aMAX) rounding as taught in school
  
set aBlue to round ((cCol’s blueComponent()) * aMAX) rounding as taught in school
  
  
if aRed > aMAX then set aRed to aMAX
  
if aGreen > aMAX then set aGreen to aMAX
  
if aBlue > aMAX then set aBlue to aMAX
  
  
return {aRed, aGreen, aBlue} as list
end retColListFromNSColor

–AS書式で配色に重複がないかどうかチェック
on chkASLexicalFormatColorConfliction(aList)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set bList to (anArray’s valueForKeyPath:"redValue.stringValue") as list
  
set cList to (anArray’s valueForKeyPath:"greenValue.stringValue") as list
  
set dList to (anArray’s valueForKeyPath:"blueValue.stringValue") as list
  
  
set colStrList to {}
  
repeat with i from 1 to (length of bList)
    set bItem to contents of item i of bList
    
set cItem to contents of item i of cList
    
set dItem to contents of item i of dList
    
set the end of colStrList to bItem & " " & cItem & " " & dItem
  end repeat
  
  
set aRes to returnDuplicatesOnly(colStrList) of me
  
  
if aRes is equal to {} then
    return true –重複が存在しなかった場合
  else
    return false –重複があった場合
  end if
end chkASLexicalFormatColorConfliction

on returnDuplicatesOnly(aList as list)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bList to (aSet’s allObjects()) as list
  
  
set dupList to {}
  
repeat with i in bList
    set aRes to (aSet’s countForObject:i)
    
if aRes > 1 then
      set the end of dupList to (contents of i)
    end if
  end repeat
  
  
return dupList
end returnDuplicatesOnly

–テキスト形式のAppleScriptをコンパイルして書式付きテキスト(NSAttributedString)を返す
on compileASSourcetextAndReturnStyledText(theSource)
  — create a new AppleScript instance
  
set anOSALanguageInstance to OSALanguage’s languageForName:"AppleScript"
  
  
set theScript to OSAScript’s alloc()’s initWithSource:theSource fromURL:(missing value) languageInstance:anOSALanguageInstance usingStorageOptions:(OSANull)
  
set {theResult, theError} to theScript’s compileAndReturnError:(reference)
  
  
if theResult as boolean is false then
    — handle error
    
error "Compile Error"
  else
    set styledSourceText to theScript’s richTextSource()
  end if
  
  
return styledSourceText
end compileASSourcetextAndReturnStyledText

–文字置換
on repChar(origText as string, targChar as string, repChar as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

–リストに入れたレコードを、指定の属性ラベルの値で抽出。値が別途指定のリストの中に存在していることが条件
on filterRecListByLabelAndSublist(aRecList as list, aPredicate as string, aSubList as list)
  set aArray to NSArray’s arrayWithArray:aRecList
  
set aSubArray to NSArray’s arrayWithArray:aSubList
  
  
–抽出
  
set aPredicate to NSPredicate’s predicateWithFormat_(aPredicate, aSubArray)
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  
return filteredArray as list
end filterRecListByLabelAndSublist

★Click Here to Open This Script 

Posted in Color OSA Text | Tagged 10.12savvy 10.13savvy 10.14savvy NSArray NSCountedSet NSDictionary NSMutableArray NSMutableDictionary NSPredicate NSString NSUnarchiver OSALanguage OSALanguageInstance OSANull OSAScript | 2 Comments

アイテム番号リストをもとに、ヒットしなかった項目を返す

Posted on 7月 31, 2019 by Takaaki Naganoya

アイテム番号の入ったリスト(配列変数)をもとに、ヒットしなかった項目を返すというAppleScriptです。

これは、なにがしかのデータとなにがしかの書類上に配置されたデータの照合を行って、存在が確認されなかったデータの一覧を作成するために作成したものです。

存在確認といえば、たいていは「存在していたデータそのもの、あるいはデータのアイテム番号一覧」といったものを出力するようになっています。そこに、「存在が確認できなかったデータの一覧が欲しい」という要望が出てくることも、だいたいは普通の出来事です。

ただ、この手の「言語化すると大したことはないが、実施に精神的な抵抗感をおぼえる処理」といいいますか、「後片付け的な処理」というのは、書くのが面倒で後回しになりがちなものでもあります。

事実、実際に書いてみたら、、、既存のサブルーチンの組み合わせで作りましたが、1日たって見直してみると何が書いてあるのかよく思い出せないレベルです(既存のサブルーチンの組み合わせが過ぎて、意味がわかりにくいのかも)。

AppleScript名:アイテム番号リストをもとに、ヒットしなかった項目を返す.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/29
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

property NSArray : a reference to current application’s NSArray
property NSMutableSet : a reference to current application’s NSMutableSet
property NSMutableArray : a reference to current application’s NSMutableArray

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set strList to {"Apple", "Book", "Cheeze", "Dictionary", "Escalgo", "Find", "Gorila", "hook", "ink", "jet"}
set hitIDList to {1, 2, 3, 4, 6}

set bList to getUnpickedContents(hitIDList, strList) of me
–> {"Escalgo", "Gorila", "hook", "ink", "jet"}

on getUnpickedContents(aList, strList)
  set aMax to length of strList
  
set bList to getSequentialNumList(aMax) of me
  
set cList to excludeTwoList(bList, aList) of me
  
  
set anArray to NSMutableArray’s arrayWithArray:(cList)
  
set dList to (anArray’s sortedArrayUsingSelector:"compare:") as list
  
  
set eList to retConListFromItemNumList(dList, strList) of me
  
return eList
end getUnpickedContents

–アイテム項目番号が入っているリストをもとに、中身が入っているリストから項目を取り出す
on retConListFromItemNumList(dList, strList)
  set outList to {}
  
repeat with i in dList
    set aTmp to item (contents of i) of strList
    
set the end of outList to aTmp
  end repeat
  
return outList
end retConListFromItemNumList

on excludeTwoList(aList, bList)
  set aArray to NSArray’s arrayWithArray:(aList)
  
set bArray to NSArray’s arrayWithArray:(bList)
  
  
set aSet to NSMutableSet’s alloc()’s initWithArray:aArray
  
set bSet to NSMutableSet’s alloc()’s initWithArray:bArray
  
  
aSet’s minusSet:bSet –補集合
  
set resList to aSet’s allObjects() as list
  
  
return resList
end excludeTwoList

on getSequentialNumList(aMax)
  set outList to {}
  
repeat with i from 1 to aMax
    set the end of outList to i
  end repeat
  
return outList
end getSequentialNumList

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSArray NSMutableArray NSMutableSet | Leave a comment

NumbersのColumn Adr(26進数)と10進数との相互変換

Posted on 7月 22, 2019 by Takaaki Naganoya

Numbersの表のカラムを表現するアドレス文字列(26進数)と数値の間のエンコーダーおよびデコーダーのAppleScriptです。

もともとは、Excel 2004/2008で採用されたカラムアドレス形式に対処するためのScriptでした。

Excel上のセルのアドレス(場所)を指し示す方法は、「R1C1」(行とカラムを数値指定)形式、あるいはAppleScriptの行オブジェクトとセルオブジェクトで指定する形式でした。

そこへ新たに「A1」形式……画面上で表記されている行、カラムと親和性の高い形式が採用されることになりました。当初は相互変換のための関数なども用意されていなかったと記憶しています(間違っていたらご指摘ください)。そのため、Scripterが自力でこのA1形式に対応する必要が出てきました。

そんな中、Excel書類にAppleScriptを埋め込んで実行するAppleScriptを開発。本ルーチンはそのScript開発のために作成したものです。

Excel 2008でVBAの処理系が外されてMac上のVBA的なマクロ処理はAppleSctiptに一本化されるという話になっていたため(当時の話)、これを好機ととらえ、マイクロソフトのご担当にデモして、US本社で紹介していただくということになりました。

ただ、その会議上でVBAの復活プランが発表され、自分の提案したプランは廃案に(まさか当時のREALbasicのコンパイラの開発者を引き抜いてきてVBAの処理系をスクラッチで書かせるとは思いませんでしたわー)。

その後は、Excel 2011でVBAの処理系が復活。本ルーチンも割とHDDの肥やしとして絶賛在庫状態になっておりました。ごくたまーに、このExcel 2011でVBAの処理系が復活したことを知らない方がいて、「VBAで動いているマクロをAppleScriptに移植してほしい」という問い合わせがあるのですが、「Excelの最新版を購入してください。VBAがありますよ」とお返事しています。最新のExcelが動く環境を購入する費用よりも安く仕事としてお受けすることは困難なので。

そこから年月が流れ、AppleがNumbersをリリース。そのカラム表記がExcel 2004/2008と同じ形式になっているために、しまいこんでいたルーチンをふたたび引っ張り出してきた次第です。

一応、条件つきで使えるものの、作りが古い(やっつけ仕事な)点が気になります。きっと、誰かがもっといいルーチンを作って使っているに違いありません。一応、本ルーチンでは1〜1351の範囲での動作を確認しています。

もう少し改良したいところではあります。実用上は、Numbersでそこまで大きなデータは扱わない(はず)ので、問題はあまりないものと思われます。

AppleScript名:NumbersのColumn Adr(26進数)と10進数との相互変換
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/07/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property NSString : a reference to current application’s NSString
property NSArray : a reference to current application’s NSArray
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch

repeat with i from 1 to 1351
  set a to numAdrToColumnEncode(i) of me
  
set b to colAddrToNumDecode(a) of me
  
set aRes to (i = b) as boolean
  
log {i, b, a, aRes}
  
if aRes = false then display dialog i as string
end repeat

–10進数数値をExcel 2004/2008的カラム表現にエンコードするサブルーチン(エンコード範囲:1〜1351)
on numAdrToColumnEncode(origNum)
  if origNum > 1351 then
    error "エラー:Numbersのカラム表現(A1形式)への変換ルーチンにおいて、想定範囲外(1351以上)のパラメータが指定されました"
  end if
  
  
set upperDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
set lowerDigitEncTable to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A"}
  
  
set oNum to origNum
  
set nTh to 26
  
set stringLength to 4
  
  
–数字が1桁の場合の対応
  
if origNum < 27 then
    set aRes to (item origNum of upperDigitEncTable) as string
    
return aRes
  end if
  
  
  
if origNum > 702 then
    –3桁になる場合
    
set upupNum to oNum div 676 –整数除算–上の上の桁
    
set oNum to oNum – (upupNum * 676)
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
–log {origNum, upupNum, upNum, lowNum}
    
    
–超つじつま合わせルーチン【強引】
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upupChar to (item upupNum of upperDigitEncTable) as string
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upupChar & upChar & lowChar
    
  else
    –2桁の場合
    
set upNum to oNum div 26 –整数除算–上の桁
    
set lowNum to oNum mod 26 – 1 –余剰計算–下の桁
    
    
    
–超つじつま合わせルーチン【強引】
    
if lowNum = -1 then
      set upNum to upNum – 1
      
set lowNum to 25
    end if
    
    
set upChar to (item upNum of upperDigitEncTable) as string
    
set lowChar to (item (lowNum + 1) of lowerDigitEncTable) as string
    
set resText to upChar & lowChar
    
  end if
  
  
return resText
  
end numAdrToColumnEncode

–Numbersの横方向アドレス(A〜Zの26進数)文字列を10進数に変換
on colAddrToNumDecode(origStr)
  return aNthToDecimal(origStr, {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}) of me
end colAddrToNumDecode

–n進数文字列を10進数に変換する
on aNthToDecimal(origStr, nTh)
  set resNumber to 0
  
  
set sList to reverse of (characters of origStr)
  
set aLen to length of nTh
  
set digitCount to 0
  
  
repeat with i in sList
    set j to contents of i
    
set aRes to offsetInList(j, nTh) of me
    
    
set resNumber to resNumber + (aLen ^ digitCount) * aRes
    
    
set digitCount to digitCount + 1
  end repeat
  
  
return resNumber as integer
end aNthToDecimal

on offsetInList(aChar, aList)
  set anArray to NSArray’s arrayWithArray:aList
  
set aInd to (anArray’s indexOfObject:aChar)
  
if aInd = current application’s NSNotFound or (aInd as number) > 9.99999999E+8 then
    error "Invalid Character Error"
  else
    return (aInd as integer) + 1 –0 to 1 based index conversion
  end if
end offsetInList

★Click Here to Open This Script 

Posted in list Number | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSRegularExpressionSearch NSString Numbers | Leave a comment

TeamViewerの「リモートコントロール」画面からIDとパスワードを取得(v14対応)

Posted on 7月 15, 2019 by Takaaki Naganoya

リモート操作ソフト「TeamViewer」の画面から、ユーザーIDとパスワードを取得するAppleScriptです。


▲TeamViewer v14をmacOS 10.12(左)、10.14(右)で起動したところ

GUI Scriptingの機能を使って、画面上から情報を取得し、正規表現で抽出してみました。

TeamViewerで実家のMacなど、離れた場所にあるマシンをメンテナンスする必要がある場合に、実家から電話がかかってきて、TeamViewerのパスワードとIDを口頭で教えてもらって接続する必要があるわけですが、親が高齢のためそれが心もとなくなりつつある昨今、確実に確認するために作成したものです。

その場で、(自分のために)「TeamViewerを起動してIDとパスを確認して自分あてにメールで送ってくるAppleScript」を作りかけたのですが、途中からビデオ編集の仕方を教えろなどと言われて説明する羽目に。

AppleScript名:TeamViewerの「リモートコントロール」画面からIDとパスワードを取得(v14対応)
【コメント】 ?
— Created 2019-07-15 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property NSArray : a reference to current application’s NSArray
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableArray : a reference to current application’s NSMutableArray

set aRes to getIDAndPassFromTeamViewer() of me
–> {myID:"XXX XXX XXX", myPass:"xxXxXX"}

–TeamViewerの「リモートコントロール」画面からIDとパスワードを取得(v14対応)
on getIDAndPassFromTeamViewer()
  –画面の表示状態を変更
  
selectRemoteControlRowOnTV("リモートコントロール") of me
  
  
–画面(Window)上のテキストをとりあえず全部取得
  
set sList to retEveryStaticTextInCurrentView() of me
  
  
set anArray to NSArray’s arrayWithArray:sList
  
  
–「使用中のID」を取得
  
set aPred to NSPredicate’s predicateWithFormat:"SELF MATCHES ’[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}’"
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
set idRes to contents of first item of bRes
  
–> "XXX XXX XXX"
  
  
set bList to removeItemFromList(sList, idRes) of me
  
set bArray to NSArray’s arrayWithArray:bList
  
set bPred to NSPredicate’s predicateWithFormat:"SELF MATCHES ’^[0-9a-zA-Z]+$’"
  
set cRes to (bArray’s filteredArrayUsingPredicate:bPred) as list
  
set psRes to contents of first item of cRes
  
–> "xxXxXX"
  
  
return {myID:idRes, myPass:psRes}
end getIDAndPassFromTeamViewer

on retEveryStaticTextInCurrentView()
  activate application "TeamViewer"
  
tell application "System Events"
    tell process "TeamViewer"
      tell window 1
        tell group 2
          set sList to value of every static text
          
–> {"接続準備完了(安全な接続)", "パートナーID", "リモートコンピュータの操作", "遠隔操作を受ける許可", "使用中のID", "999 999 999", "パスワード", "xxx999", "無人アクセス"}
          
return sList
        end tell
      end tell
    end tell
  end tell
end retEveryStaticTextInCurrentView

–TeamViewerで「リモートコントロール」行を選択
on selectRemoteControlRowOnTV(aTargStr)
  activate application "TeamViewer"
  
tell application "System Events"
    tell process "TeamViewer"
      tell table 1 of scroll area 1 of window 1
        set rCount to count every row
        
        
repeat with i from 1 to rCount
          tell row i
            tell UI element 1
              set aText to value of static text 1
            end tell
            
            
if aText = aTargStr then
              set selected to true
            end if
          end tell
        end repeat
        
      end tell
    end tell
  end tell
end selectRemoteControlRowOnTV

–1次元配列から指定の内容の要素をすべて削除して返す
on removeItemFromList(aTargList, aTargValue)
  set anArray to NSMutableArray’s arrayWithArray:aTargList
  
repeat
    set aInd to anArray’s indexOfObject:aTargValue
    
if aInd = current application’s NSNotFound or (aInd as real > 9.99999999E+8) then exit repeat
    
anArray’s removeObjectAtIndex:aInd
  end repeat
  
return anArray as list
end removeItemFromList

★Click Here to Open This Script 

Posted in GUI Scripting regexp | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSArray NSMutableArray NSPredicate TeamViewer | Leave a comment

Post navigation

  • Older posts

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

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • CotEditorで2つの書類の行単位での差分検出
  • macOS 15, Sequoia
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • AppleScriptによる並列処理
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • NaturalLanguage.frameworkでNLEmbeddingの処理が可能な言語をチェック
  • Keynote/Pagesで選択中の表カラムの幅を均等割
  • Keynote、Pages、Numbers Ver.14.0が登場
  • macOS 15 リモートApple Eventsにバグ?
  • デフォルトインストールされたフォント名を取得するAppleScript
  • AppleScript入門① AppleScriptってなんだろう?

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (193) 14.0savvy (145) 15.0savvy (125) CotEditor (66) Finder (51) iTunes (19) Keynote (116) 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 (76) 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年4月
  • 2025年3月
  • 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