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

カテゴリー: list

PDFのしおり(TOC)の内容を取得する v3

Posted on 4月 11, 2019 by Takaaki Naganoya

指定のTOCつきPDFからTOCをrecord in listで取得するAppleScriptです。各TOC項目のページ数、アウトラインレベル、ラベル(ページ名称)を順次取得して配列に出力します。

先日発売したBlogアーカイブ本のTOCを本Scriptで取得すると、以下のようになります(抜粋)。

{{pageIndex:1, outlineLevel:1, outLabel:"表紙"}, {pageIndex:2, outlineLevel:1, outLabel:"商標について"}, {pageIndex:3, outlineLevel:1, outLabel:"本書をご覧になるために"}, ... {pageIndex:34, outlineLevel:1, outLabel:"2011/1"}, {pageIndex:35, outlineLevel:2, outLabel:"リスト同士のdiffをとる"}, {pageIndex:40, outlineLevel:2, outLabel:"日本の月呼称を返す"}, {pageIndex:42, outlineLevel:2, outLabel:"指定の月を1月分リスト化"},...... {pageIndex:450, outlineLevel:1, outLabel:"奥付"}}

あれ? 「本書について」(2P)という記事が掲載もれしているのを、TOC出力から見つけてしまった、、、、、、、、

AppleScript名:PDFのしおり(TOC)の内容を取得する v3
— Created 2017-01-09 by Takaaki Naganoya
— Modified 2019-04-10 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property PDFDocument : a reference to current application’s PDFDocument
property titleList : {}

set my titleList to {}

set aFile to POSIX path of (choose file of type {"com.adobe.pdf"})

set fileURL to |NSURL|’s fileURLWithPath:aFile
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:fileURL

–TOCの読み込み
set parentOL to aPDFdoc’s outlineRoot() –あらかじめTOCが存在していないとmissing valueになる
if parentOL is equal to missing value then
  display dialog "本PDFにはTOCが添付されていないため、処理を終了します" with title "No TOC Error:"
  
return
end if

getChilds(parentOL) of me
return (my titleList)
–> {{pageIndex:1, outlineLevel:1, outLabel:"表紙"}, {pageIndex:2, outlineLevel:1, outLabel:"商標について"}, {pageIndex:3, outlineLevel:1, outLabel:"本書をご覧になるために"}, {pageIndex:5, outlineLevel:1, outLabel:"まえがき"},…… {pageIndex:33, outlineLevel:1, outLabel:"2011年"}, {pageIndex:34, outlineLevel:1, outLabel:"2011/1"}, {pageIndex:35, outlineLevel:2, outLabel:"リスト同士のdiffをとる"}, {pageIndex:40, outlineLevel:2, outLabel:"日本の月呼称を返す"},…….}

–PDFOutlineを再帰で取得する
on getChilds(parentOL)
  set outLineStr to parentOL’s label()
  
set outLineCount to (parentOL’s numberOfChildren()) as number
  
  
repeat with i from 0 to (outLineCount – 1)
    set anOut to (parentOL’s childAtIndex:i)
    
    
–Label (Page Title)
    
set tmpOut to (anOut’s label()) as string
    
    
–Page Indel Label
    
set tmpInd to (anOut’s destination()’s page()’s label()) as integer
    
    
–Outline Level
    
set outlevel to 0
    
copy anOut to aTmpOut
    
repeat
      set aTmpOut to aTmpOut’s |parent|()
      
if aTmpOut = missing value then exit repeat
      
set outlevel to outlevel + 1
    end repeat
    
    
set the end of my titleList to {pageIndex:tmpInd, outlineLevel:outlevel, outLabel:tmpOut}
    
    
set tmpChild to (anOut’s numberOfChildren()) as integer
    
    
if tmpChild is not equal to 0 then
      getChilds(anOut) of me
    end if
  end repeat
end getChilds

★Click Here to Open This Script 

Posted in list PDF Record recursive call | Tagged 10.12savvy 10.13savvy 10.14savvy NSURL PDFDocument | Leave a comment

PDFのしおり(TOC)の内容を取得する v2

Posted on 4月 10, 2019 by Takaaki Naganoya

指定のTOCつきPDFからTOCを1Dリスト(1次元配列)で取得するAppleScriptです。各TOC項目のlabelの文字列を順次取得して1次元配列に出力します。

AppleScript名:PDFのしおり(TOC)の内容を取得する v2
— Created 2017-01-09 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property PDFDocument : a reference to current application’s PDFDocument
property titleList : {}

set my titleList to {}

set aFile to POSIX path of (choose file of type {"com.adobe.pdf"})

set fileURL to |NSURL|’s fileURLWithPath:aFile
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:fileURL

–TOCの読み込み
set parentOL to aPDFdoc’s outlineRoot() –あらかじめTOCが存在していないとmissing valueになる
if parentOL is equal to missing value then
  display dialog "本PDFにはTOCが添付されていないため、処理を終了します" with title "No TOC Error:"
  
return
end if

getChilds(parentOL) of me
return (my titleList)

–再帰処理してみた
on getChilds(parentOL)
  set outLineStr to parentOL’s label()
  
set outLineCount to (parentOL’s numberOfChildren()) as number
  
  
repeat with i from 0 to (outLineCount – 1)
    set anOut to (parentOL’s childAtIndex:i)
    
set tmpOut to (anOut’s label()) as string
    
set the end of my titleList to tmpOut
    
set tmpChild to (anOut’s numberOfChildren()) as integer
    
    
if tmpChild is not equal to 0 then
      getChilds(anOut) of me
    end if
  end repeat
end getChilds

★Click Here to Open This Script 

Posted in list PDF recursive call | Tagged 10.12savvy 10.13savvy 10.14savvy NSURL PDFDocument | Leave a comment

Keynoteのテキストアイテム内の文字の実際の幅でリサイズ

Posted on 4月 5, 2019 by Takaaki Naganoya

Keynote書類上のテキストアイテムを、内容の文字の実際の幅でリサイズするAppleScriptです。


▲テキストアイテム中のテキストをコピー(Command-C)


▲リサイズ前のテキストアイテム。文字に対してフレームサイズが大きい


▲本Scriptによりリサイズした後のテキストアイテム。文字の内容に対してフレームサイズがフィットしている


▲本Scriptにより順次複数のテキストアイテムを文字幅にリサイズしたところ。selectionがまともに動作していれば、複数のテキストアイテムを選択した状態でScriptを走らせて一度にリサイズできる(はず)だが、selectionが動作していないので複数のオブジェクトに対して同じ回数の操作が必要

Keynoteにはversion 9.0で「selection」の予約語が追加されましたが、これは一般に期待されるような「表示中のスライド上で選択中のオブジェクトへの参照が取得できる」というものではありません。

つまり、選択中のオブジェクトへの参照をKeynoteに対して問い合わせることは、(現時点では)不可能です。

そこで、テキストアイテム内の内容(object text)をコピー(Command-C)してクリップボードに格納し、その内容をキーにして現在表示中のスライド内のテキストアイテムを抽出してみたところ、うまくいきました(なんでこんなことさせられてるんだろ?)。早くselectionがKeynote上でもまともに使えるようになってほしいものです(Pages上ではけっこういい感じに使えるのに)。

テキストアイテムの特定ができるようになったので、入っているobject textのサイズに合わせてテキストアイテムをリサイズする処理を書いてみました。

クリップボードの内容をNSAttributedStringとして取得し、その画面上の描画サイズを取得するサブルーチンは毎日さんざん使っているので(掲載リストのproperty宣言部をソートするのに使っています)信頼性があります。

とりあえず、擬似的に現在選択中のオブジェクトを特定する実験コードにちょっとだけ機能をつけたものですが、操作1に対して結果1というのは普通「自動化」とか言わないので、実用性は皆無です(めんどくさい操作ステップを省けるぐらいしか)。

AppleScript名:Keynoteのテキストアイテム内の文字の実際の幅でリサイズ
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/04/05
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use BPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

property NSFont : a reference to current application’s NSFont
property NSColor : a reference to current application’s NSColor
property NSPasteboard : a reference to current application’s NSPasteboard
property NSAttributedString : a reference to current application’s NSAttributedString
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName

load framework

–クリップボードの内容をテキストとして取得し、現在のKeynote書類中のtext itemのうちコピーしたテキストに該当するものを抽出
set targString to (the clipboard) as string

tell application "Keynote"
  tell document 1
    tell current slide
      set tList to every text item whose object text is targString
      
if tList = {} then return
      
set targObject to first item of tList
    end tell
  end tell
end tell

–クリップボードの内容をNSAttributedStringに
set anAttr to my getClipboardASStyledText()

–Split Attributed Strings into lines
set attrList to splitAttributedStringByLines(anAttr) of me

–Sort Attributed String list by its width in descending (large–> small)
set aResList to shellSortListDecending(attrList, 1) of me
set {targObjWidth, targAttrStrObj} to first item of aResList

–Resize Text item objet’s frame by its drawing width on screen
tell application "Keynote"
  tell document 1
    tell current slide
      tell targObject
        set width to ((targObjWidth as number) + 10) –resize it !
      end tell
    end tell
  end tell
end tell

–入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  return sort2DList(a, keyItem, {true}) of me
end shellSortListAscending

–入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  return sort2DList(a, keyItem, {false}) of me
end shellSortListDecending

–2D Listをソート
on sort2DList(aList as list, sortIndexes as list, sortOrders as list)
  
  
–index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換
  
set newIndex to {}
  
repeat with i in sortIndexes
    set j to contents of i
    
set j to j – 1
    
set the end of newIndex to j
  end repeat
  
  
–Sort TypeのListを作成(あえて外部から指定する内容でもない)
  
set sortTypes to {}
  
repeat (length of sortIndexes) times
    set the end of sortTypes to "compare:"
  end repeat
  
  
–Sort
  
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
  
  
return resList
end sort2DList

— クリップボードの内容をNSAttributedStringとして取り出して返す
on getClipboardASStyledText()
  try
    set theNSPasteboard to NSPasteboard’s generalPasteboard()
    
set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({NSAttributedString}) options:(missing value)
    
set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0
    
return theNSAttributedString
  on error
    return missing value
  end try
end getClipboardASStyledText

on splitAttributedStringByLines(theStyledText)
  set outList to {}
  
set outAttr to NSMutableAttributedString’s alloc()’s initWithString:""
  
  
set thePureString to theStyledText’s |string|() –pure string from theStyledText
  
set theLength to theStyledText’s |length|()
  
set startIndex to 0
  
  
repeat until (startIndex = theLength)
    set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex}
    
set aText to (thePureString’s substringWithRange:theRange) as string –String
    
    
set aColor to (theAtts’s valueForKeyPath:"NSColor") –Color
    
set aFont to (theAtts’s valueForKeyPath:"NSFont") –Font
    
if aFont is equal to missing value then error "Not font name and size are specified" –Font Name error
    
set aDFontName to aFont’s displayName() –Font Name
    
set aDFontSize to aFont’s pointSize() –Font Size
    
    
set tmpAttrStr to generateAttributedString(aText, aDFontName, aDFontSize, aColor) of me
    
outAttr’s appendAttributedString:tmpAttrStr
    
    
if (aText contains return) or (aText contains string id 10) then –CR or LF
      set tmpSize to outAttr’s |size|()
      
set tmpWidth to retWidthFromSize(tmpSize) of me
      
      
set the end of outList to {tmpWidth, outAttr}
      
set outAttr to NSMutableAttributedString’s alloc()’s initWithString:""
    end if
    
    
set startIndex to current application’s NSMaxRange(theRange)
  end repeat
  
  
set tmpSize to outAttr’s |size|()
  
set tmpWidth to retWidthFromSize(tmpSize) of me
  
set the end of outList to {tmpWidth, outAttr}
  
  
return outList
end splitAttributedStringByLines

on retWidthFromSize(tmpSize)
  if class of tmpSize = record then
    –macOS 10.10, 10.11, 10.12
    
set tmpWidth to width of tmpSize
  else
    –macOS 10.13, 10.14
    
set tmpWidth to first item of tmpSize
  end if
  
return tmpWidth
end retWidthFromSize

on generateAttributedString(aStr, aFontPSName, aFontSize, aColor)
  set tmpAttr to NSMutableAttributedString’s alloc()’s initWithString:aStr
  
set aRange to current application’s NSMakeRange(0, tmpAttr’s |length|())
  
set aVal1 to NSFont’s fontWithName:aFontPSName |size|:aFontSize
  
tmpAttr’s beginEditing()
  
tmpAttr’s addAttribute:(NSFontAttributeName) value:aVal1 range:aRange
  
tmpAttr’s addAttribute:(NSForegroundColorAttributeName) value:aColor range:aRange
  
tmpAttr’s endEditing()
  
return tmpAttr
end generateAttributedString

★Click Here to Open This Script 

Posted in Clipboard list Record RTF Sort | Tagged 10.12savvy 10.13savvy 10.14savvy Keynote NSAttributedString NSColor NSFont NSFontAttributeName NSForegroundColorAttributeName NSMutableAttributedString NSPasteboard | Leave a comment

アラートダイアログ上にsegmented controlを表示 v4

Posted on 3月 27, 2019 by Takaaki Naganoya

アラートダイアログ上にsegmented controlを複数表示して、複数選択項目を取得するAppleScriptです。

複数選択項目にpopup buttonではなくsegmented controlを使う必要性というのは、あんまりないですが………すべての選択項目が見える状態で少量の選択項目から1つを選ぶような用途には使えるのではないでしょうか。

segmented controlをそれぞれBoxに入れ、Viewにまとめ、Scroll viewに突っ込んでいますが、あまり効果があるんだかないんだか不明です。

AppleScript名:アラートダイアログ上にsegmented controlを表示 v4
— Created 2019-03-26 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
–Scroll Viewをつかってみた

property NSBox : a reference to current application’s NSBox
property NSView : a reference to current application’s NSView
property NSAlert : a reference to current application’s NSAlert
property NSScrollView : a reference to current application’s NSScrollView
property NSSegmentedControl : a reference to current application’s NSSegmentedControl
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSSegmentStyleTexturedRounded : a reference to current application’s NSSegmentStyleTexturedRounded

property returnCode : 0
property returnSels : {}

set paramObj to {myMessage:"複数項目選択", mySubMessage:"どれか選択してください。", segmentMes:{{"Red1", "Blue1", "Yellow1", "Brown1", "White1", "Cyan1", "Grey1"}, {"Red2", "Blue2", "Yellow2", "Brown2", "White2", "Cyan2", "Grey2"}, {"Red3", "Blue3", "Yellow3", "Brown3", "White3", "Cyan3", "Grey3"}, {"Red4", "Blue4", "Yellow4", "Brown4", "White4", "Cyan4", "Grey4"}, {"Red5", "Blue5", "Yellow5", "Brown5", "White5", "Cyan5", "Grey5"}}, segmentTitles:{"1st Segments", "2nd Segments", "3rd Segments", "4th Segments", "5th Segments"}}

my chooseMultipleSegments:paramObj

return my returnSels
–> {1, 2, 3, 4, 5}

on chooseMultipleSegments:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set segMes2DList to segmentMes of paramObj
  
set segTitleList to segmentTitles of paramObj
  
  
set aTmpY to (length of segMes2DList) * 60
  
  
–BoX + Segmented Control をつくる
  
set segsList to {}
  
set boxLIst to {}
  
set segsCount to 0
  
set tmpMaxX to 500
  
  
set aCount to 1
  
  
repeat with i in segMes2DList
    set aSeg to makeSegmentedControlWithStartY(i, aTmpY – segsCount – 40, 500, 40) of me
    
    
set aDBounds to aSeg’s |bounds|()
    
set tmpWidth to getWidth(aDBounds) of me
    
    
set aBox to (NSBox’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aTmpY – segsCount – 60, 500, 60)))
    (
aBox’s setTitle:(item aCount of segTitleList))
    (
aBox’s addSubview:aSeg)
    
    
if tmpWidth > tmpMaxX then set tmpMaxX to tmpWidth
    
    
set the end of segsList to aSeg –選択検出用
    
set the end of boxLIst to aBox –表示用
    
    
set segsCount to segsCount + 60
    
set aCount to aCount + 1
  end repeat
  
  
— create a view
  
set theView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, tmpMaxX, aTmpY))
  
theView’s setSubviews:boxLIst
  
  
— create a Scroll View
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, tmpMaxX, aTmpY))
  
aScroll’s setDocumentView:theView
  
theView’s enclosingScrollView()’s setHasHorizontalScroller:false
  
theView’s enclosingScrollView()’s setHasVerticalScroller:false
  
  
— set up alert
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aScroll
  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 my returnSels to {}
  
repeat with i in segsList
    set tmpSegSel to (i’s selectedSegment()) as number
    
set the end of (my returnSels) to tmpSegSel + 1
  end repeat
end chooseMultipleSegments:

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

on makeSegmentedControlWithStartY(titleList, aStartY, aWidth, aHeight)
  set aLen to length of titleList
  
  
set aSeg to NSSegmentedControl’s alloc()’s init()
  
aSeg’s setSegmentCount:aLen
  
  
set aCount to 0
  
repeat with i in titleList
    set j to contents of i
    (
aSeg’s setLabel:j forSegment:aCount)
    
set aCount to aCount + 1
  end repeat
  
  
aSeg’s setTranslatesAutoresizingMaskIntoConstraints:false
  
aSeg’s setSegmentStyle:(NSSegmentStyleTexturedRounded)
  
aSeg’s setFrame:(current application’s NSMakeRect(20, aHeight – 35, aWidth, aHeight – 40))
  
  
aSeg’s setTrackingMode:0
  
aSeg’s setTarget:me
  
aSeg’s setAction:"clickedSeg:"
  
aSeg’s setSelectedSegment:0
  
  
return aSeg
end makeSegmentedControlWithStartY

on clickedSeg:aSender
  set aSel to aSender’s selectedSegment()
end clickedSeg:

on getWidth(aDBounds)
  if class of aDBounds = list then
    –macOS 10.13 or later
    
return item 1 of item 1 of aDBounds
  else
    –macOS 10.10….10.12
    
return width of |size| of aDBounds
  end if
end getWidth

★Click Here to Open This Script 

Posted in GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSBox NSRunningApplication NSScrollView NSSegmentedControl NSView | 1 Comment

選択中のメッセージに添付ファイルがあれば赤く塗る

Posted on 3月 26, 2019 by Takaaki Naganoya

Mail.app上で選択中のメッセージ(Mail)に添付ファイルがあれば名称リストで背景色を赤く塗るAppleScriptです。

なんでこんなもんが必要なのか?

macOS 10.9あたりからでしょうか、Mail.appでGmailのアカウントのメールを取得するのに時間がかかったり、障害が発生するようになり、「とてもGmailを使っていられない」と感じました。

その結果、Gmailにこだわる必要もなかったので、Gmailアカウントは使わないようにしていました。macOS+Gmailだとトラブルの予感しかしません。

久しぶりにGmailのアカウントを使う必要があり、仕方なく使っていたのですが………やっぱりトラブルに遭遇。

ファイルが添付されたメールでも、受信メッセージ一覧に添付ファイルの表示が一切出ません(macOS 10.12.6+Mail 10.3)。気づいたときには久しぶりに震えましたが、再発防止のためには何かプログラム的なチェックを行う必要があります。

そこで、冒頭で説明したように「Mail.app上で選択中のメールに添付ファイルが存在しているものは、背景色を赤くする」というAppleScriptを作成した次第です。


▲Mail.appのメッセージ一覧(Classicレイアウト)で、本Scriptを用いて添付ファイルチェックを行なったところ。添付メッセージを検出して濃い赤で塗られているメッセージでも、Mail.appの添付ファイルインジケータに添付ファイルがあるように表示されない。Gmailアカウント以外でこのような症状に遭遇した経験はありません

とりあえず、「選択中のメッセージ」を処理するように書いてテストしましたが、Mail.appのメール受信時に実行するメールアクションで実行することが理想です。

ただ、macOS 10.13上ではMail関連(とくにメールアクション)がまともに動いていないので、「見なかったこと」にして、macOS 10.14上ではまともになっていることを祈るばかりです。そういう、macOS 10.13のような壊れ環境でも実行できるようにこのような(メールアクションではない形式の)Scriptで書いてみたという次第です。

AppleScript名:選択中のメッセージに添付ファイルがあれば赤く塗る.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/26
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

tell application "Mail"
  set aSel to selection
  
if aSel = {} then return
  
  
repeat with i in aSel
    set j to contents of i
    
    
set attList to mail attachment of j
    
    
set attNames to {}
    
repeat with ii in attList
      set jj to contents of ii
      
set aName to name of jj
      
set the end of attNames to aName
    end repeat
    
    
if attList is not equal to {} then
      set aLen to (length of attList)
      
display notification "Attached " & (aLen as string) & retMultiple(aLen) of me & "….." & retStrFromArrayWithDelimiter(attNames, ", ") of me
      
      
set background color of j to red
    end if
    
  end repeat
  
end tell

on retMultiple(aLen)
  if aLen = 0 then
    return " no item"
  else if aLen = 1 then
    return " item"
  else
    return " items"
  end if
end retMultiple

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

★Click Here to Open This Script 

Posted in list | Tagged 10.12savvy Mail NSArray | Leave a comment

青空文庫のテキストのルビタグを超高速削除

Posted on 3月 18, 2019 by Takaaki Naganoya

青空文庫のテキストのルビタグをすべて削除するAppleScriptです。

CotEditorでオープン中の青空文庫のテキストからルビタグを削除し、元のドキュメントに置換結果を反映させます。

テストに使用したのは、夏目漱石の「こころ」のテキストです。上記ページの「テキストファイル(ルビあり)」をダウンロードして、Zipアーカイブを展開して使用しました。

ファイルサイズ373KB、当該部分4,570箇所。開始文字「《」、終了文字「》」で囲われたエリアをすべて削除するという、AppleScriptにはあからさまに不得意そうな処理で、最初に書いたAppleScriptでは1分半以上かかっていました。内容は、おおよそ常識的なサブルーチンを組み合わせてループで回しただけです。わざと遅くなるように組んだりはしていません。

これを、

 (1)CotEditorからの文章テキストの取得
 (2)置換当該箇所のリストアップ
 (3)文字置換
 (4)CotEditorへの文章テキストの転送

の4つのステージに分け、それぞれ処理時間を計測。すると、(1)、(2)、(4)については1秒かかるかかからないかぐらいの速度で実行していることが判明。圧倒的に(3)文字置換の処理に時間がかかっていました。

もともと文字置換には、AppleScript処理系最速のtext item delimitersを用いるサブルーチンを使用していました。これ以上、この方向に頑張っても速く処理することはできません。一応、ダメ元で4,570個の要素を持つ巨大なtext item delimitersを作成し一括処理できないか試してみたものの、さすがに処理系のキャパシティを超過しているようで処理が戻ってきません(迷走状態)。完全にお手上げです。

そこで、AppleScriptの処理系に依存したtext item delimitersによる処理をやめ、メモリ管理効率がよくないAppleScriptのstring型のデータで保持することをやめ、置換のたびにAppleScriptのstring型に変換(cast)することをやめ、置換中は最初から最後までNSMutableStringで管理するようにしました。

このように大幅に書き換えたところ、トータルで3.58秒で処理終了するようになりました。

すべての置換が終了したあとにNSMutableStringをAppleScriptのstringに変換し、CotEditorの最前面のドキュメントに結果を転送しています。

AppleScript名:青空文庫のテキストのルビタグを削除
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/18
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property NSScanner : a reference to current application’s NSScanner
property NSOrderedSet : a reference to current application’s NSOrderedSet
property NSMutableString : a reference to current application’s NSMutableString
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch

tell application "CotEditor"
  tell front document
    set aCon to contents
  end tell
end tell

set bCon to trimStrFromTo(aCon, "《", "》") of me

tell application "CotEditor"
  tell front document
    set contents to bCon
  end tell
end tell

–開始文字と終了文字に囲われた文字列をすべて削除する
on trimStrFromTo(aParamStr, fromStr, toStr)
  script hsAry
    property anArray : {}
    
property curStr : ""
  end script
  
  
set theScanner to NSScanner’s scannerWithString:aParamStr
  
set (anArray of hsAry) to {}
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {aResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
theScanner’s scanString:fromStr intoString:(missing value)
    
    
set {bResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to ""
    
    
theScanner’s scanString:toStr intoString:(missing value)
    
set the end of (anArray of hsAry) to (fromStr & theValue & toStr)
  end repeat
  
  
–Case: Not found
  
if length of (anArray of hsAry) = 0 then return aParamStr
  
  
–Uniquefy
  
set (anArray of hsAry) to makeUniqueListFrom((anArray of hsAry)) of me
  
  
–Replace strings as NSMutableString
  
set (curStr of hsAry) to NSMutableString’s stringWithString:aParamStr
  
repeat with i in (anArray of hsAry)
    set j to contents of i
    
set (curStr of hsAry) to ((curStr of hsAry)’s stringByReplacingOccurrencesOfString:(j) withString:"" options:(NSRegularExpressionSearch) range:{location:0, |length|:((curStr of hsAry)’s |length|())})
  end repeat
  
  
return (curStr of hsAry) as string
end trimStrFromTo

–1D Listをユニーク化(重複削除)
on makeUniqueListFrom(theList)
  set theSet to NSOrderedSet’s orderedSetWithArray:theList
  
return (theSet’s array()) as list
end makeUniqueListFrom

★Click Here to Open This Script 

Posted in list Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy CotEditor NSMutableString NSOrderedSet NSRegularExpressionSearch NSScanner | 2 Comments

アラートダイアログ上にBrowser+Map Viewを表示 v2

Posted on 3月 17, 2019 by Takaaki Naganoya

ダイアログ上に表示したNSBrowserで都道府県→都道府県別データを選択し、選択したデータの位置情報をMap Viewに表示するAppleScriptの改良版です。

初版掲載時のおかしな挙動を減らし、地図種別の切り替えができるようになっています。本掲載リストだけだと動作が完結しないため、ライブラリを含んだスクリプトバンドルをダウンロードして実行してください。下記リストは「参考までに」掲載しているものです。

–> Download whole Script bundle with Library

前バージョンでは、NSBrowser上でデータが存在していない箇所に空白のセルが表示され、クリックするとクラッシュするという状態でした。本バージョンでもたまに出てくるので完全ではないのですが、

 ・予想どおりデータの行数をカウントするハンドラで絞り込みを行うPredicates文の文字列に問題があった
 ・データに半角のシングルクォートが入っていて、これによってデータの絞り込みに問題が出た

という問題を解消しました。前者はこまめにstringにcastし、後者は全角文字に置き換えました。

それでもまだ問題が出るケースがあるので、まだしばらく実際に使いつつ様子見といったところでしょうか。

AppleScript名:アラートダイアログ上にBrowser+Map Viewを表示 v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/10
—
–  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 "MapKit"
use scripting additions
use skLib : script "senjoNoKizunaLib"

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 NSBrowser : a reference to current application’s NSBrowser
property MKMapView : a reference to current application’s MKMapView
property NSScrollView : a reference to current application’s NSScrollView
property NSMutableArray : a reference to current application’s NSMutableArray
property MKMapTypeHybrid : a reference to current application’s MKMapTypeHybrid
property MKMapTypeSatellite : a reference to current application’s MKMapTypeSatellite
property MKMapTypeStandard : a reference to current application’s MKMapTypeStandard
property NSSegmentedControl : a reference to current application’s NSSegmentedControl
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn
property NSSegmentStyleTexturedRounded : a reference to current application’s NSSegmentStyleTexturedRounded

property zLevel : 17
property aMaxViewWidth : 1000
property aMaxViewHeight : 500
property theResult : 0
property returnCode : 0
property theDataSource : {}
property aSelection : {}
property aMapView : missing value
property aBrowser : missing value
property skDataList : {}

property prefList : {"北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県", "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県", "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県"}

if my skDataList = {} then
  set my skDataList to current application’s NSMutableArray’s arrayWithArray:(getSenjoNokizunaGameCenterDataList() of skLib)
end if

set tmpLen to length of (my skDataList as list)

set aSelection to {}

set paramObj to {myMessage:"Choose a Game Center", mySubMessage:("Choose an appropriate Game Center from list (" & tmpLen as string) & ") to play Senjo-no-Kizuna"}

my performSelectorOnMainThread:"chooseItemByBrowser:" withObject:(paramObj) waitUntilDone:true
if (my returnCode as number) = 1001 then error number -128

return my aSelection
–> {loc_id:"QIEXj9er5QSA_Y42-OjPNg", gcName:"THE 3RD PLANET ジャングルパーク鹿児島", latitude:31.5703088, longitude:130.5653137, address:"鹿児島県 鹿児島市 与次郎 1-11-1 フレスポジャングルパーク2F"}

on chooseItemByBrowser:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
  
— create a view
  
set theView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aMaxViewWidth, aMaxViewHeight))
  
set aMapView to MKMapView’s alloc()’s initWithFrame:(current application’s NSMakeRect(410, 30, aMaxViewWidth – 410, aMaxViewHeight – 30))
  
tell aMapView
    its setMapType:(MKMapTypeStandard)
    
its setZoomEnabled:true
    
its setScrollEnabled:true
    
its setPitchEnabled:true
    
its setRotateEnabled:true
    
its setShowsCompass:true
    
its setShowsZoomControls:true
    
its setShowsScale:true
    
its setShowsUserLocation:true
    
its setDelegate:me
  end tell
  
  
— make browser view with scroll view
  
set aScrollWithTable to makeBrowserView(prefList, 400, aMaxViewHeight) of me
  
  
–Segmented Controlをつくる
  
set segTitleList to {"Map", "Satellite", "Satellite + Map"}
  
set aSeg to makeSegmentedControl(segTitleList, 410, 0, 150, 20) of me
  
  
–Compose Views in NSView
  
theView’s setSubviews:{aScrollWithTable, aMapView, aSeg}
  
  
— 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:theView
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
end chooseItemByBrowser:

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

on makeBrowserView(aList as list, aWidth as number, aHeight as number)
  set (my theDataSource) to NSMutableArray’s arrayWithArray:aList
  
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
set aBrowser to NSBrowser’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
  
aBrowser’s setDelegate:(me)
  
aBrowser’s setTarget:(me)
  
aBrowser’s setAction:"browserCellSelected:"
  
aBrowser’s setMinColumnWidth:120
  
aBrowser’s setSeparatesColumns:true
  
aBrowser’s setMaxVisibleColumns:2
  
aBrowser’s setAutohidesScroller:true
  
aBrowser’s setTakesTitleFromPreviousColumn:true
  
–aBrowser’s setBackgroundColor:(NSColor’s grayColor())
  
  
aScroll’s setDocumentView:aBrowser
  
aBrowser’s enclosingScrollView()’s setHasHorizontalScroller:true
  
aBrowser’s enclosingScrollView()’s setHasVerticalScroller:true
  
  
return aScroll
end makeBrowserView

–NSBrowser Event Handlers
on browser:aView numberOfRowsInColumn:aColumn
  if aColumn = 0 then
    return my theDataSource’s |count|()
  else if aColumn = 1 then
    set aPath to (text 2 thru -1 of ((aView’s |path|()) as string)) as string –ここが問題だったもよう
    
set tmpArray to (my filterRecListByLabel1(skDataList, "address BEGINSWITH ’" & aPath & "’")) as list
    
return (length of tmpArray)
  else
    return 0
  end if
end browser:numberOfRowsInColumn:

on browser:aView willDisplayCell:(aCell) atRow:(rowIndex as integer) column:(colIndex as integer)
  if colIndex = 0 then
    –Prefectures
    
aCell’s setTitle:((item (rowIndex + 1) of prefList) as string)
    
aCell’s setLeaf:false
    
  else if colIndex = 1 then
    –Each Game Centers in the Prefecture
    
set aPath to text 2 thru -1 of ((aView’s |path|()) as string)
    
set tmpArray to my filterRecListByLabel1(skDataList, "address BEGINSWITH ’" & aPath & "’")
    
set tmpItem to (tmpArray’s objectAtIndex:rowIndex)
    
    
set aGameCenterName to (tmpItem’s gcName) as string
    
aCell’s setTitle:(aGameCenterName)
    
aCell’s setLeaf:true
    
  else if colIndex ≥ 2 then
    error "Wrong NSBrowser status"
  end if
end browser:willDisplayCell:atRow:column:

on browserCellSelected:aSender
  set aPath to my aBrowser’s |path|()
  
set aList to (aPath’s pathComponents()) as list
  
set aLen to length of aList
  
  
if aLen = 3 then
    –set aPref to contents of item 2 of aList
    
set aGc to contents of last item of aList
    
    
set tmpArray to my filterRecListByLabel1(skDataList, "gcName == ’" & aGc & "’")
    
–set tmpArray to my filterRecListByLabel1(skDataList, "gcName == " & aGc)
    
set tmpItem to contents of first item of (tmpArray as list)
    
    
copy tmpItem to my aSelection
    
    
set aLatitude to (latitude of tmpItem) as real
    
set aLongitude to (longitude of tmpItem) as real
    
    
tell aMapView
      set aLocation to current application’s CLLocationCoordinate2DMake(aLatitude, aLongitude)
      
its setCenterCoordinate:aLocation zoomLevel:(zLevel) animated:false
    end tell
    
  end if
end browserCellSelected:

–NSArrayに入れたNSDictionaryを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList, aPredicate as string)
  set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aRecList’s filteredArrayUsingPredicate:aPredicate
  
return filteredArray
end filterRecListByLabel1

–Segmented Controlをつくる
on makeSegmentedControl(titleList, startX, startY, aWidth, aHeight)
  set aLen to length of titleList
  
  
set aSeg to NSSegmentedControl’s alloc()’s init()
  
aSeg’s setSegmentCount:aLen
  
  
set aCount to 0
  
repeat with i in titleList
    set j to contents of i
    (
aSeg’s setLabel:j forSegment:aCount)
    
set aCount to aCount + 1
  end repeat
  
  
aSeg’s setTranslatesAutoresizingMaskIntoConstraints:false
  
aSeg’s setSegmentStyle:(NSSegmentStyleTexturedRounded)
  
aSeg’s setFrame:(current application’s NSMakeRect(startX, startY, aWidth, aHeight))
  
aSeg’s setTrackingMode:0
  
aSeg’s setTarget:me
  
aSeg’s setAction:"clickedSeg:"
  
aSeg’s setSelectedSegment:0
  
  
return aSeg
end makeSegmentedControl

–Segmented Controlのクリック時のイベントハンドラ
on clickedSeg:aSender
  set aSel to aSender’s selectedSegment()
  
set tList to {MKMapTypeStandard, MKMapTypeSatellite, MKMapTypeHybrid}
  
set tmpType to contents of item (aSel + 1) of tList
  
  
aMapView’s setMapType:(tmpType)
  
  
set selSeg to aSel
end clickedSeg:

★Click Here to Open This Script 

Posted in geolocation GUI list Map regexp | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy MKMapTypeHybrid MKMapTypeSatellite MKMapTypeStandard MKMapView NSAlert NSAlertSecondButtonReturn NSBrowser NSColor NSMutableArray NSRunningApplication NSScrollView NSSegmentedControl NSSegmentStyleTexturedRounded NSView | Leave a comment

アラートダイアログ上にBrowser+Map Viewを表示

Posted on 3月 12, 2019 by Takaaki Naganoya

ダイアログ上に表示したNSBrowserで都道府県→都道府県別データを選択し、選択したデータの位置情報をMap Viewに表示するAppleScriptです。

# 2021年11月末にアーケードゲーム「戦場の絆」のサービスが終了したため、Webサイト側からデータ取得する部分の処理は動作しません(Webサイトごとアクセスできなくなったため)


▲macOS 10.12.6上で動作

「OK」ボタンをクリックすると選択したゲームセンターの諸元情報を返してきます。リスト一覧から名称を選択し、途中でその位置情報を地図で見られるという程度のインタフェースです。

NSBrowserを利用したUIの習作で、Web上の「戦場の絆」公式ページから導入店舗の情報を取得し(このあたりライブラリにまとめて部品化)、データを都道府県別にしぼりこんでNSBrowser上で選択できるようにしてみました。


▲macOS 10.13.6上で動作


▲macOS 10.14.4上で動作(Light Mode)


▲macOS 10.14.4上で動作(Dark Mode)

ぱっと見は「何かすごいもの」に見えるのですが、キーワード検索が実装されていないため、データしぼりこみに柔軟性がないと感じるものです。

NSBrowserをAppleScriptから利用した場合に、Xcodeを利用しないとこのレベルの単純なデータ階層固定でデータしぼりこみを行う程度のものしか作りにくいところでしょうか。不可能とは言わないものの、「普通にXcode上で作れば?」という話になると思います。

さらに、該当するカラムの行数を返すハンドラでデータをしぼりこんでレコード数を返しているものの、いまひとつここがうまく動いていない印象を受けます。NSBrowser上で「存在しないデータのような何か」をクリックできるケースがあり、それをクリックすると実行環境(スクリプトエディタとか、Script Debuggerとか)ごとクラッシュします。

→ 原因がわかりました。問題箇所も想像どおり。目下、修正版で謎のゴースト行も出てこなくなったので、あとで修正版を掲載しておきます。問題が2つあって、1つがPredicates文をas stringですべてcastしていなかった件、もうひとつは、、、副次的に発生した問題で、愛知県のデータに「M’s」という半角クォート文字を含むものがあって、これでPredicates文が中断されてしまった問題。半角シングルクォートを全角に置き換えるとかいろいろ手を加える必要がありました。

本Script全体をダウンロードして、スクリプトエディタ上で実行すると、最初に「戦場の絆」公式ホームページの日本国内の店舗情報をスキャンして店舗情報を抽出するため、サーバーの混雑具合やネットワーク回線速度にも左右されますが、自分の環境では5〜15秒程度でデータの取得が終了し、ダイアログが表示されます。

–> Download whole Script bundle with Library

AppleScript名:アラートダイアログ上にBrowser+Map Viewを表示.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/10
—
–  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 "MapKit"
use scripting additions
use skLib : script "senjoNoKizunaLib"

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 NSBrowser : a reference to current application’s NSBrowser
property MKMapView : a reference to current application’s MKMapView
property NSScrollView : a reference to current application’s NSScrollView
property NSMutableArray : a reference to current application’s NSMutableArray
property MKMapTypeStandard : a reference to current application’s MKMapTypeStandard
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn

property zLevel : 17
property aMaxViewWidth : 1000
property aMaxViewHeight : 500
property theResult : 0
property returnCode : 0
property theDataSource : {}
property aSelection : {}
property aMapView : missing value
property aBrowser : missing value
property skDataList : {}

property prefList : {"北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県", "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県", "石川県", "福井県", "山梨県", "長野県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県", "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県"}

if my skDataList = {} then
  set my skDataList to current application’s NSMutableArray’s arrayWithArray:(getSenjoNokizunaGameCenterDataList() of skLib)
end if

set aSelection to {}

set paramObj to {myMessage:"Choose a Game Center", mySubMessage:"Choose an appropriate Game Center from list to play Senjo-no-Kizuna"}

my performSelectorOnMainThread:"chooseItemByBrowser:" withObject:(paramObj) waitUntilDone:true
return my aSelection
–> {loc_id:"QIEXj9er5QSA_Y42-OjPNg", gcName:"THE 3RD PLANET ジャングルパーク鹿児島", latitude:31.5703088, longitude:130.5653137, address:"鹿児島県 鹿児島市 与次郎 1-11-1 フレスポジャングルパーク2F"}

on chooseItemByBrowser:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
  
— create a view
  
set theView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aMaxViewWidth, aMaxViewHeight))
  
set aMapView to MKMapView’s alloc()’s initWithFrame:(current application’s NSMakeRect(410, 0, aMaxViewWidth – 410, aMaxViewHeight))
  
tell aMapView
    its setMapType:(MKMapTypeStandard)
    
its setZoomEnabled:true
    
its setScrollEnabled:true
    
its setPitchEnabled:true
    
its setRotateEnabled:true
    
its setShowsCompass:true
    
its setShowsZoomControls:true
    
its setShowsScale:true
    
its setShowsUserLocation:true
    
its setDelegate:me
  end tell
  
  
— make browser view with scroll view
  
set aScrollWithTable to makeBrowserView(prefList, 400, aMaxViewHeight) of me
  
  
–Compose Views in NSView
  
theView’s setSubviews:{aScrollWithTable, aMapView}
  
  
— 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:theView
  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
end chooseItemByBrowser:

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

on makeBrowserView(aList as list, aWidth as number, aHeight as number)
  set (my theDataSource) to NSMutableArray’s arrayWithArray:aList
  
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
set aBrowser to NSBrowser’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
  
aBrowser’s setDelegate:(me)
  
aBrowser’s setTarget:(me)
  
aBrowser’s setAction:"browserCellSelected:"
  
aBrowser’s setMinColumnWidth:120
  
aBrowser’s setSeparatesColumns:true
  
aBrowser’s setMaxVisibleColumns:2
  
aBrowser’s setAutohidesScroller:true
  
aBrowser’s setTakesTitleFromPreviousColumn:true
  
–aBrowser’s setBackgroundColor:(NSColor’s grayColor())
  
  
aScroll’s setDocumentView:aBrowser
  
aBrowser’s enclosingScrollView()’s setHasHorizontalScroller:true
  
aBrowser’s enclosingScrollView()’s setHasVerticalScroller:true
  
  
return aScroll
end makeBrowserView

–NSBrowser Event Handlers
–ここ、いまひとつきちんと動いていないかも???
on browser:aView numberOfRowsInColumn:aColumn
  if aColumn = 0 then
    return my theDataSource’s |count|()
  else if aColumn = 1 then
    set aPath to text 2 thru -1 of ((aView’s |path|()) as string)
    
set tmpArray to (my filterRecListByLabel1(skDataList, "address BEGINSWITH ’" & aPath & "’")) as list
    
return (length of tmpArray)
  end if
end browser:numberOfRowsInColumn:

on browser:aView willDisplayCell:(aCell) atRow:(rowIndex as integer) column:(colIndex as integer)
  if colIndex = 0 then
    –Prefectures
    
aCell’s setTitle:((item (rowIndex + 1) of prefList) as string)
    
aCell’s setLeaf:false
    
  else if colIndex = 1 then
    –Each Game Centers in the Prefecture
    
set aPath to text 2 thru -1 of ((aView’s |path|()) as string)
    
set tmpArray to my filterRecListByLabel1(skDataList, "address BEGINSWITH ’" & aPath & "’")
    
set tmpItem to (tmpArray’s objectAtIndex:rowIndex)
    
    
set aGameCenterName to (tmpItem’s gcName) as string
    
aCell’s setTitle:(aGameCenterName)
    
aCell’s setLeaf:true
    
  end if
end browser:willDisplayCell:atRow:column:

on browserCellSelected:aSender
  set aPath to my aBrowser’s |path|()
  
set aList to (aPath’s pathComponents()) as list
  
set aLen to length of aList
  
  
if aLen = 3 then
    –set aPref to contents of item 2 of aList
    
set aGc to contents of last item of aList
    
    
set tmpArray to my filterRecListByLabel1(skDataList, "gcName == ’" & aGc & "’")
    
set tmpItem to contents of first item of (tmpArray as list)
    
    
copy tmpItem to my aSelection
    
    
set aLatitude to (latitude of tmpItem) as real
    
set aLongitude to (longitude of tmpItem) as real
    
    
tell aMapView
      set aLocation to current application’s CLLocationCoordinate2DMake(aLatitude, aLongitude)
      
its setCenterCoordinate:aLocation zoomLevel:(zLevel) animated:false
    end tell
    
  end if
end browserCellSelected:

–NSArrayに入れたNSDictionaryを、指定の属性ラベルの値で抽出
on filterRecListByLabel1(aRecList, aPredicate as string)
  set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aRecList’s filteredArrayUsingPredicate:aPredicate
  
return filteredArray
end filterRecListByLabel1

★Click Here to Open This Script 

Posted in geolocation list Record | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy MKMapTypeStandard MKMapView NSAlert NSAlertSecondButtonReturn NSBrowser NSColor NSMutableArray NSRunningApplication NSScrollView NSView | Leave a comment

レコード in リストのラベルを置換する

Posted on 3月 9, 2019 by Takaaki Naganoya

レコード in リスト(配列に入れたレコード)のラベルを置換するAppleScriptです。

普通にプログラムを書いているととても必要な処理なので、Cocoaに最初から用意されているような気がするのですが、自分が知らないだけなんでしょうか?

# レコードのラベル部分を書き換えるのではなく、アクセス時の「valueForKeyPath」で指定するパス指定文字列のほうを書き換えたほうがまっとうな処理な気がします

AppleScript名:レコード in リストのラベルを置換する
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/09
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set aList to {{gcName:"ディノスパーク札幌中央", address:"北海道 札幌市中央区 南三条西 1-8 ゲオディノス札幌中央ビル", loc_id:"94fQ-vXfZIWbNCIhWjMEEQ", latitude:43.0568243, longitude:141.3567126}, {gcName:"アドアーズ狸小路", address:"北海道 札幌市中央区 南三条西 4-12-1 アルシュビル5・6F", loc_id:"BaAw8srtioGkso463Jlrpg", latitude:43.0569575, longitude:141.352524}}

set labelRepList to {{"gcName", "latitude", "longitude"}, {"placeName", "aLat", "aLong"}}
set origLabel to contents of first item of labelRepList
set toLabel to contents of second item of labelRepList

set rList to repRecListLabels(aList, origLabel, toLabel) of me
–> {{placeName:"ディノスパーク札幌中央", loc_id:"94fQ-vXfZIWbNCIhWjMEEQ", aLong:141.3567126, address:"北海道 札幌市中央区 南三条西 1-8 ゲオディノス札幌中央ビル", aLat:43.0568243}, {placeName:"アドアーズ狸小路", loc_id:"BaAw8srtioGkso463Jlrpg", aLong:141.352524, address:"北海道 札幌市中央区 南三条西 4-12-1 アルシュビル5・6F", aLat:43.0569575}}

on repRecListLabels(aList as list, origLabel as list, toLabel as list)
  script spdRecList
    property newList : {}
    
property aList : {}
  end script
  
  
set (newList of spdRecList) to {}
  
set (aList of spdRecList) to aList
  
  
repeat with i in (aList of spdRecList)
    set aDict to (current application’s NSMutableDictionary’s dictionaryWithDictionary:i)
    
set allKeys to (aDict’s allKeys()) as list
    
set bDict to (current application’s NSMutableDictionary’s new())
    
    
repeat with ii in allKeys
      set jj to contents of ii
      
set tmpVal to (aDict’s valueForKey:jj)
      
      
if jj is in origLabel then
        using terms from scripting additions
          set aRes to offset of jj in origLabel
        end using terms from
        
set aLabel to contents of item aRes of toLabel
        (
bDict’s setObject:tmpVal forKey:aLabel)
      else
        (bDict’s setObject:tmpVal forKey:jj)
      end if
      
    end repeat
    
    
set bDict to bDict as record
    
set the end of (newList of spdRecList) to bDict
  end repeat
  
  
return (newList of spdRecList)
end repRecListLabels

on offset of bArg in anArg
  set aClass to class of anArg
  
set bClass to class of bArg
  
  
if {aClass, bClass} = {text, text} then –case 1
    return getOffset(anArg, bArg) of me
  else if {aClass, bClass} = {list, list} then –case 2 (The target case)
    return execOffsetList(bArg, anArg) of me
  else if {aClass, bClass} = {text, list} then –case 3 (Illegular case)
    return execOffsetList(bArg, {anArg}) of me
  else if {aClass, bClass} = {list, text} then –case 4 (Illegular case)
    return execOffsetList({bArg}, anArg) of me
  end if
end offset

–1D List同士のoffset演算を行うルーチンの本体
on execOffsetList(aList as list, bList as list)
  set resList to {}
  
repeat with i in aList
    set j to contents of i
    
set aCount to 1
    
    
repeat with ii in bList
      set jj to contents of ii
      
if jj = j then
        set the end of resList to aCount
        
exit repeat
      end if
      
set aCount to aCount + 1
    end repeat
  end repeat
  
  
–見つかったItem No.が連続値かどうかチェック
  
set sRes to chkSequential(resList) of me
  
if sRes = true then
    return contents of first item of resList
  else
    return false
  end if
end execOffsetList

–与えられた1D Listが連続値かどうかをチェックする
on chkSequential(aList as list)
  if length of aList = 1 then return true
  
if aList = {} then return false
  
  
set aFirst to first item of aList
  
set aList to rest of aList
  
  
repeat with i in aList
    set j to contents of i
    
if j is not equal to (aFirst + 1) then
      return false
    end if
    
copy j to aFirst
  end repeat
  
  
return true
end chkSequential

–テキスト同士のoffset ofを(2.5x fasterで)実行する
on getOffset(str as string, searchStr as string)
  set d to divideBy(str, searchStr)
  
if (count d) is less than 2 then return 0
  
return (length of item 1 of d) + 1
end getOffset

on divideBy(str, separator)
  set delSave to AppleScript’s text item delimiters
  
set the AppleScript’s text item delimiters to separator
  
set strItems to every text item of str
  
set the AppleScript’s text item delimiters to delSave
  
return strItems
end divideBy

★Click Here to Open This Script 

Posted in list Record | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy | Leave a comment

アラートダイアログ上に縦棒グラフを表示

Posted on 3月 4, 2019 by Takaaki Naganoya

アラートダイアログ上に縦棒グラフを表示するAppleScriptです。

グラフを作成するのであれば、NumbersやKeynoteなどを使ったほうがいいですが、簡単にデータの傾向だけダイアログ上で見せたいような場合のために作成した部品です。

NSImageViewの表示実験がしたかっただけなので、別に他の言語処理系で作成したグラフ画像を読み込んでNSImageに読み込んでからNSIMageViewで表示するぐらいの使い方でもぜんぜんOKでしょう。

WebViewを表示してインタラクティブなグラフ(マウスカーソルに反応してアニメーション表示を行うとか)を表示するといった使い方もよさそうです(アプリケーション化しないとダメかも)。

macOS 10.14上のDark Modeについては、実行環境によって対応度が異なります。


▲macOS 10.14上のDark Modeで実行したところ。左側からスクリプトエディタ、AppleScriptアプレット、スクリプトメニュー


▲macOS 10.14上のDark Modeで実行したところ。左側からScript Debugger、Script DebuggerからSD拡張アプレットで書き出したもの

グラフの描画については、色指定をblackColor()とかgrayColor()などと指定せずに、Dark Mode時に制御されるUI色で指定しておく必要があることでしょう。

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

property NSFont : a reference to current application’s NSFont
property NSAlert : a reference to current application’s NSAlert
property NSColor : a reference to current application’s NSColor
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSScreen : a reference to current application’s NSScreen
property NSDictionary : a reference to current application’s NSDictionary
property NSBezierPath : a reference to current application’s NSBezierPath
property NSImageView : a reference to current application’s NSImageView
property NSMutableArray : a reference to current application’s NSMutableArray
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName
property NSImageScaleProportionallyUpOrDown : a reference to current application’s NSImageScaleProportionallyUpOrDown

property returnCode : 0

set plotData to {20, 30, 100, 80, 150, 90}
set paramObj to {myMessage:"This is a graph", mySubMessage:"Browse bar graph", aPlotData:plotData}
my performSelectorOnMainThread:"displayBarGraph:" withObject:(paramObj) waitUntilDone:true

on displayBarGraph:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTList to (aPlotData of paramObj) as list
  
  
— make NSIMageView with graph image
  
set anImage to makeGraphImage(aTList, 300, 200) of me
  
set anImageView to NSImageView’s alloc()’s initWithFrame:{origin:{x:0.0, y:0.0}, |size|:{width:300.0, height:200.0}}
  
anImageView’s setImageScaling:(NSImageScaleProportionallyUpOrDown)
  
anImageView’s setEditable:false
  
anImageView’s setImage:anImage
  
anImageView’s display()
  
  
— 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:anImageView
  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
end displayBarGraph:

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

on makeGraphImage(plotData, aWidth, aHeight)
  set innerGapL to 30
  
set innerGapU to 10
  
set innerGapR to 20
  
set innerGapD to 20
  
set barGap to 10
  
  
–パラメータから下地になる画像を作成する
  
set aSize to current application’s NSMakeSize(aWidth, aHeight)
  
set anImage to NSImage’s alloc()’s initWithSize:aSize
  
  
–各種パラメータの計算
  
–copy plotArea to {plotWidth, plotHeight}
  
set itemNum to count every item of plotData
  
set barThickness to (aWidth – (itemNum * barGap * 2)) div itemNum
  
  
–プロットデータの最大値
  
set anArray to NSArray’s arrayWithArray:plotData
  
set aYmax to (anArray’s valueForKeyPath:"@max.self")’s intValue()
  
set aMaxYVal to aHeight – innerGapU – innerGapD
  
set aYPlotArea to aHeight – innerGapU – innerGapD – 20
  
set aYUnit to aYPlotArea / aYmax
  
  
–数値データをもとに描画データを組み立てる
  
set drawList to {}
  
  
set startX to innerGapL
  
copy startX to origX
  
  
repeat with i in plotData
    set the end of drawList to current application’s NSMakeRect(startX, innerGapD, barThickness, innerGapD + (i * aYUnit))
    
set startX to startX + barThickness + barGap
  end repeat
  
  
–グラフ塗りつぶし処理呼び出し
  
set fillColor to (NSColor’s colorWithCalibratedRed:0.1 green:0.1 blue:0.1 alpha:0.3)
  
set resImage to drawImageWithColorFill(anImage, drawList, fillColor) of me
  
  
–数値データ(文字)をグラフィックに記入
  
set fillColor2 to NSColor’s blackColor()
  
set resImage to drawImageWithString(resImage, drawList, fillColor2, plotData, "Eurostile Bold", 20.0) of me
  
  
–補助線を引く
  
set fillColor3 to (NSColor’s colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:0.8)
  
set aVertical to current application’s NSMakeRect(origX, innerGapD, aWidth – innerGapL – innerGapR, 1)
  
set aHorizontal to current application’s NSMakeRect(origX, innerGapD, 1, aHeight – innerGapU – innerGapD)
  
set draw2List to {aVertical, aHorizontal}
  
set resImage to drawImageWithColorFill(resImage, draw2List, fillColor3) of me
  
  
return resImage
end makeGraphImage

–NSImageに対して文字を描画する
on drawImageWithString(anImage, drawList, fillColor, dataList, aPSFontName, aFontSize)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
set aDict to (NSDictionary’s dictionaryWithObjects:{NSFont’s fontWithName:aPSFontName |size|:aFontSize, fillColor} forKeys:{NSFontAttributeName, NSForegroundColorAttributeName})
  
  
anImage’s lockFocus() –描画開始
  
  
set aLen to length of drawList
  
repeat with i from 1 to aLen
    set i1 to contents of item i of drawList
    
    
set v2 to system attribute "sys2"
    
if v2 ≤ 12 then
      –To macOS 10.12.x
      
set origX to (x of origin of i1) / retinaF
      
set origY to (y of origin of i1) / retinaF
      
set sizeX to (width of |size| of i1) / retinaF
      
set sizeY to (height of |size| of i1) / retinaF
      
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
    else
      –macOS 10.13 or later
      
set origX to (item 1 of item 1 of i1) / retinaF
      
set origY to (item 2 of item 1 of i1) / retinaF
      
set sizeX to (item 1 of item 2 of i1) / retinaF
      
set sizeY to (item 2 of item 2 of i1) / retinaF
      
set theRect to {{origX, origY}, {sizeX, sizeY}}
    end if
    
    
set aString to (current application’s NSString’s stringWithString:((contents of item i of dataList) as string))
    (
aString’s drawAtPoint:(current application’s NSMakePoint(origX + (sizeX / 2), sizeY)) withAttributes:aDict)
  end repeat
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end drawImageWithString

–NSImageに対して矩形を塗りつぶす
on drawImageWithColorFill(anImage, drawList, fillColor)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
anImage’s lockFocus() –描画開始
  
  
repeat with i in drawList
    set v2 to system attribute "sys2"
    
if v2 ≤ 12 then
      –To macOS 10.12.x
      
set origX to (x of origin of i) / retinaF
      
set origY to (y of origin of i) / retinaF
      
set sizeX to (width of |size| of i) / retinaF
      
set sizeY to (height of |size| of i) / retinaF
      
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
    else
      –macOS 10.13 or later
      
set origX to (item 1 of item 1 of i) / retinaF
      
set origY to (item 2 of item 1 of i) / retinaF
      
set sizeX to (item 1 of item 2 of i) / retinaF
      
set sizeY to (item 2 of item 2 of i) / retinaF
      
set theRect to {{origX, origY}, {sizeX, sizeY}}
    end if
    
    
set theNSBezierPath to NSBezierPath’s bezierPath
    (
theNSBezierPath’s appendBezierPathWithRect:theRect)
    
    
fillColor’s |set|() –色設定
    
theNSBezierPath’s fill() –ぬりつぶし
  end repeat
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end drawImageWithColorFill

★Click Here to Open This Script 

Posted in GUI Image list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSAlertSecondButtonReturn NSArray NSBezierPath NSBitmapImageRep NSColor NSDictionary NSFont NSFontAttributeName NSForegroundColorAttributeName NSImage NSImageScaleProportionallyUpOrDown NSImageView NSMutableArray NSRunningApplication NSScreen NSString | Leave a comment

「戦場の絆」の日本国内の設置店舗数をカウント

Posted on 3月 3, 2019 by Takaaki Naganoya

アーケードゲーム「戦場の絆」が設置されている日本国内のゲームセンターの件数をカウントするAppleScriptです。

指定ページを取得し、本文内容をプレーンテキスト化して正規表現でデータ抽出して加算を行うという処理内容です。

部品がありきたりなので、新規作成部分はほとんどありません(メイン部分だけ)。

AppleScript名:戦場の絆の日本国内の設置店舗数をカウント.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/03/03
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSPredicate : a reference to current application’s NSPredicate
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSAttributedString : a reference to current application’s NSAttributedString
property NSURLComponents : a reference to current application’s NSURLComponents
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSUnicodeStringEncoding : a reference to current application’s NSUnicodeStringEncoding

set aTotal to 0

–「戦場の絆」公式ページに掲載されている店舗一覧の都道府県別コード
set areaList to {"JP-01", "JP-02", "JP-03", "JP-04", "JP-05", "JP-06", "JP-07", "JP-08", "JP-09", "JP-10", "JP-11", "JP-12", "JP-13", "JP-14", "JP-15", "JP-16", "JP-17", "JP-18", "JP-19", "JP-20", "JP-21", "JP-22", "JP-23", "JP-24", "JP-25", "JP-26", "JP-27", "JP-28", "JP-29", "JP-30", "JP-31", "JP-32", "JP-33", "JP-34", "JP-35", "JP-36", "JP-37", "JP-38", "JP-39", "JP-40", "JP-41", "JP-42", "JP-43", "JP-44", "JP-45", "JP-46", "JP-47"}

–「戦場の絆」公式ページに掲載されている店舗一覧のBase URL
set baseURL to "https://gundam-kizuna.jp/locations/list"

–都道府県コードでループ
repeat with i in areaList
  –店舗一覧の都道府県別ページをダウンロード
  
set j to contents of i
  
set aRec to {area:j}
  
set aRes to retURLwithParams(baseURL, aRec) of me
  
set shellText to "curl -s " & aRes
  
set htmlRes to (do shell script shellText)
  
set aPlainText to (HTMLDecode(htmlRes) of me)
  
  
–店舗件数が入っている箇所を抽出して加算
  
set aList to paragraphs of aPlainText
  
set anArray to (NSArray’s arrayWithArray:aList)
  
set aPred to (NSPredicate’s predicateWithFormat:"SELF MATCHES ’[0-9]{1,3}件の店舗が見つかりました。’")
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
  
if bRes is not equal to {} then
    set a to repChar(contents of first item of bRes, "件の店舗が見つかりました。", "") of me
    
try
      set aNum to a as integer
      
set aTotal to aTotal + aNum
    on error
      log {"Error in ", j}
    end try
  end if
end repeat

return aTotal
–> 622 (2019/3/3)

(*
set countryList to {"HKG", "TWN"}–香港と台湾の店舗のコード
set baseURL2 to "https://gundam-kizuna.jp/locations/list"
*)

–パラメータつきURLを作成
on retURLwithParams(aBaseURL as string, aRec as record)
  set aDic to 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 bLen to length of aValList
  
if aLen is not equal to bLen then return false
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to (contents of item i of aKeyList) as string
    
set aVal to (contents of item i of aValList) as string
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

–テキストをHTMLとして解釈しプレーンテキスト化
on HTMLDecode(HTMLString)
  set theString to NSString’s stringWithString:HTMLString
  
set theData to theString’s dataUsingEncoding:(current application’s NSUnicodeStringEncoding)
  
set attStr to NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)
  
set aStr to attStr’s |string|()
  
return aStr as string
end HTMLDecode

–文字置換
on repChar(origText as string, targStr as string, repStr as string)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

★Click Here to Open This Script 

Posted in list regexp Text URL | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSAttributedString NSMutableDictionary NSPredicate NSString NSUnicodeStringEncoding NSURLComponents NSURLQueryItem | Leave a comment

アラートダイアログ上にBrowserを表示

Posted on 3月 2, 2019 by Takaaki Naganoya

アラートダイアログ上にNSBrowserを表示するAppleScriptの試作品です。

# 本Scriptは実験用であって実用性はありません(^ー^;;

Finderに使われていることで認知度は高いものの、使いどころが難しいうえにサンプル・コードも多くないので登場頻度が少ないNSBrowserです。(Mac OS Xの前身である)NEXTSTEPのFile Managerがこんな感じのUIでした。

(個人的に)敷居の高かったNSBrowserも、いろいろ試してみたらいい感じになってきました。現状では単に2次元配列っぽいものにアクセスしているだけですが、ツリー型の構造データを与えるようにするとか、配列から徐々にデータ抽出する(大分類→中分類→小分類)ようにすれば、だいたい期待どおりの動作になるはずです。

NSBrowserも、使いたいときに調べると情報が見つからずに困りますが、用のないときなら調べておくといろいろ大丈夫そうです。

macOS 10.14上のスクリプトエディタ/Script Debugger上で動くようにハンドラをメインスレッド上で明示的に動作させるように変更しましたが、10.14上でだけ独特な挙動があるため要注意です。ただ、テストプログラムなのでそんなものだろうかと。

AppleScript名:アラートダイアログ上にBrowserを表示 v1.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 scripting additions

property NSAlert : a reference to current application’s NSAlert
property NSColor : a reference to current application’s NSColor
property NSBrowser : a reference to current application’s NSBrowser
property NSScrollView : a reference to current application’s NSScrollView
property NSMutableArray : a reference to current application’s NSMutableArray
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn

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

set paramObj to {myMessage:"Choose a data", mySubMessage:"Choose an appropriate data from tree structured data list", aTableList:{{"1", "1.1", "1.1.1"}, {"2", "2.1", "2.1.1"}, {"3", "3.1", "3.1.1"}, {"4", "4.1", "4.1.1"}}}
–set aRes to my chooseItemByBrowser:paramObj
my performSelectorOnMainThread:"chooseItemByBrowser:" withObject:(paramObj) waitUntilDone:true

on chooseItemByBrowser:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTList to (aTableList of paramObj) as list
  
  
— make browser view with scroll view
  
set aScrollWithTable to makeBrowserView(aTList, 500, 200) of me
  
  
— 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:aScrollWithTable
  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
end chooseItemByBrowser:

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

on makeBrowserView(aList as list, aWidth as number, aHeight as number)
  set (my theDataSource) to NSMutableArray’s arrayWithArray:aList
  
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
set aView to NSBrowser’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
  
aView’s setDelegate:(me)
  
aView’s setMinColumnWidth:120
  
aView’s setSeparatesColumns:true
  
–aView’s setMaxVisibleColumns:2
  
aView’s setAutohidesScroller:true
  
–aView’s setBackgroundColor:(NSColor’s grayColor())
  
aView’s setReusesColumns:true
  
  
aScroll’s setDocumentView:aView
  
aView’s enclosingScrollView()’s setHasHorizontalScroller:true
  
aView’s enclosingScrollView()’s setHasVerticalScroller:true
  
  
return aScroll
end makeBrowserView

–NSBrowser Event Handlers
on browser:aView numberOfRowsInColumn:aColumn
  set aCount to my theDataSource’s |count|()
  
return aCount
end browser:numberOfRowsInColumn:

on browser:aView willDisplayCell:(aCell) atRow:rowIndex column:colIndex
  set selRow to (my theDataSource)’s objectAtIndex:(rowIndex)
  
set selCol to selRow’s objectAtIndex:(colIndex)
  
aCell’s setTitle:(selCol as string)
  
  
set colMax to ((my theDataSource)’s objectAtIndex:0)’s |count|()
  
log {"colMax, colIndex", colMax, colIndex}
  
set exMax to (colIndex < colMax – 1) as boolean
  
aCell’s setLeaf:(not exMax)
end browser:willDisplayCell:atRow:column:

★Click Here to Open This Script 

Posted in GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSAlertSecondButtonReturn NSBrowser NSColor NSMutableArray NSRunningApplication NSScrollView | Leave a comment

クリップボード内のRTFをStyled Stringとして解釈して行ごとに分割して画面描画サイズ幅で昇順ソートして再結合

Posted on 2月 27, 2019 by Takaaki Naganoya

クリップボードにコピーしておいた書式つきテキストを、行ごとに画面上の描画サイズで昇順ソートし、再結合してクリップボードに転送するAppleScriptです。

ほぼ毎日使っているAppleScriptの改良版です。

Cocoaの機能を呼び出すと、AppleScript冒頭にproperty宣言文を書くことになりますが(書かなくてもいいんですが)、この宣言文を「プロポーショナルフォントを考慮しつつ実際の画面上の描画サイズを考慮」して短いものから長いものにソートして掲載しています。

ただし、従来のバージョンでは実際のフォントではなく一律に指定フォントで描画したさいの描画幅でソートしていたため、ごくまれに掲載時のリストが短い順になっていませんでした(世界中で誰も気にしてねえよ! ^ー^;)。

これを、実際の指定フォントやサイズを考慮した行単位のスタイル付きテキストに分解し、描画幅でソートしたあとにスタイル付きテキストを再合成するようにしました。

従来のOld Style AppleScriptではスタイル付きテキストの操作は一切できませんでしたが、Cocoaの機能を活用することでこのように自由度の高い処理ができるようになった、という好例です。

なお、本ルーチンによって厳密に描画幅でソートしても、Web掲載時にはWebブラウザのレンダリングのルール(&ユーザー側で指定しているWebブラウザのフォント)が適用されるため、macOS上のアプリケーション上での見た目と若干違いが出るという残念な結果も出ています。

macOS標準装備のスクリプトメニューに入れて呼び出しています。

AppleScript名:クリップボード内のRTFをStyled Stringとして解釈して行ごとに分割して画面描画サイズ幅で昇順ソートして再結合
— Created 2017-04-24 by Shane Stanley
— Modified 2019-02-27 by Takaaki Naganoya
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

property NSFont : a reference to current application’s NSFont
property NSColor : a reference to current application’s NSColor
property NSPasteboard : a reference to current application’s NSPasteboard
property NSAttributedString : a reference to current application’s NSAttributedString
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName

–クリップボードの内容をNSAttributedStringに
set anAttr to my getClipboardASStyledText()
if anAttr = missing value then return

–Split Attributed Strings into lines
set attrList to splitAttributedStringByLines(anAttr) of me

–Sort by Width in ascending
set sortedList to shellSortListAscending(attrList, 1) of me

–Append Attributed Strings
set mutableReturn to NSMutableAttributedString’s alloc()’s initWithString:(return)
set outStr to NSMutableAttributedString’s alloc()’s initWithString:""

repeat with i in sortedList
  copy i to {tmpWidth, tmpAttr}
  (
outStr’s appendAttributedString:tmpAttr)
  
  
set tmpStr to tmpAttr’s |string|() as string
  
if (tmpStr does not end with string id 10) and (tmpStr does not end with string id 13) then
    (outStr’s appendAttributedString:mutableReturn)
  end if
end repeat

— Set Styled String to Clipboard
set theArray to {outStr}
restoreClipboard(theArray) of me

–Clipboardにデータを設定する
on restoreClipboard(theArray as list)
  set thePasteboard to NSPasteboard’s generalPasteboard()
  
thePasteboard’s clearContents()
  
thePasteboard’s writeObjects:theArray
end restoreClipboard

— クリップボードの内容をNSAttributedStringとして取り出して返す
on getClipboardASStyledText()
  try
    set theNSPasteboard to NSPasteboard’s generalPasteboard()
    
set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({NSAttributedString}) options:(missing value)
    
set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0
    
return theNSAttributedString
  on error
    return missing value
  end try
end getClipboardASStyledText

on splitAttributedStringByLines(theStyledText)
  set outList to {}
  
set outAttr to NSMutableAttributedString’s alloc()’s initWithString:""
  
  
set thePureString to theStyledText’s |string|() –pure string from theStyledText
  
set theLength to theStyledText’s |length|()
  
set startIndex to 0
  
  
repeat until (startIndex = theLength)
    set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex}
    
set aText to (thePureString’s substringWithRange:theRange) as string –String
    
    
set aColor to (theAtts’s valueForKeyPath:"NSColor") –Color
    
set aFont to (theAtts’s valueForKeyPath:"NSFont") –Font
    
if aFont is equal to missing value then error "Not font name and size are specified" –Font Name error
    
set aDFontName to aFont’s displayName() –Font Name
    
set aDFontSize to aFont’s pointSize() –Font Size
    
    
set tmpAttrStr to generateAttributedString(aText, aDFontName, aDFontSize, aColor) of me
    
outAttr’s appendAttributedString:tmpAttrStr
    
    
if (aText contains return) or (aText contains string id 10) then –CR or LF
      set tmpSize to outAttr’s |size|()
      
set tmpWidth to retWidthFromSize(tmpSize) of me
      
      
set the end of outList to {tmpWidth, outAttr}
      
set outAttr to NSMutableAttributedString’s alloc()’s initWithString:""
    end if
    
    
set startIndex to current application’s NSMaxRange(theRange)
  end repeat
  
  
set tmpSize to outAttr’s |size|()
  
set tmpWidth to retWidthFromSize(tmpSize) of me
  
set the end of outList to {tmpWidth, outAttr}
  
  
return outList
end splitAttributedStringByLines

on retWidthFromSize(tmpSize)
  if class of tmpSize = record then
    –macOS 10.10, 10.11, 10.12
    
set tmpWidth to width of tmpSize
  else
    –macOS 10.13, 10.14
    
set tmpWidth to first item of tmpSize
  end if
  
return tmpWidth
end retWidthFromSize

on generateAttributedString(aStr, aFontPSName, aFontSize, aColor)
  set tmpAttr to NSMutableAttributedString’s alloc()’s initWithString:aStr
  
set aRange to current application’s NSMakeRange(0, tmpAttr’s |length|())
  
set aVal1 to NSFont’s fontWithName:aFontPSName |size|:aFontSize
  
tmpAttr’s beginEditing()
  
tmpAttr’s addAttribute:(NSFontAttributeName) value:aVal1 range:aRange
  
tmpAttr’s addAttribute:(NSForegroundColorAttributeName) value:aColor range:aRange
  
tmpAttr’s endEditing()
  
return tmpAttr
end generateAttributedString

–入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  return sort2DList(a, keyItem, {true}) of me
end shellSortListAscending

–入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  return sort2DList(a, keyItem, {false}) of me
end shellSortListDecending

–2D Listをソート
on sort2DList(aList as list, sortIndexes as list, sortOrders as list)
  load framework
  
–index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換
  
set newIndex to {}
  
repeat with i in sortIndexes
    set j to contents of i
    
set j to j – 1
    
set the end of newIndex to j
  end repeat
  
  
–Sort TypeのListを作成(あえて外部から指定する内容でもない)
  
set sortTypes to {}
  
repeat (length of sortIndexes) times
    set the end of sortTypes to "compare:"
  end repeat
  
  
–Sort
  
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
  
return resList
end sort2DList

★Click Here to Open This Script 

Posted in Clipboard Color list RTF Sort Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAttributedString NSColor NSFont NSFontAttributeName NSForegroundColorAttributeName NSMutableAttributedString NSPasteboard | Leave a comment

アラートダイアログの背景色を指定してTable Viewを表示

Posted on 2月 25, 2019 by Takaaki Naganoya

指定色のアラートダイアログを作成し、その上にScroll View+Table Viewを表示して選択肢の選択を行うAppleScriptです。

アラートダイアログの背景色変更のテストを行なったものです。また、メソッド名をテキストで(Table Viewから)取得し、そのメソッド名で色を作成するテスト(NSSelectorFromString)も行いました。

Table Viewで選択した色は、次回実行時に反映されます(スクリプトエディタ/Script Debugger上で)。

本当はTable View上で行を選択したときに、アラートダイアログの背景色を変更させたかったのですが、試してみたもののうまく動かなかったので、その機能は削りました。最初から指定色のダイアログウィンドウを表示するだけなら問題はないので、現状のようなコードに落ち着きました。

AppleScript名:アラートダイアログの背景色を指定してTable Viewを表示
— Created 2019-02-25 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSColor : a reference to current application’s NSColor
property NSIndexSet : a reference to current application’s NSIndexSet
property NSScrollView : a reference to current application’s NSScrollView
property NSTableView : a reference to current application’s NSTableView
property NSTableColumn : a reference to current application’s NSTableColumn
property NSMutableArray : a reference to current application’s NSMutableArray
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn
property NSFullSizeContentViewWindowMask : a reference to current application’s NSFullSizeContentViewWindowMask

property theResult : 0
property returnCode : 0
property theDataSource : {}
property theColList : {}
property theWindow : missing value
property selectedColor : missing value

set paramObj to {myMessage:"Choose a color", mySubMessage:"Choose an appropriate color from color name list", aTableList:{"redColor", "blueColor", "yellowColor", "greenColor", "grayColor", "blackColor"}}
set aRes to my chooseItemByTableView:paramObj

on chooseItemByTableView:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTList to (aTableList of paramObj) as list
  
  
— make table view with scroll view
  
set aScrollWithTable to makeTableView(aTList, 300, 120) of me
  
  
— 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:aScrollWithTable
  end tell
  
  
— customize alert’s window style and color
  
if selectedColor = missing value then
    set tmpColor to NSColor’s grayColor()
  else
    set tmpSel to current application’s NSSelectorFromString(selectedColor)
    
set tmpColor to NSColor’s performSelector:tmpSel
  end if
  
  
set theWindow to theAlert’s |window|()
  
theWindow’s setTitleVisibility:false
  
–theWindow’s setStyleMask:( NSFullSizeContentViewWindowMask)
  
theWindow’s setTitlebarAppearsTransparent:true
  
theWindow’s setBackgroundColor:tmpColor
  
  
— 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
  
  
return (aScrollWithTable’s documentView’s selectedRow()) + 1
end chooseItemByTableView:

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

on makeTableView(aList as list, aWidth as number, aHeight as number)
  set aOffset to 40
  
  
set sourceList to {}
  
repeat with i in aList
    set the end of sourceList to {dataItem:(contents of i)}
  end repeat
  
  
set theDataSource to NSMutableArray’s alloc()’s init()
  
theDataSource’s addObjectsFromArray:sourceList
  
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
  
set aView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
  
  
  
set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:"dataItem")
  (
aColumn’s setWidth:aWidth)
  (
aColumn’s headerCell()’s setStringValue:"dataItem")
  (
aView’s addTableColumn:aColumn)
  
  
aView’s setDelegate:me
  
aView’s setDataSource:me
  
aView’s reloadData()
  
  
aScroll’s setDocumentView:aView
  
aView’s enclosingScrollView()’s setHasVerticalScroller:true
  
  
–1行目を選択
  
set aIndexSet to NSIndexSet’s indexSetWithIndex:0
  
aView’s selectRowIndexes:aIndexSet byExtendingSelection:false
  
  
–強制的にトップにスクロール
  
set aDBounds to aScroll’s documentView()’s |bounds|()
  
if class of aDBounds = list then
    –macOS 10.13 or later
    
set maxHeight to item 2 of item 1 of aDBounds
  else
    –macOS 10.10….10.12
    
set maxHeight to height of |size| of aDBounds
  end if
  
  
set aPT to current application’s NSMakePoint(0.0, -40.0) —— (aScroll’s documentView()’s |bounds|()’s |size|()’s height))
  
aScroll’s documentView()’s scrollPoint:aPT
  
  
return aScroll
end makeTableView

–TableView Event Handlers
on numberOfRowsInTableView:aView
  return my theDataSource’s |count|()
end numberOfRowsInTableView:

on tableView:aView objectValueForTableColumn:aColumn row:aRow
  set selRow to (aRow as number)
  
set aRec to (my theDataSource)’s objectAtIndex:selRow
  
set aTitle to (aColumn’s headerCell()’s title()) as string
  
set aRes to (aRec’s valueForKey:aTitle) as string
  
set my selectedColor to aRes
  
  
return aRes
end tableView:objectValueForTableColumn:row:

on tableView:aView shouldEditTableColumn:aTableColumn row:rowIndex
  return false –Not Editable
end tableView:shouldEditTableColumn:row:

★Click Here to Open This Script 

Posted in Color GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSAlertSecondButtonReturn NSColor NSFullSizeContentViewWindowMask NSIndexSet NSMutableArray NSRunningApplication NSScrollView NSTableColumn NSTableView | Leave a comment

アラートダイアログ上にTable View+Map Viewを表示

Posted on 2月 22, 2019 by Takaaki Naganoya

アラートダイアログを作成し、その上にScroll View+Table ViewおよびMap Viewを表示して選択肢の選択を行うAppleScriptです。

Map Viewを使用するために、インターネット接続が必要です。地図表示パフォーマンスはインターネット接続速度次第です。

{{placeName:"Hey", aLat:"35.69906613", aLong:"139.77084064"}....}

のように位置データを、

名称, 緯度, 経度

とまとめたリストで選択肢を指定します。実行すると選択肢のアイテム番号(1からはじまる)を返してきます。


▲左から、macOS 10.12.6、10.13.6、10.14.4betaで同じAppleScriptを動かしたところ。同じ場所、同じZoom LevelでもOSバージョンごとに微妙に表示内容が違う

現状の実装では、表UI(Table View)内のデータがダブルクリックで編集できてしまうので、編集できないようにするあたりが改良対象でしょうか。あと、地図・衛星写真の切り替えもつけておくとよいでしょう。

本Scriptに与える緯度・経度情報についてはあらかじめ住所ジオコーダーによって「住所情報→緯度・経度情報」の変換を行なったものを書いておく必要がありますが、Yahoo!の住所ジオコーダーサービスなどを呼び出せば、住所情報をパラメーターとすることも可能です。

サンプルデータの緯度・経度情報は、例によって「戦場の絆」の入っている近所のゲーセンの情報を適当にみつくろって入れてみたものです。

AppleScript名:アラートダイアログ上にTable View+Map Viewを表示 v2
— Created 2019-02-22 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "MapKit"

property NSAlert : a reference to current application’s NSAlert
property NSIndexSet : a reference to current application’s NSIndexSet
property MKMapView : a reference to current application’s MKMapView
property NSScrollView : a reference to current application’s NSScrollView
property NSTableView : a reference to current application’s NSTableView
property NSTableColumn : a reference to current application’s NSTableColumn
property NSMutableArray : a reference to current application’s NSMutableArray
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn

property zLevel : 18
property aMaxViewWidth : 800
property theResult : 0
property returnCode : 0
property theDataSource : {}
property curRow : 0
property aMapView : missing value
property nameL : {}
property placeL : {}

set aPlaceList to {{placeName:"Hey", aLat:"35.69906613", aLong:"139.77084064"}, {placeName:"namco中野", aLat:"35.70859274", aLong:"139.66584339"}, {placeName:"ゲームシティ板橋", aLat:"35.74572771", aLong:"139.67553260"}, {placeName:"ラウンドワンスタジアム板橋", aLat:"35.77661583", aLong:"139.67864491"}, {placeName:"キャロム練馬", aLat:"35.76386421", aLong:"139.66591600"}, {placeName:"アミュージアムOSC", aLat:"35.75308308", aLong:"139.59476696"}}

set paramObj to {myMessage:"場所の選択", mySubMessage:"適切な場所を以下のリストからえらんでください", placeList:aPlaceList}
my performSelectorOnMainThread:"chooseItemByTableViewWithMap:" withObject:(paramObj) waitUntilDone:true
return (my theResult)

on chooseItemByTableViewWithMap:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTList to (placeList of paramObj) as list
  
  
set nameL to {}
  
set placeL to {}
  
repeat with i in aTList
    set the end of nameL to (placeName of i)
    
set the end of placeL to {contents of (aLat of i), contents of (aLong of i)}
  end repeat
  
  
— create a view
  
set theView to current application’s NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aMaxViewWidth, 400))
  
  
— make table view with scroll view
  
set aScrollWithTable to makeTableView(nameL, 200, 400) of me
  
  
set aMapView to MKMapView’s alloc()’s initWithFrame:(current application’s NSMakeRect(210, 0, aMaxViewWidth – 210, 400))
  
tell aMapView
    its setMapType:(current application’s MKMapTypeStandard)
    
its setZoomEnabled:true
    
its setScrollEnabled:true
    
its setPitchEnabled:true
    
its setRotateEnabled:true
    
its setShowsCompass:true
    
its setShowsZoomControls:true
    
its setShowsScale:true
    
its setShowsUserLocation:true
    
    
set aLocation to current application’s CLLocationCoordinate2DMake((first item of first item of placeL) as real, (second item of first item of placeL) as real)
    
    
its setCenterCoordinate:aLocation zoomLevel:(zLevel) animated:false
    
its setDelegate:me
  end tell
  
  
theView’s setSubviews:{aScrollWithTable, aMapView}
  
  
— 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:theView
  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 (my theResult) to (aScrollWithTable’s documentView’s selectedRow()) + 1
end chooseItemByTableViewWithMap:

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

on makeTableView(aList as list, aWidth as number, aHeight as number)
  set aOffset to 0
  
  
set sourceList to {}
  
repeat with i in aList
    set the end of sourceList to {dataItem:(contents of i)}
  end repeat
  
  
set theDataSource to NSMutableArray’s alloc()’s init()
  
theDataSource’s addObjectsFromArray:sourceList
  
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
  
set aView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
  
  
  
set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:"dataItem")
  (
aColumn’s setWidth:aWidth)
  (
aColumn’s headerCell()’s setStringValue:"dataItem")
  (
aView’s addTableColumn:aColumn)
  
  
aView’s setDelegate:me
  
aView’s setDataSource:me
  
aView’s reloadData()
  
  
aScroll’s setDocumentView:aView
  
aView’s enclosingScrollView()’s setHasVerticalScroller:true
  
  
–1行目を選択
  
set aIndexSet to NSIndexSet’s indexSetWithIndex:0
  
aView’s selectRowIndexes:aIndexSet byExtendingSelection:false
  
  
–強制的にトップにスクロール
  
–強制的にトップにスクロール
  
–set maxHeight to aScroll’s documentView()’s |bounds|()’s |size|()’s height
  
set aDBounds to aScroll’s documentView()’s |bounds|()
  
if class of aDBounds = list then
    –macOS 10.13 or later
    
set maxHeight to item 2 of item 1 of aDBounds
  else
    –macOS 10.10….10.12
    
set maxHeight to height of |size| of aDBounds
  end if
  
  
set aPT to current application’s NSMakePoint(0.0, -40.0) —— (aScroll’s documentView()’s |bounds|()’s |size|()’s height))
  
aScroll’s documentView()’s scrollPoint:aPT
  
  
return aScroll
end makeTableView

–TableView Event Handlers
on numberOfRowsInTableView:aView
  return my theDataSource’s |count|()
end numberOfRowsInTableView:

on tableView:aView objectValueForTableColumn:aColumn row:aRow
  set (tmpRow) to aView’s selectedRow() as number
  
  
–Table View上で現在選択行「以外の」行が選択されたら、Mapを選択項目で更新
  
if (my curRow) is not equal to tmpRow then
    set tmpLat to (first item of item (tmpRow + 1) of placeL) as real
    
set tmpLong to (second item of item (tmpRow + 1) of placeL) as real
    
    
tell aMapView
      set aLocation to current application’s CLLocationCoordinate2DMake(tmpLat, tmpLong)
      
its setCenterCoordinate:aLocation zoomLevel:(zLevel) animated:false
    end tell
    
    
set (my curRow) to tmpRow
  end if
  
  
set aRec to (my theDataSource)’s objectAtIndex:(aRow as number)
  
set aTitle to (aColumn’s headerCell()’s title()) as string
  
set aRes to (aRec’s valueForKey:aTitle)
  
return aRes
end tableView:objectValueForTableColumn:row:

★Click Here to Open This Script 

Posted in geolocation GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy MKMapView NSAlert NSAlertSecondButtonReturn NSIndexSet NSMutableArray NSRunningApplication NSScrollView NSTableColumn NSTableView | Leave a comment

アラートダイアログ上にTable Viewを表示

Posted on 2月 22, 2019 by Takaaki Naganoya

アラートダイアログを作成し、その上にScroll View+Table Viewを表示して選択肢の選択を行うAppleScriptです。

1D List(1次元配列)を与え、選択を行うUIです。実行すると選択肢のアイテム番号(1からはじまる)を返してきます。

とくにこれが作りたかったのではなく、位置情報(Geo Location)の選択をTable Viewで行い、該当する場所をMap Viewに表示させると擬似的に複数の固定位置情報から任意のものを選択させるダイアログが作れるだろうかと思って作成した試作品です。

東京への引っ越しの荷物が到着して、ようやく検証用のサブマシンが届いたものの、ネットワークを新規敷設しなくてはならないのでぼちぼちです。

AppleScript名:アラートダイアログ上にTable Viewを表示 v3
— Created 2019-02-21 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSIndexSet : a reference to current application’s NSIndexSet
property NSScrollView : a reference to current application’s NSScrollView
property NSTableView : a reference to current application’s NSTableView
property NSTableColumn : a reference to current application’s NSTableColumn
property NSMutableArray : a reference to current application’s NSMutableArray
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn

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

set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", aTableList:{"なし", "りんご", "ひよこ", "ぎょうざ", "すなぎも", "ぼんじり", "もも"}}
set aRes to my chooseItemByTableView:paramObj

on chooseItemByTableView:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTList to (aTableList of paramObj) as list
  
  
–define the matrix size where you’ll put the radio buttons
  
set aScrollWithTable to makeTableView(aTList, 300, 150) of me
  
  
— 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:aScrollWithTable
  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
  
  
return (aScrollWithTable’s documentView’s selectedRow()) + 1
end chooseItemByTableView:

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

on makeTableView(aList as list, aWidth as number, aHeight as number)
  set aOffset to 40
  
  
set sourceList to {}
  
repeat with i in aList
    set the end of sourceList to {dataItem:(contents of i)}
  end repeat
  
  
set theDataSource to NSMutableArray’s alloc()’s init()
  
theDataSource’s addObjectsFromArray:sourceList
  
  
set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
  
set aView to NSTableView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, aOffset, aWidth, aHeight))
  
  
  
set aColumn to (NSTableColumn’s alloc()’s initWithIdentifier:"dataItem")
  (
aColumn’s setWidth:aWidth)
  (
aColumn’s headerCell()’s setStringValue:"dataItem")
  (
aView’s addTableColumn:aColumn)
  
  
aView’s setDelegate:me
  
aView’s setDataSource:me
  
aView’s reloadData()
  
  
aScroll’s setDocumentView:aView
  
aView’s enclosingScrollView()’s setHasVerticalScroller:true
  
  
–1行目を選択
  
set aIndexSet to NSIndexSet’s indexSetWithIndex:0
  
aView’s selectRowIndexes:aIndexSet byExtendingSelection:false
  
  
–強制的にトップにスクロール
  
–set maxHeight to aScroll’s documentView()’s |bounds|()’s |size|()’s height
  
set aDBounds to aScroll’s documentView()’s |bounds|()
  
if class of aDBounds = list then
    –macOS 10.13 or later
    
set maxHeight to item 2 of item 1 of aDBounds
  else
    –macOS 10.10….10.12
    
set maxHeight to height of |size| of aDBounds
  end if
  
  
set aPT to current application’s NSMakePoint(0.0, -40.0) —— (aScroll’s documentView()’s |bounds|()’s |size|()’s height))
  
aScroll’s documentView()’s scrollPoint:aPT
  
  
return aScroll
end makeTableView

–TableView Event Handlers
on numberOfRowsInTableView:aView
  return my theDataSource’s |count|()
end numberOfRowsInTableView:

on tableView:aView objectValueForTableColumn:aColumn row:aRow
  set aRec to (my theDataSource)’s objectAtIndex:(aRow as number)
  
set aTitle to (aColumn’s headerCell()’s title()) as string
  
set aRes to (aRec’s valueForKey:aTitle)
  
return aRes
end tableView:objectValueForTableColumn:row:

★Click Here to Open This Script 

Posted in GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSAlert NSAlertSecondButtonReturn NSIndexSet NSMutableArray NSRunningApplication NSScrollView NSTableColumn NSTableView | 1 Comment

アラートダイアログ上にsegmented controlを表示

Posted on 2月 19, 2019 by Takaaki Naganoya

アラートダイアログを作成し、その上にsegmented control(タブみたいな複数項目1択UI部品)を表示して選択肢の選択を行うAppleScriptです。

選択肢のインデックス番号が(1からはじまる)返ってきます。

AppleScript名:アラートダイアログ上にsegmented controlを表示
— Created 2019-02-14 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSView : a reference to current application’s NSView
property NSSegmentedControl : a reference to current application’s NSSegmentedControl
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSSegmentStyleTexturedRounded : a reference to current application’s NSSegmentStyleTexturedRounded

property theSegSel : 0
property returnCode : 0

set paramObj to {myMessage:"項目選択", mySubMessage:"どれか選択してください。", segmentMes:{"Red", "Blue", "Yellow", "Brown", "White", "Cyan", "Grey"}}
set segRes to my chooseSegment:paramObj

on chooseSegment:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set segMesList to segmentMes of paramObj
  
  
— create a view
  
set theView to NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 400, 200))
  
  
–Segmented Controlをつくる
  
set aSeg to makeSegmentedControl(segMesList, 400, 80) of me
  
  
theView’s setSubviews:{aSeg}
  
  
set theAlert to NSAlert’s alloc()’s init()
  
  
— set up alert
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aSeg
  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 (my theSegSel) to (aSeg’s selectedSegment()) as number
  
return (my theSegSel) + 1
end chooseSegment:

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

on makeSegmentedControl(titleList, aWidth, aHeight)
  set aLen to length of titleList
  
  
set aSeg to NSSegmentedControl’s alloc()’s init()
  
aSeg’s setSegmentCount:aLen
  
  
set aCount to 0
  
repeat with i in titleList
    set j to contents of i
    (
aSeg’s setLabel:j forSegment:aCount)
    
set aCount to aCount + 1
  end repeat
  
  
aSeg’s setTranslatesAutoresizingMaskIntoConstraints:false
  
aSeg’s setSegmentStyle:(NSSegmentStyleTexturedRounded)
  
aSeg’s setFrame:(current application’s NSMakeRect(20, aHeight – 60, aWidth, aHeight – 40))
  
aSeg’s setTrackingMode:0
  
aSeg’s setTarget:me
  
aSeg’s setAction:"clickedSeg:"
  
aSeg’s setSelectedSegment:0
  
  
return aSeg
end makeSegmentedControl

on clickedSeg:aSender
  set aSel to aSender’s selectedSegment()
  
set selSeg to aSel
end clickedSeg:

★Click Here to Open This Script 

Posted in dialog GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy NSAlert NSRunningApplication NSSegmentedControl NSSegmentStyleTexturedRounded NSView | Leave a comment

アラートダイアログ上にRadio Buttonを表示

Posted on 2月 19, 2019 by Takaaki Naganoya

アラートダイアログを作成し、その上にRadio Buttonを表示して位置座標の選択を行うAppleScriptです。

任意の選択肢をlist(配列)で指定すると、その通りのRadio Buttonを表示します。ユーザーが選択した項目のtitle文字列を返します。項目番号ではありません。

AppleScript名:アラートダイアログ上にRadio Buttonを表示 v2
— Created 2019-02-14 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSMatrix : a reference to current application’s NSMatrix
property NSButtonCell : a reference to current application’s NSButtonCell
property NSRadioButton : a reference to current application’s NSRadioButton
property NSRadioModeMatrix : a reference to current application’s NSRadioModeMatrix
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn

property theResult : 0
property returnCode : 0

set paramObj to {myMessage:"項目の選択", mySubMessage:"適切なものを以下からえらんでください", matrixTitleList:{"なし", "りんご", "ひよこ", "ぎょうざ", "すなぎも", "ぼんじり", "もも"}}
set aRes to my chooseItemByRadioButton:paramObj

on chooseItemByRadioButton:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aMatList to (matrixTitleList of paramObj) as list
  
set aLen to length of aMatList
  
  
–create the radio button prototype
  
set aProto to NSButtonCell’s alloc()’s init()
  
aProto’s setTitle:"Options"
  
aProto’s setButtonType:(NSRadioButton)
  
  
–define the matrix size where you’ll put the radio buttons
  
set matrixRect to current application’s NSMakeRect(20.0, 20.0, 200.0, (20.0 * aLen))
  
set aMatrix to NSMatrix’s alloc()’s initWithFrame:matrixRect mode:(NSRadioModeMatrix) prototype:aProto numberOfRows:(aLen) numberOfColumns:1
  
  
set cellList to aMatrix’s cells()
  
set aCount to 0
  
repeat with i in aMatList
    set j to contents of i
    ((
cellList’s objectAtIndex:aCount)’s setTitle:j)
    
set aCount to aCount + 1
  end repeat
  
  
— 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:aMatrix
  end tell
  
  
— show alert in modal loop
  
current application’s NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
  
return (aMatrix’s selectedCell()’s title()) as string
end chooseItemByRadioButton:

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

★Click Here to Open This Script 

Posted in dialog GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy NSAlert NSAlertSecondButtonReturn NSButtonCell NSMatrix NSRadioButton NSRadioModeMatrix | Leave a comment

Keynoteの最前面のドキュメントの現在のスライドに指定月の日曜日はじまりカレンダーを表で作成

Posted on 2月 14, 2019 by Takaaki Naganoya

Keynoteの最前面の書類の現在選択中のスライド(ページ)に、指定月の日曜日はじまりカレンダーを、表オブジェクトで作成するAppleScriptです。

Keynoteで資料を作成していると、資料にカレンダーを入れたいケースが多々あります。Terminal.appを起動してcalコマンドでカレンダーを作ってみたり、Dashboardのカレンダーをコピーして入れることも多いですが、Dashboardはもうあるんだかないんだ分からない状態。かわりのものを用意してみました。

あとは、サイズやスタイル、土日のデータを削除するなど用途に応じて編集して表カレンダーを利用するとよいでしょう。

macOS標準装備のスクリプトメニューに入れて呼び出す場合には、アプリケーション形式で書き出したものを使う必要があります。

世の中のカレンダーは日曜日はじまりだけではないので、月曜日はじまりなど、その国、その現場ごとのルールに合わせて変更することが重要です。曜日名についても、実行中のユーザーの言語環境から取得して入れることも可能なので、そのようにしてもよいでしょう。

同じぐらいのスペックのマシンで本Scriptを動かすと、macOS 10.14, Mojave上では10.12.6上の(Keynote v8.1の)倍ぐらい速くて驚かされます。10.13.6上でも同様なのでOS側の対応というよりは、Keynote側のバージョンアップ(v8.1 –> v8.3)によるものかもしれません。

AppleScript名:指定月の日曜日はじまりカレンダーを表で作成 v2.scptd
— Created 2019-02-14 by Takaaki Naganoya
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property theDate : missing value

property daysList : {"日", "月", "火", "水", "木", "金", "土"} –Japanese
–property daysList : {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}–English

set paramObj to {myMessage:"月選択", mySubMessage:"作成対象の月を選択してください。日付はどれでもけっこうです。"}
set {targYear, targMonth} to chooseMonth(paramObj) of me

–指定月のカレンダーを1D List(7 days x 6 weeks) で作成
set aCalList to retListCalendar(targYear, targMonth) of me

set fullCalList to daysList & aCalList

set aTitle to (targYear as string) & (targMonth as string)
set dCount to 1

tell application "Keynote"
  tell front document
    tell current slide
      set aTable to make new table with properties {header column count:0, header row count:1, row count:7, column count:7, name:aTitle}
      
tell aTable
        repeat with i from 1 to 49
          tell cell i
            ignoring application responses
              set value to contents of item dCount of fullCalList
            end ignoring
          end tell
          
set dCount to dCount + 1
        end repeat
      end tell
    end tell
  end tell
end tell

–カレンダー作成対象の年、月を選択(ただし、日付をクリックして選択しないと値を取得できないので注意)
on chooseMonth(paramObj)
  my performSelectorOnMainThread:"chooseDate:" withObject:(paramObj) waitUntilDone:true
  
set aYear to year of theDate
  
set aMonth to month of theDate as number
  
return {aYear, aMonth}
end chooseMonth

on chooseDate:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
  
— create a view
  
set theView to current application’s NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 100, 200))
  
set datePicker to current application’s NSDatePicker’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 100, 100))
  
datePicker’s setDatePickerStyle:(current application’s NSClockAndCalendarDatePickerStyle)
  
datePicker’s setDatePickerElements:((current application’s NSYearMonthDayDatePickerElementFlag) + (current application’s NSHourMinuteSecondDatePickerElementFlag as integer))
  
  
datePicker’s setDateValue:(current application’s NSDate’s |date|())
  
  
set theSize to datePicker’s fittingSize()
  
  
theView’s setFrameSize:theSize
  
datePicker’s setFrameSize:theSize
  
  
theView’s setSubviews:{datePicker}
  
  
set theAlert to current application’s NSAlert’s alloc()’s init()
  
  
— set up alert
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:theView
  end tell
  
  
— show alert in modal loop
  
set returnCode to theAlert’s runModal()
  
if returnCode = (current application’s NSAlertSecondButtonReturn) then error number -128
  
  
— retrieve date
  
set (my theDate) to (datePicker’s dateValue()) as date
  
end chooseDate:

–指定月のカレンダーを1D List(7 days x 6 weeks) で返す
on retListCalendar(tYear, tMonth)
  set mLen to getMlen(tYear, tMonth) of me
  
set aList to {}
  
  
set fDat to getDateInternational(tYear, tMonth, 1) of me
  
tell current application
    set aOffset to (weekday of fDat) as number
  end tell
  
  
–header gap
  
repeat (aOffset – 1) times
    set the end of aList to ""
  end repeat
  
  
–calendar body
  
repeat with i from 1 to mLen
    set the end of aList to (i as string)
  end repeat
  
  
–footer gap
  
repeat (42 – aOffset – mLen + 1) times
    set the end of aList to ""
  end repeat
  
  
return aList
end retListCalendar

–現在のカレンダーで指定年月の日数を返す(国際化対応版)
on getMlen(aYear as integer, aMonth as integer)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
return |length| of theResult
end getMlen

–現在のカレンダーで指定年月のdate objectを返す
on getDateInternational(aYear, aMonth, aDay)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:0 minute:0 |second|:0 nanosecond:0
  
return theDate as date
end getDateInternational

★Click Here to Open This Script 

Posted in Calendar GUI list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Keynote NSAlert NSAlertSecondButtonReturn NSCalendar NSClockAndCalendarDatePickerStyle NSDate NSDatePicker NSDayCalendarUnit NSHourMinuteSecondDatePickerElementFlag NSMonthCalendarUnit NSView NSYearMonthDayDatePickerElementFlag | Leave a comment

指定ファイルサイズのJPEGに変換(圧縮率可変)_1K=1024で計算

Posted on 2月 11, 2019 by Takaaki Naganoya

Finder上で選択中の画像ファイルを指定ファイルサイズを下回るよう圧縮率を調整してJPEGに変換するAppleScriptです。

指定画像を指定のファイルサイズ(1K=1024で計算)以内になるよう、非圧縮の状態から段階的に圧縮率を高くして仕上がりファイルサイズを下回るかどうか演算を行います。ファイルに書き込まずに仕上がりサイズの計算を行うところが新機軸です。

macOS標準搭載のスクリプトメニューから呼び出して使うことを想定しています。実行すると、ダイアログ表示して指定ファイルサイズをKB単位で数値入力する必要があります。

巨大な画像ファイルを処理してしまった場合への対処として、最初に最高圧縮率で圧縮してみて、指定ファイルサイズ以内になるかどうかを計算します。

最初は圧縮率100%から25%まで1%きざみで仕上がり画像ファイルサイズを計算してみたのですが、数十Mバイトの巨大な画像を処理させたら、途中でマウスカーソルが反応しないほどOSが無反応になったので(暴走状態?)、それを避けるために、このような処理を行なってみました。

JPEGにファイル変換した画像はデスクトップに

UUID_JPEG画質.jpg

の形式で出力されます。JPEG画質は1.0から0.01までの数値です。

AppleScript名:指定ファイルサイズのJPEGに変換(圧縮率可変)_1K=1024で計算
— Created 2014-02-21 Shane Stanley
— Modified 2019-02-11 Takaaki Naganoya
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSUUID : a reference to current application’s NSUUID
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSPredicate : a reference to current application’s NSPredicate
property NSJPEGFileType : a reference to current application’s NSJPEGFileType
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

property compressList : {100, 99, 98, 97, 96, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 40, 30, 20, 25} –大→小 へと並んでいる必要がある
property kbBase : 1024

tell application "Finder"
  set inFiles to selection as alias list
end tell

–指定のAlias listのうち画像ファイルのみ抽出
set filRes1 to filterAliasListByUTI(inFiles, "public.image") of me
if filRes1 = {} then return

–ターゲットサイズを指定
set aMessage to "Input Target size in KB ( " & (kbBase as string) & " based )"
set aRes to text returned of (display dialog aMessage default answer "300" buttons {"OK"} default button 1)
if aRes = false or aRes = "" then return
set targLimit to (aRes as number) * kbBase

–選択中のファイルのうちの1つから親フォルダを求め、出力先ファイルパスを組み立てる
set outPathTarg to (contents of first item of filRes1)
set pathString to NSString’s stringWithString:outPathTarg
set newPath to (pathString’s stringByDeletingLastPathComponent()) as string

–Main Loop
repeat with i in filRes1
  set aNSImage to (NSImage’s alloc()’s initWithContentsOfFile:(i))
  
  
–巨大すぎる画像のリサイズ時への保険で、最小サイズで投機的に圧縮して結果を確認する
  
set minSize to calcSavedJPEGSizeFromNSIMage(aNSImage, (last item of compressList) / 100) of me
  
  
if minSize < targLimit then
    –Simulate Suitable JPEG Compression Ratio to target file size
    
repeat with ii in compressList
      set jj to ii / 100
      
set fileSize to calcSavedJPEGSizeFromNSIMage(aNSImage, jj) of me
      
      
if fileSize < targLimit then
        exit repeat
      end if
    end repeat
  else
    set jj to 0.01 –エラー時にはやけくそで1%まで圧縮指定
  end if
  
  
set outPOSIXpath to (newPath & "/" & (NSUUID’s UUID()’s UUIDString()) as string) & "_" & (jj as string)
  
set savePath to outPOSIXpath & ".jpg"
  
  
saveNSImageAtPathAsJPG(aNSImage, savePath, jj) of me
end repeat

–Alias listから指定UTIに含まれるものをPOSIX pathのリストで返す
on filterAliasListByUTI(aList, targUTI)
  set newList to {}
  
repeat with i in aList
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, targUTI)
    
if utiRes is not equal to {} then
      set the end of newList to j
    end if
  end repeat
  
return newList
end filterAliasListByUTI

–指定の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

–NSImageを指定パスにJPEG形式で保存、qulityNumは0.0〜1.0。1.0は無圧縮
on saveNSImageAtPathAsJPG(anImage, outPath, qulityNum as real)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(NSJPEGFileType) |properties|:{NSImageCompressionFactor:qulityNum})
  
  
set aLength to (myNewImageData’s |length|()) as number
  
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –true/false
end saveNSImageAtPathAsJPG

–NSImageをJPEG形式で保存したときのファイルサイズを計算
on calcSavedJPEGSizeFromNSIMage(anImage, qulityNum as real)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep
  
set myNewImageData to (aRawimg’s representationUsingType:(NSJPEGFileType) |properties|:{NSImageCompressionFactor:qulityNum})
  
set aLength to (myNewImageData’s |length|()) as number
  
return aLength
end calcSavedJPEGSizeFromNSIMage

★Click Here to Open This Script 

Posted in file Image list UTI | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Finder NSArray NSBitmapImageRep NSImage NSPredicate NSString NSURL NSURLTypeIdentifierKey NSUUID | Leave a comment

Post navigation

  • Older posts
  • Newer posts

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

Google Search

Popular posts

  • Numbersで選択範囲のセルの前後の空白を削除
  • macOS 15でも変化したText to Speech環境
  • デフォルトインストールされたフォント名を取得するAppleScript
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • 【続報】macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ
  • Script Debuggerの開発と販売が2025年に終了
  • macOS 14で変更になったOSバージョン取得APIの返り値
  • macOS 15 リモートApple Eventsにバグ?
  • AppleScript入門① AppleScriptってなんだろう?
  • KagiのWebブラウザ、Orion
  • macOS 26, Tahoe
  • 2024年に書いた価値あるAppleScript
  • macOS 15:スクリプトエディタのAppleScript用語辞書を確認できない
  • NSObjectのクラス名を取得 v2.1
  • 有害ではなくなっていたSpaces
  • Pixelmator Proがv3.6.8でHDR画像をサポート
  • Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行
  • AVSpeechSynthesizerで読み上げテスト
  • (確認中)AppleScript Dropletのバグっぽい動作が解消?
  • AppleScript Dropletのバグっぽい動作が「復活」(macOS 15.5β)

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (199) 14.0savvy (154) 15.0savvy (143) CotEditor (66) Finder (52) Keynote (119) 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 (55) Pixelmator Pro (20) 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
  • date
  • 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
  • process
  • 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年8月
  • 2025年7月
  • 2025年6月
  • 2025年5月
  • 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