Menu

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

AppleScriptの穴

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

カテゴリー: GUI Scripting

Keynoteで選択中のテキストアイテムからテキスト取り出し

Posted on 1月 27 by Takaaki Naganoya

Keynoteで選択中のテキストアイテムからテキスト情報を抽出するAppleScriptです。

FileMaker Pro Scripting Bookの英語版を来る日も来る日も作っており、気づけばぜんぜんScriptを書いていないので「Piyomaru Software」ではなく「Piyomaru Publishing」だ、などと言っている今日このごろです。Keynoteを毎日使っていますが、微妙に痒いところに手が届かないので、使えば使うほどAppleScriptで機能を補いたくなってきます。

Keynoteに「selected objects」といった「選択中のオブジェクト」を求めるAppleScript用語が用意されていないため、本来やりたい「選択中の部品のデータを処理して元の部品に書き戻す」「選択中の部品からデータを抜き出す」といった処理ができません。selectionで取得できるのが「選択中のスライド」だというのが非常に残念です。

Keynote書類でテキストアイテムを選択し、コピーすると……テキスト情報は取り出せません。Finder上でクリップボード内容を確認してみると、あろうことか「PNGイメージ」と表示されます。

コピーされたクリップボードの内容を解析して、そのオブジェクト情報からテキストを抽出できるとよいだろうか、などとも考えたのですが、

Keynote内部オブジェクトで、ちょっと手強そうです。

というわけで、Keynote側には一切機能がないわけですが、選択中のオブジェクトをコピーし、新規ドキュメントにペーストしたうえで新規ドキュメント上のオブジェクトからテキストを取り出し、新規ドキュメントを破棄することにしました。

GUI Scriptingを用いているので、システム環境設定の「セキュリティとプライバシー」でGUI Scriptingを許可してから実行してください。

AppleScript名:選択中のテキストアイテムからテキスト取り出し.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2021/01/27
—
–  Copyright © 2021 Piyomaru Software, All Rights Reserved
—

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

set kList to getEveryTextFromCurrentKeynoteSlide() of me
set tRes to retDelimedText(kList, return) of me

on getEveryTextFromCurrentKeynoteSlide()
  tell application "Keynote"
    activate
    
    
set dCount to count every document
    
if dCount = 0 then
      display notification "There is no Keynote document"
      
return {}
    end if
    
    
tell front document
      set sCount to count every slide
    end tell
    
if sCount = 0 then
      display notification "There is no Slide in Keynote document"
      
return {}
    end if
    
  end tell
  
  
–Copy
  
tell application "System Events"
    keystroke "c" using {command down}
  end tell
  
  
  
tell application "Keynote"
    activate
    
set nDoc to make new document
    
tell nDoc
      set aMaster to master slide "空白"
      
–set aMaster to master slide "Blank"
      
      
tell current slide
        set base slide to aMaster
      end tell
    end tell
    
  end tell
  
  
–Paste
  
tell application "System Events"
    keystroke "v" using {command down}
  end tell
  
  
delay 0.1 –Important!!
  
  
set tOut to {}
  
tell application "Keynote"
    tell front document
      tell current slide
        set tList to every iWork item
        
        
repeat with i in tList
          set aTmpStr to object text of i
          
set the end of tOut to aTmpStr
        end repeat
      end tell
    end tell
    
    
–Dispose document
    
tell front document
      close without saving
    end tell
  end tell
  
  
return tOut
end getEveryTextFromCurrentKeynoteSlide

on retDelimedText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retDelimedText

★Click Here to Open This Script 

Posted in GUI Scripting Text | Tagged 10.14savvy 10.15savvy 11.0savvy Keynote | Leave a comment

Switch Controlを起動

Posted on 5月 13, 2020 by Takaaki Naganoya

Switch Controlを起動するAppleScriptです。macOS標準搭載のScript Menuに入れて呼び出して使っています。Switch Controlは、障害者向けの支援機能を提供するmacOSの標準機能で、標準のマウス/トラックパッド、キーボードなどの利用が困難なユーザーに向けて少ないボタンや音声で操作する機能を提供するものです。

一般のユーザーにとってもSwitch Controlは有効活用できる機能であるため、個人的にいろいろ試しています。

もともと、Switch Controlを起動するためのコマンドは用意されていません。AppleScriptのコマンドで起動できるとか、コマンドラインから起動できるとかいった手軽な起動手段は存在していません。

……というわけで、仕方なくGUI Scriptingで画面上のチェックボックスをクリックするという不毛な処理を書いたわけですが、ただダラダラとGUI部品の階層をなぞるだけの知性のカケラもないコードを書くだけでは意味がありません。

この、クリックする対象のチェックボックスを実行言語環境が変わっても自動で検出できるようにチャレンジしてみました。

結果:失敗 追いかける対象が大きすぎたようです。システム環境設定の画面上のチェックボックスについているタイトル文字を特定するだけの話なのですが、システム環境設定(System Preferences.app)の各機能はプラグインで提供されており、システム環境設定のバンドル内のstringsファイルを追いかけても希望の文字列は得られません。

# このため、チェックボックスのタイトルを言語環境ごとに書き換える必要があります

では、実際に各プラグインのバンドル構造内でstringsファイルを取得することを試みたのですが、これにも失敗。それらしい文字列は得られるものの、文章すべてが1エントリに登録されているわけではないようで、stringsファイルでキーを指定すれば各ロケールごとの対象文字列が得られる……という理想的な処理はできませんでした。

今回のアプローチは技術的には失敗してしまいましたが、他の誰かが突破する日も来るかもしれません。自分のマシンのSSD内には、割とそうした「失敗作」のScriptも存在しており、そうした失敗作が別の機会の土台になることも多々あります。

仕事で作り込む必要のあるScriptであれば、スクリプトバンドル内に各言語ごとの文字列テーブルを自分で作って、localized stringでその値を引けるようにする感じでしょうか。OS側で対象箇所の文言を変更した場合には自分のテーブル側もアップデートする必要が出てきてしまいます。

本Scriptの冒頭でSwitch Controlが起動しているかどうかのチェックを行い、起動中であれば起動処理を行わないようにしています。この判定処理自体は、単体ではほぼ意味がありませんが、こうして組み合わせることで「不要な処理を行わない」ための部品として有効に活用できているといえます。

AppleScript名:Switch Controlを起動
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/05/13
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set aRes to launchSwitchControl() of me

on launchSwitchControl()
  if current application’s NSWorkspace’s sharedWorkspace()’s isSwitchControlEnabled() = true then return true
  
set aLoc to (current application’s NSLocale’s currentLocale()’s languageCode()) as string
  
  
–Current Language detection
  
if aLoc = "en" then
    set aStr to "Enable Switch Control" –English
  else if aLoc = "ja" then
    set aStr to "スイッチコントロールを有効にする" –Japanese
  else
    error "Make current language entry"
  end if
  
  
tell application "System Preferences"
    activate
    
tell pane id "com.apple.preference.universalaccess"
      reveal anchor "Switch"
    end tell
  end tell
  
  
set hitF to false
  
  
tell application "System Events"
    tell process "System Preferences"
      repeat 200 times
        delay 0.1
        
if (exists checkbox aStr of tab group 1 of group 1 of window 1) then
          click checkbox aStr of tab group 1 of group 1 of window 1
          
set hitF to true
          
exit repeat
        end if
      end repeat
    end tell
  end tell
  
  
tell application "System Preferences" to quit
  
return hitF
end launchSwitchControl

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 10.14savvy 10.15savvy Switch Control System Events | Leave a comment

面積で評価して、Keynoteのメインウィンドウのうち最大のもののItem Numberを返す

Posted on 5月 10, 2020 by Takaaki Naganoya

ウィンドウ上に複数存在するscroll areaのうち処理対象となるべきものを面積を計算することで特定するGUI Scripting系のAppleScriptです。

どーしてもGUI Scriptingでしか操作できない機能があって、それを自動化する価値があって、大幅に発生する可能性の高い労力を削減できる見込みが立ったので、一気に自動化Scriptを作成。本Scriptはその中で作成した1つの部品です。

自分が書いた処理内容は、Keynoteで作った書類の目次ページに用意した、各スライドのタイトルに実際のスライドへのリンクを付加するもの。

本来、Keynote自体のAppleScript用語辞書に標準装備されていてほしい機能です。残念ながら標準装備されていないために、自分で組む必要があったわけです。

それを作っている途中で、このメインの(Keynoteオブジェクトを配置する中央のエリア)scroll areaのIDが起動するたびに変わるという現象に直面。初期状態(インスペクタの表示状態)をそろえてもIDが変わる。たいていこうしたGUI Scriptingがらみの「怪奇現象」に直面した場合には、スクルプトエディタやアプリケーションの再起動を行えば回避できることが多いのですが、何回かためしても回避できなかったので、scroll areaの特定をID以外で行うことに。

IDが毎回(起動ごとに)変更になるので、「最大の面積を持つもの」を計算して求めるようにしてみました。

AppleScript名:面積で評価して、Keynoteのメインウィンドウのうち最大のもののItem Numberを返す
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/05/09
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

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

set areaNum to getKenoteScrollAreaMax() of me

–面積で評価して、Keynoteのメインウィンドウのうち最大のもののItem Numberを返す
on getKenoteScrollAreaMax()
  tell application "System Events"
    tell process "Keynote"
      tell window 1
        set sCount to count every scroll area
        
set tmpMax to 0
        
set tmpMaxItem to 0
        
repeat with i from 1 to sCount
          tell scroll area i
            set tmpA to size
            
copy tmpA to {tmpW, tmpH}
            
set tmpArea to tmpW * tmpH
            
if tmpArea > tmpMax then
              set tmpMax to tmpArea
              
set tmpMaxItem to i
            end if
          end tell
        end repeat
      end tell
    end tell
  end tell
  
  
return tmpMaxItem
end getKenoteScrollAreaMax

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 10.13savvy 10.14savvy 10.15savvy Keynote System Events | Leave a comment

Previewで現在表示中のPDFのページ番号を抽出する

Posted on 4月 29, 2020 by Takaaki Naganoya

macOS標準搭載の画像/PDFビューワーの「Preview.app」で表示中のPDFの、現在表示中のページの番号を取得するAppleScriptです。

本来、Preview.appのような超低機能アプリケーションから強引に情報を(GUI Scriptingまで使って)取得する意味はありません。PDFビューワーとしてまっとうな機能を持っているSkimを使って表示中のページ番号を取得するのが筋です。

ただ、どうしてもPreviewでないといけないケースで、仕方なく作ったものですが、英語環境でも日本語環境でも同様に動くために作ってみたらこんな感じに。指定アプリケーション単体で言語環境を指定して起動できると、各言語環境における動作確認が手軽に行えてよいと思うものの、手段がありそうで見つかりません(Xcode上でそういう起動ができるので、不可能ではないと思うのですが)。


▲英語環境で実行したところ(macOS 10.14.6)


▲日本語環境で実行したところ(macOS 10.15.4)

仕方なくGUI Scripting経由でウィンドウのタイトルを取得して、ファイル名とページ情報を文字列処理で分離しています。このあたり、英語環境と日本語環境でセパレータ(括弧)が異なるので、分離したページ情報から数字判定を行なって取得しています。Preview.appのアプリケーションバンドル内にこうしたフォーマットのテキストが存在していれば、そちらを使うべきです(見つかっていないので現状こんな感じで)。

GUI Scripting内でプロセス名を指定する箇所で、ローカライズされたプロセス名をCocoaの機能を用いて取得しています。これで、英語環境と日本語環境については問題なく共通Scriptでカバーできています。

AppleScript名:Previewで現在表示中のPDFのページ番号を抽出する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/04/29
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

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

set pNum to retPreviewDispPageNumber() of me

on retPreviewDispPageNumber()
  set aLocName to getLocalizedName("com.apple.Preview") of me
  
  
tell application "Preview"
    if (count every document) = 0 then return false
  end tell
  
  
tell application "System Events"
    tell process aLocName
      tell window 1
        set aTitle to title
      end tell
    end tell
  end tell
  
  
set aPageInfo to pickUpFromToStr(aTitle, "(", ")") of me –English env
  
if aPageInfo = false then
    set aPageInfo to pickUpFromToStr(aTitle, "(", ")") of me –double witdh parenthesis (Japanese env)
  end if
  
set pList to words of aPageInfo
  
set hitF to false
  
repeat with i in pList
    set nRes to chkNumeric(i) of me
    
if nRes = true then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then return
  
return i as integer
end retPreviewDispPageNumber

on getLocalizedName(aBundleID as string)
  set pRes to getProcessByBUndleID(aBundleID) of me
  
if pRes = false then return ""
  
set pName to pRes’s localizedName()
  
return pName as string
end getLocalizedName

–指定プロセスを取得する
on getProcessByBUndleID(aBundleID)
  set appArray to current application’s NSRunningApplication’s runningApplicationsWithBundleIdentifier:aBundleID
  
if appArray’s |count|() > 0 then
    set appItem to appArray’s objectAtIndex:0
    
return appItem
  else
    return false
  end if
end getProcessByBUndleID

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

–数字のみかチェック
on chkNumeric(checkString)
  set digitCharSet to current application’s NSCharacterSet’s characterSetWithCharactersInString:"0123456789"
  
set ret to my chkCompareString:checkString baseString:digitCharSet
  
return ret as boolean
end chkNumeric

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

★Click Here to Open This Script 

Posted in GUI Scripting PDF Text | Tagged 10.13savvy 10.14savvy 10.15savvy NSCharacterSet NSRunningApplication NSScanner Preview | Leave a comment

GUI Scriptingでコンテクストメニューのキャプチャを取得

Posted on 1月 24, 2020 by Takaaki Naganoya

GUI Scriptingを用いてスクリプトエディタ上でコンテクストメニューを表示させて、コンテクストメニューのキャプチャを行うAppleScriptです。macOS 10.14.6上で試しただけなので、10.15上では試していません(たぶん動くと思うのですが)。

–> Watch Demo Movie

画面キャプチャをNSImageに取得する機能については、DSCapture.frameworkをビルドして、AppleScript(実行環境はScript Debugger)から呼び出しています。

–> Download DSCapture.framework (To ~/Library/Frameworks/)

AppleScriptの鬼っ子機能である「GUI Scripting」。ある顧客が「キーボードショートカットとキーボード操作をそのままScript化することでこれを作れ」という仕様を出してきて、とても実現できない内容がまだらに含まれていたので「ナイスジョーク!」と却下した記憶があります。

「できること」(キーボードショートカット操作)に「できないこと」(目視で当該データの位置確認)を混ぜて仕様を出されると悪夢でしかありません。

あと、GUI Scriptingで操作を行うと信頼性がないうえに、信頼性を確保するためには「待つ」という処理が必要だったり、本来のAppleScriptの処理速度よりも100倍以上遅くなるので、本当に必要な箇所にだけ使う&可能なかぎり使用しないというのが自分の基本的なスタンスです。

コンテクストメニューの画面キャプチャを自動で

そんな中、新型Piyomaru Script Assistant(macOS 10.14以降対応。10.13は無視)の資料を作るために、コンテクストメニューの内容を画面キャプチャする必要に迫られました。

AppleScriptが500個ぐらい、階層化されたメニューに入っているので、これの画面キャプチャを撮るだけで一仕事です。テストで1階層分の資料を作ってみたのですが、けっこうかかります。

そこで、スクリプトエディタ上で表示させたコンテクストメニューをプログラムで撮れないかを検討することになります。

真っ先に調べたのはCocoaにそういう機能がないかどうか。自前のプログラムのビューの内容をキャプチャすることはできますが、他のアプリケーションのビューをキャプチャする機能は見つかりませんでした。

仕方がないので、画面のキャプチャをまるごと取得して、撮影対象のビューの座標から画像の切り抜きを行うようにしてみました。

GUI Scriptingでコンテクストメニューを表示させるのは無理かと思っていたのですが、できるんですね。これも冗談半分で探してみたらあっけなく方法が見つかって、

tell theTarget to perform action "AXShowMenu" --コンテクストメニューの表示

これで実際にコンテクストメニューの表示を行えました。

実現はいろいろ大変

スクリプトエディタ上の画面撮影を行うので、Scriptの実行そのものはScript Debuggerで実行。

問題になったのは2点。

1つは、画面キャプチャの撮影を行うフレームワークでメインディスプレイから画面イメージをNSImageで取得し、その結果をメイン側(暗黙のrunハンドラといったほうがいいのか)のプロパティに返しているのですが、すぐに反映されないらしくて、クラッシュを頻発。

結局、プロパティに内容が反映されたことをループで時間待ちして受信。

もう1つ困った点は、コンテクストメニューの表示解除(キャンセル)。1つのコンテクストメニュー表示&キャプチャ撮影だけでは意味がありません。選択メニュー項目を変更して、複数のコンテクストメニューのキャプチャを順次行えなくてはなりません。

いろいろな方法を試してみたものの、最前面のアプリケーションを「スクリプトエディタ」でない状態を作ればコンテクストメニュー表示状態は解除されます(このあたり、AppleScriptが純粋なプログラマーに苦手とされる理由。実際に使っているユーザーでないとこういう発想自体が出てこない)。Script Debugger側で実行しているので、Script Debuggerをactivateすることでその状態(コンテクストメニュー表示解除)を作ってみました。

そんなわけでコンテクストメニューの(選択項目を変更して全項目を選択状態にした)画面キャプチャができました。

現在は1階層分のコンテクストメニューの撮影を行なっていますが、メニュー階層をすべてAppleScript側からスキャンして、それぞれのメニューに対して、各メニュー項目を表示した状態で巡回キャプチャできるとよさそうです。

cropNSImageTo:{x1, y1, x2, y2} fromImage:theImage ハンドラは、Y座標の変換をこの用途(全画面キャプチャしたNSImageの切り抜き)の帳尻合わせのために書き換えています。この用途以外では使えないはずです。

AppleScript名:GUI Scriptingで指定したGUI部品のキャプチャを取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/24
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "DSCapture" –https://github.com/kiding/DSCapture.framework
use framework "AppKit"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSUUID : a reference to current application’s NSUUID
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep

property targPos : {0, 0}
property targSize : {0, 0}
property screenSize : {}
property outPath : ""
property captImg : missing value

set (my captImg) to missing value

tell application "System Events"
  tell process "スクリプトエディタ" –"Script Editor" (Japanese localized name)
    –Editor Area Reference
    
set theTarg to text area 1 of scroll area 1 of splitter group 1 of splitter group 1 of window 1
  end tell
end tell

repeat with i from 1 to 19
  
  
set captImg to missing value
  
set outPath to (POSIX path of (path to desktop)) & ((NSUUID’s UUID()’s UUIDString()) as string) & ".png"
  
  
set {mPos, mSize} to dispMenu(i, theTarg) of me
  
copy mPos to {cx1, cy1}
  
copy mSize to {cWidth, cHeight}
  
  
screenCapture() of me
  
  
repeat 100 times
    if (my captImg) is not equal to missing value then exit repeat
    
delay 0.1
  end repeat
  
  
  
set bImg to (my cropNSImageTo:{cx1, cy1, cWidth, cHeight} fromImage:(my captImg))
  
saveNSImageAtPathAsPNG(bImg, my outPath) of me
  
  
tell current application to activate –Cancel Context Menu
  
delay 0.1
end repeat

on screenCapture()
  current application’s DSCapture’s sharedCapture()’s |full|()’s captureWithTarget:me selector:"displayCaptureData:" useCG:false
end screenCapture

–Delegate Handler
on displayCaptureData:aSender
  set aCount to aSender’s |count|()
  
set anImage to (aSender’s imageAtIndex:0)
  
set my captImg to anImage
  
–saveNSImageAtPathAsPNG(anImage, my outPath) of me
end displayCaptureData:

on cropNSImageTo:{x1, y1, x2, y2} fromImage:theImage
  set newWidth to x2
  
set newHeight to y2
  
set theSize to (theImage’s |size|())
  
set oldHeight to height of theSize
  
  
— transpose y value for Cocoa coordintates
  
set y1 to oldHeight – newHeight – y1
  
set newRect to {{x:x1, y:y1}, {width:x2, height:y2}}
  
  
theImage’s lockFocus()
  
set theRep to NSBitmapImageRep’s alloc()’s initWithFocusedViewRect:newRect
  
theImage’s unlockFocus()
  
  
set outImage to NSImage’s alloc()’s initWithSize:(theRep’s |size|())
  
outImage’s addRepresentation:theRep
  
  
return outImage
end cropNSImageTo:fromImage:

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –成功ならtrue、失敗ならfalseが返る
end saveNSImageAtPathAsPNG

on dispMenu(selInd, theTarget)
  activate application "Script Editor"
  
tell application "System Events"
    tell process "スクリプトエディタ" –"Script Editor" (Japanese localized name)
      
      
ignoring application responses
        tell theTarget to perform action "AXShowMenu" –コンテクストメニューの表示
      end ignoring
      
      
tell text area 1 of scroll area 1 of splitter group 1 of splitter group 1 of window 1
        tell menu 1
          set mPos to position
          
set mSize to size
          
          
set mList to every menu item
          
tell menu item (13 + selInd)
            set aRes to properties
            
set selected to true
          end tell
        end tell
      end tell
      
    end tell
  end tell
  
  
return {mPos, mSize}
end dispMenu

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 10.14savvy NSBitmapImageRep NSImage NSPNGFileType NSString NSURL NSUUID | Leave a comment

Keynoteで選択中の画像を特定する

Posted on 1月 22, 2020 by Takaaki Naganoya


Keynoteの書類上で選択中のオブジェクト(image)を特定するAppleScriptです。

Keynoteには、選択中のオブジェクトを求めるという重要な機能「selection」が実装されていません。一応、最新版のKeynoteには「selection」の予約語が存在しているものの、slide中の選択オブジェクトではなく「選択中のスライド」が返ってきます。current slideとほぼ同じ動作です。これでは実用性がいまひとつです。

ないと困るselection(get selected object)ですが、実装されていないのは仕方ありません。

そのものズバリの機能が存在していないものの、他のやりかたで選択中の画像を特定してみました。数が少なかったり重複するものが存在していない場合には有効のようです。

本Scriptでは、Keynote書類上で選択中の画像があるという前提の上で、GUI Scripting経由でコピーを行い、選択対象をクリップボードに入れます。このクリップボード内の画像のサイズを取得。次に、現在のKeynote書類の現在のスライド(ページ)上の画像(imageオブジェクト)のサイズを取得し、順次照合。同じサイズのものがあれば、それが選択中のオブジェクトであると類推します。

かなり穴の多いロジックですが、最初の一歩としては悪くないでしょう。とりあえずは、サイズで比較を行い、同一のものがあれば画像同士の類似性を計算するといった方法も検討できそうです。

お手上げになってしまうのは、画像サイズや内容ともに同一のものが複数あった場合です。その場合を除けば割と識別できそうに思えます。

また、ながらく調査を行なってきた「ローカライズ言語に依存しないGUI Scripting」を用いて選択中のオブジェクトのコピーができるとよさそうです。

AppleScript名:Keynoteで選択中の画像を特定する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/22
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set kRes to getSelectedImage() of me
–> image 3 of slide 24 of document id "534F4E65-4459-4B00-95D4-34C3E020467E"

on getSelectedImage()
  my executeKeynoteItemCopy() –Execute Copy From Menu
  
  
–クリップボードの内容をNSImageに
  
set aNSIMage to my getClipboardASImage()
  
if aNSIMage = false then
    return false
  end if
  
  
–クリップボード中のNSImageのサイズを取得
  
set aSize to aNSIMage’s |size|()
  
set selWidth to (aSize’s width) as real
  
set selHeight to (aSize’s height) as real
  
  
–Keynoteの最前面のドキュメントの現在のスライド上の画像からサイズを取得してクリップボード内の画像サイズと照合する
  
tell application "Keynote"
    tell front document
      tell current slide
        set iList to every image
        
        
repeat with i in iList
          set myHeight to (height of i) as real
          
set myWidth to (width of i) as real
          
if {myWidth, myHeight} = {selWidth, selHeight} then
            return contents of i
          end if
        end repeat
      end tell
    end tell
  end tell
  
return false
end getSelectedImage

— クリップボードの内容をNSImageとして取り出して返す
on getClipboardASImage()
  set theNSPasteboard to current application’s NSPasteboard’s generalPasteboard()
  
set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({current application’s NSImage}) options:(missing value)
  
if theAttributedStringNSArray = {} then return false
  
set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0
  
return theNSAttributedString
end getClipboardASImage

on executeKeynoteItemCopy()
  activate application "Keynote"
  
tell application "System Events"
    tell process "Keynote"
      –click menu item "Copy" of menu 1 of menu bar item "Edit" of menu bar 1
      
click menu item "コピー" of menu 1 of menu bar item "編集" of menu bar 1
    end tell
  end tell
end executeKeynoteItemCopy

★Click Here to Open This Script 

Posted in Clipboard GUI Scripting Image | Tagged 10.14savvy 10.15savvy Keynote NSImage NSPasteboard | Leave a comment

GUI ScriptingでGUI要素のIDもOSアップデート後には見直す必要あり

Posted on 1月 13, 2020 by Takaaki Naganoya

GUI Scripting、それは画面上のGUI部品に直接メッセージを送って強引にアプリケーションを動かす必要悪。AppleScript非対応機能を強引に動かすことが目的の機能です。

MarkdownエディタのMacDownも、PDF書き出しについてはAppleScript用語辞書にコマンドが掲載されていないので、Markdown書類のPDF書き出しはGUI Scriptingで行なっています。

メイン環境をmacOS 10.12.6から10.14.6に移行して、はじめて動かした重量級のAppleScriptがあります。指定フォルダ以下のPages、Markdown、Wordなどの書類をすべてデスクトップ上にPDFで書き出して、ファイル名順にならべかえて1つのPDFにまとめるAppleScriptです。

つまり、電子書籍の書き出し&連結作業を1本でこなすScriptなわけで、自分にとっては命綱的に重要なAppleScriptです。

で、こいつがmacOS 10.14.6上でまともに動かないことが判明して、顔色が変わりました。

システム環境設定のセキュリティ系の妨害を受けているのかと思って確認してみると、必要な設定はすべて行なってある状態。MacDownからのPDF書き出しだけが効いていません。


–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceExport()
  activate application "MacDown"
  
tell application "System Events"
    tell process "MacDown"
      — File > Export > PDF
      
–click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
click menuItemRef1
      
      
–Go to Desktop Folder
      
keystroke "d" using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceExport

★Click Here to Open This Script 

ためしに、SIPを解除して実行してみたものの、それでも問題は解決しません。

動きを観察していると、書き出し時に「保存」ではなく「キャンセル」ボタンをクリックしている模様。そこで、ボタンをIndexではなくTitleで指し示してみたら、問題なく書き出しできました。


–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceExport()
  activate application "MacDown"
  
tell application "System Events"
    tell process "MacDown"
      — File > Export > PDF
      
–click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
click menuItemRef1
      
      
–Go to Desktop Folder
      
keystroke "d" using {command down}
      
      
–Save Button on Sheet
      
click button "保存" of sheet 1 of window 1 –保存ボタンのIndexが変わっていた
    end tell
  end tell
end macDownForceExport

★Click Here to Open This Script 

# 久しぶりに怪奇現象っぽい挙動で震えました。だいたい、怪奇現象とか心霊現象っぽい挙動というのは、技術や経験が足りない場合に「そう見える」だけであって、知っていたり経験していれば「当然」という現象に見えます

いま、ちょうどGUI Scriptingは端境期で、手で書かなくてもアプリケーションの状態を検知してオブジェクト階層を動的に走査して動かすようなScriptが一部で使われている一方で、古いタイプのIDやTitleを直接指定するような書き方が残っていたりします。

OSがアップデートされても、IDで書いておけば同じGUI部品を指定できるだろう、という思い込みがありましたが、Titleで指定していたほうがIDの数え方が変わっても影響がない、という現象だったのでしょうか。

久しぶりにハマりそうになりました。あと、MacDownのソースに手を入れて、PDF書き出し命令ぐらいは自前で実装したい気がします。

Posted in GUI Scripting | Tagged 10.14savvy MacDown System Events | Leave a comment

Dynamic Menu Clicker v2

Posted on 11月 23, 2019 by Takaaki Naganoya

動的にアプリケーションのメニューをたどって目的のメニュー項目のオブジェクトを取得し、強制的にクリックを行うAppleScript「Dynamic Menu Clicker」の改良版(v2)です。

「Dynamic Menu Clicker」はGUI Scriptingのメニュークリックの効率化を行います。アプリケーション名とクリックさせるメニュー項目のメニューバーからの階層のメニュータイトルを順次記述したリストを渡すと、クリックを行います。

v2で強化したのは、「トグル式メニュー項目」への対応です。1回目呼び出した時と2回目呼び出した時でメニュー内容がトグル式に入れ替わる項目に対処しています。

–> Watch Demo Movie

先日リリースした「Tanshio」にこのDynamic Menu Clickerを仕込んであり、実際にいろいろテストを実施。トグル式メニュー項目をクリックできないという問題が発覚したため、対処することになりました。やっぱり、実際に使ってみないと問題点も見当たらないところです。


▲1回目の呼び出し時は「行番号を表示」


▲2回目の呼び出し時は「行番号を非表示」

このような動作を行うメニュー項目は思いのほか多く、対処が必要です。そこで、nameで指定しているメニュー項目にindex情報を付記。各メニュー項目をparseする段階でnameとindexを分離し、nameでメニュー項目の存在確認を行なって見つからなかった場合には、indexで指定したメニュー項目を採用することにします。

 {"表示", "行番号を表示[11]"}

“行番号を表示[11]” の項目の[11]というのがインデックス番号です。ちょっとこのあたり、メニューに表示されている項目と合わないのですが、


▲macOS 10.14.6 TouchBar Serverをインストールしてある状態


▲macOS 10.15.1 Mac mini TouchBar系のツールやエミュレータを一切インストールしていない状態

TouchBar搭載機だと表示されるメニュー項目(「Control Stripをカスタマイズ…」)が余計にカウントされているようで。

# このインデックス値がmacOS 10.14と10.15で合っていない可能性があるため、調査中です
# → TouchBar搭載/非搭載、TouchBar Simulator(TouchBar Serverなど)のインストール状況などの影響を受けるようです

このあたり、実際にツールを使わないとわからず、ちょっと不親切ではありますが、PfiddlesoftのUI BrowserはScripterの必須ツールなので、目をつぶることにしましょう。

メニュー項目のクリックについてはだいたいこんなもんかと思いますが、次に着手しているウィンドウ上の各種GUI部品のクリック指定を一意に行う「Dynamic Window Clicker」が、実に大変で、、、、仕様を決めつつ試験実装しているものの、ある程度割り切らないとダメでしょう(諦める機能の見切りが重要)。

システム環境設定ぐらいは操作したいところですが、実際のところユーザー認証が必要な箇所は操作できないと思います。

AppleScript名:Dynamic Menu Clicker
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/10
–  version 2
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

–Force Menu item Clicker by parameters
set appName to "CotEditor" –Application Name
set aList to {"表示", "行番号を表示[11]"} –Localized Menu Titles with index number if toggle

set aRes to clickSpecifiedMenuElement(appName, aList) of me

on clickSpecifiedMenuElement(appName as string, menuList as list)
  set menuObj to generateMenuItemReference(appName, menuList) of me
  
if menuObj = false then return false
  
  
tell application appName to activate
  
  
tell application "System Events"
    click menuObj
  end tell
end clickSpecifiedMenuElement

on generateMenuItemReference(appName, aList)
  tell application "System Events"
    tell application process appName
      set fItem to contents of first item of aList
      
set aList to rest of aList
      
set anObj to menu fItem of menu bar item fItem of menu bar 1
      
      
set aLen to length of aList
      
      
repeat with i from 1 to aLen – 1
        set j to contents of item i of aList
        
set {mRes, fRes} to trimStrFromTo(j, "[", "]") of me
        
        
if fRes = false then
          set tmpObj to menu mRes of menu item mRes of anObj
          
        else
          set tmpObj to menu mRes of menu item mRes of anObj
          
set tmpExist to (exists of tmpObj)
          
if tmpExist = false then
            set tmpObj to menu mRes of menu item (fRes as integer) of anObj
          end if
          
        end if
        
        
copy tmpObj to anObj
      end repeat
      
      
      
set {mRes, fRes} to trimStrFromTo(contents of last item of aList, "[", "]") of me
      
      
if fRes = false then
        set tmpObj2 to menu item (mRes) of anObj
      else
        set tmpObj2 to menu item (fRes as integer) of anObj
      end if
      
      
set exRes to (exists of tmpObj2)
      
if exRes = true then
        return tmpObj2
      else
        return false
      end if
    end tell
  end tell
end generateMenuItemReference

on trimStrFromTo(aStr, fromStr, toStr)
  set f1 to aStr contains fromStr
  
set f2 to aStr contains toStr
  
if {f1, f2} is not equal to {true, true} then
    return {aStr, false}
  end if
  
  
–fromStrは前から探す
  
if fromStr is not equal to "" then
    set sPos to (offset of fromStr in aStr) + 1
  else
    return false
  end if
  
  
–toStrは後ろから探す
  
if toStr is not equal to "" then
    set b to (reverse of characters of aStr) as string
    
set ePos to (offset of toStr in b)
    
set ePos to ((length of aStr) – ePos)
  else
    set ePos to length of aStr
  end if
  
set aRes to text sPos thru ePos of aStr
  
set bRes to text 1 thru (sPos – 2) of aStr
  
  
return {bRes, aRes}
end trimStrFromTo

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 10.14savvy 10.15savvy | Leave a comment

Tanzakuの実証実験用バージョン「Tanshio」の配布を開始

Posted on 11月 6, 2019 by Takaaki Naganoya

Tanzakuの実証試験用バージョン「Tanshio」の配布を開始しました。Tanzakuは、Piyomaru Softwareが開発中の自然言語インタフェース系自動処理プログラムの最新シリーズです。

# Tanzakuの派生シリーズや実験プログラムは「Tan-XXX」と命名します。

TanshioはTanzakuで予定している各種機能の縮小版をひととおり実装して、使い勝手を調べたりユーザー環境でうまく動作するかといったことを検証するためのものです。本バージョンには2020/1/31までの動作期限を設けています。

–> Download Tanshio(70KB)

Tanzakuの動作原理は、「ファイル名にユーザーが行いたい内容を記述することで、Tanzakuプログラムがそれを解釈して実行する」というものです(Talking Droplet)。

Tanshioでは、指定アプリケーションのメニュー項目をファイル名に記述することで、その項目をクリックします。

アプリケーション名>メニュー名>メニュー項目名1>メニュー項目名2

のように、階層メニュー名称をファイル名に記述することで、一切のプログラミングなしにメニュー項目の自動クリックを実現するものです。プログラミングを一切行わず、ファイル名を付け替えるだけでメニューのクリックを行い、ファイル名の指定が正しくない場合にはアプリケーション名、メニュー名などをユーザーに問い合わせることで、正しいファイル名を自動フィードバックします。

コマンドをファイル名から取得し、ファイル名のつけかえにより動作を変更し(可塑性のあるプログラム)、いったんコマンドを受理したあとはツールのように振る舞うといったTanzakuの主な特徴を備えています。さらに、入力コマンド(ファイル名)を誤った場合にはユーザーに対して選択肢を提示して正しいコマンドへと誘導する仕様もこのTanshioに実装。実際に使ってみてウザくないかといった確認を行うためのテストベッドです。

動作OSバージョンはmacOS 10.14.6および10.15.xですが、10.14を推奨します。また、システム環境設定の「セキュリティとプライバシー」で、「アクセシビリティ」「オートメーション」などの項目にTanshioを登録する必要があるため、実行・運用にあたってはシステム管理者権限が必要になります。


▲初期状態のTanshio


▲初期状態だとファイル名にアプリケーション名やメニュー項目名が書かれていないため、エラーになる


▲操作アプリケーション選択


▲操作メニュー選択


▲操作メニュー項目選択(コマンドにたどり着くまで繰り返し)


▲Tanshioが自分自身のファイル名を書き換え、正しく、アプリケーション名やメニュー名、メニュー項目名などが記入される。

この状態でTanshioを実行すると、目的のアプリケーションのメニューを操作します。


▲別のアプリケーションや別のメニュー項目をクリックするように変更したい場合にはファイル名を「Tanshio」のような短い名前に付け替えると起動時にアプリケーション選択/メニュー選択を行う

実際に使ってみて

同じメニュー項目をトグルで差し替えるような処理を行っているアプリケーションだと(例:CotEditorの「表示」>「行番号を表示」/「行番号を非表示」)、メニュー項目名をそのまま指定しても、実行2回目になるとメニュー項目が存在していないので、エラーになってアプリケーション選択とメニュー選択のやり直しになるようです。

なーるーほーどー(汗)

Posted in GUI Scripting | Tagged 10.14savvy 10.15savvy | 2 Comments

画面上の指定座標にマウスカーソルを強制移動させてクリック

Posted on 8月 22, 2019 by Takaaki Naganoya

マウスカーソルを指定座標に強制的に移動させて、マウスクリック(プレス)を行う補助アプリケーション+呼び出しAppleScriptです。

# ご注意:マウスカーソルの移動やクリックを行うのは、本来のAppleScriptの処理ではありません
# ご注意:他のマシン上で同じ動作を再現することが(初心者には)難しいため、おすすめしません

# 本ツールはCodeSignしてMac App Storeで近日リリースする予定です

必要悪! 画面上の部品や座標をクリックする機能

マウスカーソルの強制移動とクリックは、AppleScriptではなるべく避けるべき操作ですが、ごく一部の操作を実行するため、ごくごくまれに必要になることがあります。

macOS標準装備のAppleScript専用のツール「System Events」に「click」コマンドがあり、指定のGUI部品か、あるいは画面上の座標をクリックするようになっています。

ただし、これはあくまで「指定のGUI部品や指定座標をクリックしたというメッセージ」を対象(アプリケーション)に送るというものであり、実際にマウスカーソルを移動させてクリックを実行するものではありません。

強制マウス操作ツールの歴史

それでも、アプリケーションがAppleScriptに対して機能を解放していない機能を呼び出す必要があって、かつ、メニューやボタンのクリックなどで実行できないような場合には、止むを得ず指定座標のクリックをごくまれに行うことがあります(1年に1度ぐらいの頻度)。


▲強制的にマウスカーソルを移動させてクリックする支援ツールの変遷

これまで、Framework呼び出しでこれらの動作を行ってきましたが、macOS 10.14で(SIPを解除しないかぎり)スクリプトエディタ上ではサードパーティのFrameworkを呼べなくなりました(AppleScriptドロップレット上ではバンドル内のFrameworkを呼べます)。

呼び出し側から一番簡単に利用できる方法は、指定座標のクリック機能を持つアプリケーションをXcode上でAppleScriptで作成しておき、sdefを定義して、AppleScript対応アプリケーションをAppleScriptで作るものです(豆腐をすりつぶして「ひろうす」を作るようなこの迂遠さ。Google翻訳で絶対に伝わらないニュアンス。パンをすりつぶしてパンを作るような、、、)。

そのため、いままでFrameworkで運用してきたプログラムを、アプリケーション化してsdefを付加し、スクリプタブルなバックグラウンドアプリケーション(Dockに表示されない、メニューやウィンドウが表示されない)にする必要が出てきます(まんまとAppleにタダ働きさせられているような気がするので気分はよくありませんが)。

自分ではほとんど使わないマウス強制移動&クリックツールを作ってみた

そこで、sdef(AppleScript用語辞書)をつけたライブラリやアプリケーションを作る方向で調査を行っていました。実際にパラメータの受け渡しをどのように行えるのか、どのあたりでハマるのか、どのぐらいの作業量になるのか。

その1つの到達点として、この補助アプリケーション「mouseClick」を作ってみました。バックグラウンド実行専用のため、起動してもDockにアイコンは表示されません(これを知らないユーザーがバックグラウンド起動専用のアプリケーションに、Mac App Sroreでいちゃもんをつけているのを見かけて、遠い目になりました)。

–> Download mouseClick.app (To /Applications)

※ このツールは、AppleScriptからコマンドで操作する専用のものであり、画面上には何も表示されません。

簡単なAppleScriptでマウスカーソルの移動とクリックを実行できます。初回実行時はシステム環境設定の「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でmouseClickに「コンピュータの制御を許可」しておく必要があります(管理者権限が必要、2回目以降は操作不要)。

forceClickでは、画面の左上を原点とした座標系を使用しています。

呼び出し側と実行側をすべてAppleScriptで組めるようになったわけで、Classic MacOS時代からこの手の「指定座標の強制クリック系ソリューション」を(止むを得ず)使ってきた身からするとなかなか感慨深いものがあります。

参考文献:
objective c 入門 CocoaアプリケーションにAppleScriptサポートを追加するにはどうすればよいですか?

AppleScript名:force click sample
tell application "mouseClick"
  force click at {4, 4} –click apple menu
end tell

★Click Here to Open This Script 

Posted in GUI Scripting list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy 10.15savvy | Leave a comment

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

Posted on 7月 15, 2019 by Takaaki Naganoya

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


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

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

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

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

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

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

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

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

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

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

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

★Click Here to Open This Script 

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

CotEditorで編集中のMarkdown書類をPDFプレビュー

Posted on 6月 15, 2019 by Takaaki Naganoya

CotEditorで編集中のMarkdown書類を、MacDownでPDF書き出しして、Skimでオープンして表示するAppleScriptです。

CotEditorにMarkdownのプレビュー機能がついたらいいと思っている人は多いようですが、MarkdownはMarkdownで、方言は多いし標準がないし、1枚もののMarkdown書類だけ編集できればいいのか、本などのプロジェクト単位で編集とか、目次が作成できないとダメとか、リンクした画像の扱いをどうするのかとか、対応しようとすると「ほぼ別のソフトを作るのと同じ」ぐらい手間がかかりそうです(メンテナー様ご本人談)。

そこで、AppleScript経由で他のソフトを連携させてPDFプレビューさせてみました。これなら、誰にも迷惑をかけずに、今日この時点からすぐにMarkdownのプレビューが行えます(当然、HTML書き出ししてSafariでプレビューするバージョンははるかかなた昔に作ってあります)。

ただし、OS側の機能制限の問題で、CotEditor上のスクリプトメニューから実行はできません(GUI Scriptingの実行が許可されない)。OS側のスクリプトメニューに登録して実行する必要があります。

GUI Scriptingを利用してメニュー操作を行なっているため、システム環境設定で許可しておく必要があります。

本来であれば、PDFの書き出し先フォルダ(この場合は書き出しダイアログで、GUI Scirptingを用いてCommand-Dで指定して一律に場所指定が行えるデスクトップフォルダ)に同名のPDFが存在しないかどうかチェックし、存在すれば削除するといった処理が必要ですが、面倒だったのであらかじめMarkdown書類をUUIDにリネームしておくことで、書き出されたPDFも同じくUUIDのファイル名になるため、論理上はファイル名の衝突を回避できるため、削除処理を省略しています。

AppleScript名:🌏レンダリングしてPDFプレビュー
— Created 2019-06-15 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSUUID : a reference to current application’s NSUUID
property NSWorkspace : a reference to current application’s NSWorkspace

–オープン中のMarkdown書類を取得する
tell application "CotEditor"
  tell front document
    set cStyle to coloring style
    
if cStyle is not equal to "Markdown" then
      display dialog "編集中のファイルはMarkdown書類ではないようです。" buttons {"OK"} default button 1
      
return
    end if
    
    
set aPath to path
  end tell
end tell

–一時フォルダにMarkdown書類をコピー
set sPath to (path to temporary items)
tell application "Finder"
  set sRes to (duplicate ((POSIX file aPath) as alias) to folder sPath with replacing)
end tell

–コピーしたMarkdown書類をリネーム
set s1Res to sRes as alias
set aUUID to NSUUID’s UUID()’s UUIDString() as text –UUIDを作成する
tell application "Finder"
  set name of s1Res to (aUUID & ".md")
end tell

–Markdown書類をデスクトップにPDF書き出し
set pdfRes to exportFromMacDown(POSIX path of s1Res) of me

–PDF Viewerでオープン
tell application "Skim" –Preview.appでもOK
  activate
  
open pdfRes
end tell

–一時フォルダに書き出したMarkdown書類を削除
tell application "Finder"
  delete s1Res
end tell

–指定のMacDownファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromMacDown(anAlias)
  set s1Text to paragraphs of (do shell script "ls ~/Desktop/*.pdf") –pdf書き出し前のファイル一覧
  
  
tell application "MacDown"
    open {anAlias}
  end tell
  
  
macDownForceSave() of me
  
  
tell application "MacDown"
    close every document without saving
  end tell
  
  
do shell script "sync" –ねんのため
  
  
set s2Text to paragraphs of (do shell script "ls ~/Desktop/*.pdf") –pdf書き出し後のファイル一覧
  
  
set dRes to getDiffBetweenLists(s1Text, s2Text) of me –デスクトップ上のPDFファイル名一覧の差分を取得
  
set d2Res to (addItems of dRes)
  
  
if length of d2Res ≥ 1 then
    return contents of first item of d2Res
  else
    error "Error in exporting PDF to desktop folder…."
  end if
end exportFromMacDown

on getDiffBetweenLists(aArray as list, bArray as list)
  set allSet to current application’s NSMutableSet’s setWithArray:aArray
  
allSet’s addObjectsFromArray:bArray
  
  
–重複する要素のみ抜き出す
  
set duplicateSet to current application’s NSMutableSet’s setWithArray:aArray
  
duplicateSet’s intersectSet:(current application’s NSSet’s setWithArray:bArray)
  
  
–重複部分を削除する
  
allSet’s minusSet:duplicateSet
  
set resArray to (allSet’s allObjects()) as list
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aArray
  
set bSet to current application’s NSMutableSet’s setWithArray:resArray
  
aSet’s intersectSet:bSet –積集合
  
set addRes to aSet’s allObjects() as list
  
  
set cSet to current application’s NSMutableSet’s setWithArray:bArray
  
cSet’s intersectSet:bSet –積集合
  
set minusRes to cSet’s allObjects() as list
  
  
return {addItems:minusRes, minusItems:addRes}
end getDiffBetweenLists

–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceSave()
  activate application "MacDown"
  
tell application "System Events"
    tell process "MacDown"
      — File > Export > PDF
      
click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
      
–Go to Desktop Folder
      
keystroke "d" using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceSave

–Bundle IDからアプリケーションのPathを返す
on retAppAbusolutePathFromBundleID(aBundleID)
  set appPath to NSWorkspace’s sharedWorkspace()’s absolutePathForAppBundleWithIdentifier:aBundleID
  
if appPath = missing value then return false
  
return appPath as string
end retAppAbusolutePathFromBundleID

★Click Here to Open This Script 

Posted in file File path GUI Scripting Markdown PDF shell script | Tagged 10.11savvy 10.12savvy 10.13savvy CotEditor Finder MacDown NSUUID NSWorkspace Skim | Leave a comment

Keynoteで指定IDのテキストフレームを縦書き化

Posted on 4月 26, 2019 by Takaaki Naganoya

macOS 10.13/10.14+Keynote 9.0.1で指定のIDのテキストフレームを縦書き化するAppleScriptです。

Keynote 9でサポートされた文字の縦書き表示。しかし、肝心のAppleScriptでこれを操作する機能が備わっていなかったので、強引にGUI側から操作して縦書き化させてみました。

実行前に「システム環境設定」の「セキュリティとプライバシー」>「アクセシビリティ」にて、スクリプトエディタあるいはScript Debuggerに対してアクセシビリティ機能(「下のアプリケーションにコンピュータの制御を許可」)を許可しておく必要があります。

また、macOS 10.14上にてはじめてScript DebuggerからKeynoteを動かそうとした場合には、「オートメーション」項目でScript Debuggerの操作を許可しておく必要があります。


▲Before


▲After


▲2D Bin PackingのAppleScriptに、本ルーチンを組み込んで想定矩形座標内に文字を詰め込んでみた。日本語はフレームの回転ではなく縦書き表示できたほうが可読性が上がるかも


▲Keynote上でオブジェクトを選択状態にしてテキストフレームの「枠」を表示させてみるとけっこう詰まっていることがわかる

AppleScript名:Keynoteで指定IDのテキストフレームを縦書き化
set tmpID to 1

selectTextItemID(tmpID) of me
set aRes to makeTextFrameVertival(tmpID, true) of me

on makeTextFrameVertival(anID, aFlag)
  tell application "System Events"
    activate
    
set aGSflag to UI elements enabled
    
if aGSflag = false then return false
  end tell
  
  
activate application "Keynote"
  
tell application "System Events"
    tell process "Keynote"
      tell radio button "フォーマット" of radio group 1 of toolbar 1 of window 1 –*Localized*
        set aVal to value
        
if aVal = 0 then
          click
        end if
      end tell
      
      
tell radio button "テキスト" of radio group 1 of window 1 –*Localized*
        click
      end tell
      
      
tell checkbox "縦書きテキスト" of scroll area 1 of window 1 –*Localized*
        set aVal to value
        
if (aVal = 0) and (aFlag = true) then
          click
        else if (aVal = 1) and (aFlag = false) then
          click
        end if
      end tell
      
    end tell
  end tell
  
return true
end makeTextFrameVertival

on selectTextItemID(anID)
  tell application "Keynote"
    tell front document
      tell current slide
        set anObj to a reference to text item anID
        
properties of anObj
      end tell
    end tell
  end tell
end selectTextItemID

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 10.13savvy 10.14savvy Keynote | Leave a comment

macOS 10.14のバグ? アクセシビリティ認証

Posted on 2月 10, 2019 by Takaaki Naganoya

macOS 10.14.4Betaを使っていて遭遇したトラブルというかバグのような挙動なのですが、「システム環境設定」の「セキュリティーとプライバシー」>「プライバシー」>「アクセシビリティ」の項目。これは、Scripterにはおなじみの「GUI Scriptingの許可」を行う設定項目です。

# 本件については、最新のmacOS 10.14.6+同OSで利用可能な最新のXcode 11.3.1の組み合わせで、後述のように回避できるようになったことを確認しています

ここに、同一名称で異なるバージョン(バージョン番号の大きいもの=新バージョン)のアプリケーションが登録されない、という不具合です。

たとえば、v1.0のappletを登録しておいて、v2.0のappletを登録しようとしても、すでにv1.0が登録されているので追加登録できないし、いったん登録したものを削除できなかったという状況でした。

この問題のどこが困るかといえば、Xcode上でAppleScriptによるアプリケーションを開発していて、その中でGUI Scriptingを利用しているような場合です。Xcode上でAppleScriptアプリケーションをビルドすると、まず最初に動作確認用にdebugビルドを行うことになりますが、これで1つのバイナリができます。テスト実行時にアクセシビリティ認証を取得して、実行。

次いで、実際に単体で実行する(Xcodeのログ表示にlogコマンドによる表示が出ない)Relaseビルドのバイナリをビルドして実行。debugビルドとは別のバイナリができて、実行してみるとOS側からアクセシビリティ認証が許可されず、実行できないという状況でした。いったんこうなると、debugビルドのバイナリのアクセシビリティ認証を解除してもReleaseビルドを登録して認証することもできず、お手上げの状態でした。

以前、macOS 10.10あたりでこの項目で不可解な挙動が見られたことがありましたが、それが復活したような不安定さを感じます。ただ、テスト機はHDDで運用しているため、設定項目の変更がなかなか反映されないといった独特の挙動が発生することもあり、継続調査は必要だと感じていました。まだ、他のユーザーの環境でも同様の問題が発生するか「裏取り」ができていない状況でもありました。

この問題がmacOS 10.14 Beta Relaseの最中に突然発症したので、当時仕事で作成途中のプログラムがGUI Scriptingを一部で利用しており、現状のままでは10.14対応ができないと顧客に報告する必要が出てきました。

本BlogにアクセスしているクライアントのOSバージョンについてWebサーバーのアクセスlogから調査してみたところ、(自分の当時のメイン環境が10.12ということもありますが)10.12が最多。10.9から10.12にほぼ同じぐらいのクライアントが存在しているもようです。鬼っ子10.13や鬼っ子改の10.14については、アクセスlogに形跡が残っていない(Unknown Version?)としか言いようがありません。10.14は多い可能性もあります。

AppleにはmacOS 10.13でバギーなまま使い物にならない状態のOSをリリースしたという「前科」があり、10.14もその延長線上にあるということから、つねに疑問を持って接しています。

# かくして、macOS 10.14が正式リリースされ、開発には利用していましたが、メイン環境は長らく移行しませんでした。macOS 10.14.6が出た段階で再評価を行い、この問題が解決されていたので移行。当時macOS 10.15が出ていたものの、メールが消えるといった致命的な障害報告があったために、移行できない危険なOSだと判断しました。

[追記]2020/3時点での状況

macOS 10.14.x+Xcodeのプロジェクト内でGUI Scriptingを使ったプログラムの認証や開発が行えたなかった件については、Xcode側の対応が改善されたためか、OS側の対処が改善されたためか、現在では解決されています。

OSバージョン:macOS 10.14.6
Xcodeバージョン:11.3.1

この環境で、「File」>「New」>「Project」>「AppleScript App」のプロジェクトを作成し、中で他のアプリケーションをGUI Scripting経由でコントロールする処理を含むプログラムを書いて、アプリケーションとしてビルドし、OS側のセキュリティ機能と折り合いをつけて動作するようにできています。

Xcode Project側で設定すべき点は2つ。

(1)Info.plistに「Privacy – AppleEvents Sending Usage Description」のエントリを作成する(このエントリの内容の文字列が、アプリケーション初回起動時のセキュリティダイアログで表示されます)

(2)Xcode Projectの「TARGETS」でビルドターゲットを選択し、「Signning & Capabilities」>「All」で「Runtime Exceptions」の「Apple Events」にチェックを入れる

これらの設定を行って、Code Signしてあればビルドして実行すると、初回時にOSのセキュリティダイアログが表示され、そののちにGUI Scriptingの認証ダイアログが表示され、認証後、ビルドしたアプリケーションをいったん終了。

再度ビルド&実行するとOS側の「セキュリティ」認証とGUI Scriptingの(アクセシビリティの)認証が通ってアプリケーションの実行ができました。

Posted in AppleScript Application on Xcode Bug GUI Scripting | Leave a comment

Dynamic Menu Clicker

Posted on 12月 31, 2018 by Takaaki Naganoya

動的にアプリケーションのメニューをたどって目的のメニュー項目のオブジェクトを取得し、強制的にクリックを行うAppleScriptです。

GUI Scriptingのメニュークリックの効率化を行います。アプリケーション名とクリックさせるメニュー項目のメニューバーからの階層のメニュータイトルを順次記述したリストを渡すと、クリックを行います。

GUI Scriptingは強力なソリューションではありますが、たとえばメニューの強制操作を行うのであれば、メニュー項目がイネーブルになっているか事前に確認する必要がありますし、OSやアプリケーションのバージョンアップ時にメニュー項目の並び順が変わったりすると効かなかったりします。

そのため、目的のメニュー項目の並び順が変わったぐらいでは影響がないように備えるぐらいはしておきたいところです。

これまでにも、タイトルを指定すると指定アプリケーションのすべてのGUI要素から検索して実行するようなScriptを作成して試してみてはいたのですが、これだと全GUI要素をいったん取得して絞り込み検索を行うので、処理速度がいまひとつです(正直なところ、遅い)。

なので、それほど高機能ではないものの、スピードが速いものを作ってみました。メニューをたどって目的のメニュー項目を探し出します。メニュー項目を順番にリストに記述すればよいだけなので、普通にtell文でオブジェクト階層を指定するよりも手軽です。

AppleScript名:Dynamic Menu Clicker
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/12/31
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

–Force Menu item Clicker by parameters
set appName to "Keynote" –Application Name
set aList to {"配置", "オブジェクトを整列", "左揃え"} –Localized Menu Titles

set aRes to clickSpecifiedMenuElement(appName, aList) of me

on clickSpecifiedMenuElement(appName as string, menuList as list)
  set menuObj to generateMenuItemReference(appName, menuList) of me
  
if menuObj = false then return false
  
  
tell application appName to activate
  
  
tell application "System Events"
    click menuObj
  end tell
end clickSpecifiedMenuElement

on generateMenuItemReference(appName, aList)
  tell application "System Events"
    tell application process appName
      set fItem to contents of first item of aList
      
set aList to rest of aList
      
set anObj to menu fItem of menu bar item fItem of menu bar 1
      
      
set aLen to length of aList
      
      
repeat with i from 1 to aLen – 1
        set j to contents of item i of aList
        
set tmpObj to menu j of menu item j of anObj
        
copy tmpObj to anObj
      end repeat
      
      
set tmpObj2 to menu item (contents of last item of aList) of anObj
      
set exRes to exists of tmpObj2
      
if exRes = true then
        return tmpObj2
      else
        return false
      end if
    end tell
  end tell
end generateMenuItemReference

★Click Here to Open This Script 

Posted in GUI Scripting | Tagged 10.11savvy 10.12savvy System Events | 1 Comment

GUI Scripting的なUI Elementへの参照から所属するアプリケーション名を取得する

Posted on 9月 17, 2018 by Takaaki Naganoya

GUI Scripting的なUI Elementへの参照をもとに、そのUI Elementが所属するアプリケーション名を取得するAppleScriptです。

こんな、Safariの「ファイル」メニューの中にあるメニュー項目、

menu item “PDFとして書き出す…” of menu “ファイル” of menu bar item “ファイル” of menu bar 1 of application process “Safari”

があったとして、このオブジェクトが所属しているアプリケーション名を取得したいというケースがありました。上記の例では「Safari」がそれに該当します。

GUI Scripting的なお約束として、オブジェクト階層が上から下に向かって取得する場合には「entire contents」でまとめて取得できるのですが、下から上に向かってparentで取得できるわけでもなく、アプリケーションプロセス名をさかのぼって取得するといった処理ができていませんでした。

AppleScriptの「仕様」的には不可能です。

でも、不可能であることさえわかれば(正攻法では不可能なので)、いつものトリッキーかつ邪道な手口でなんとかなりそうです。

そうです、エラートラップ中で無理やりエラーを起こして、そのエラーメッセージの情報から取得してしまおうという、いつものやつです。

というわけで、GUI Elementの情報から所属アプリケーション名を取得できました。

最近は、さらなる「卑劣な手段」を用意しているので、そちらで華麗かつ卑劣に処理してもよかったのですが、とりあえず手短にまとめたかったのでいつものやつで処理しました。

AppleScript名:GUI Scripting的なUI Elementへの参照から所属するアプリケーション名を取得する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/09/17
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—

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

tell application "System Events"
  set aRef to menu item "PDFとして書き出す…" of menu "ファイル" of menu bar item "ファイル" of menu bar 1 of application process "Safari"
end tell

set appName to retRootAppProcessNameFromGUIElementRef(aRef) of me
–> "Safari"

–GUI Scripting的なUI Elementへの参照から、rootのアプリケーションプロセス名を取得する
on retRootAppProcessNameFromGUIElementRef(aRef)
  try
    set aStr to aRef as string –force cause error
  on error erM
    set offsetA to offset of " of application \"System Events\"" in erM
    
set offsetB to offset of "«class pcap» " in erM
    
set appName to text (offsetB + (length of "«class pcap» ") + 1) thru (offsetA – 2) of erM
    
return appName as string
  end try
end retRootAppProcessNameFromGUIElementRef

★Click Here to Open This Script 

Posted in GUI GUI Scripting System | Tagged 10.11savvy 10.12savvy 10.13savvy System Events | Leave a comment

表示中のCotEditor書類の「次」のファイルを縦書きでオープン v2

Posted on 2月 14, 2018 by Takaaki Naganoya

CotEditorでオープン中の書類の「次」のファイルを縦書きでオープンするAppleScriptです。

CotEditorでオープン中の書類のパスを取得して、同一フォルダ内にあるファイル名一覧からオープン中の書類の「次」に該当する書類をオープンします。

ファイルオープン後にCotEditorのメニューをGUI Scriptingで強制的に操作して縦書きに設定します。

CotEditor v3.3.2のメニューにバグがあって、横書きのメニュー項目にも縦書きのメニュー項目にも「縦書き」と書いてあり、メニュー項目を名称で指定するとうまく動きません。そこだけItem No.で指定しています。このメニュー項目の不具合については、近い将来のアップデートで修正される見込みです。


▲CotEditor内蔵のScript Menuから実行するとGUI Scriptingの実行が制限されて縦書きにならない


▲OS側のScript Menuから実行すると問題なく実行できる

AppleScript名:表示中のCotEditor書類の「次」のファイルを縦書きでオープン v2
— Created 2017-12-15 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use bPlus : script "BridgePlus"

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property SMSForder : a reference to current application’s SMSForder
property NSPredicate : a reference to current application’s NSPredicate
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSURLIsPackageKey : a reference to current application’s NSURLIsPackageKey
property NSURLIsDirectoryKey : a reference to current application’s NSURLIsDirectoryKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants
property NSDirectoryEnumerationSkipsSubdirectoryDescendants : a reference to current application’s NSDirectoryEnumerationSkipsSubdirectoryDescendants

load framework

tell application "CotEditor"
  set dCount to count every document
  
if dCount = 0 then return
  
  
tell front document
    set curPath to path
  end tell
  
  
tell window 1
    set aBounds to bounds
  end tell
end tell

set aPath to NSString’s stringWithString:curPath
set fileName to (aPath’s lastPathComponent()) –ファイル名
set pathExtension to aPath’s pathExtension() as string
set parentFol to (aPath’s stringByDeletingLastPathComponent()) as string —親フォルダ

–同じフォルダから同じ拡張子のファイルのファイル名を取得
set fList to my getFilesByIncludedStringInName:(pathExtension) fromDirectory:(parentFol) exceptPackages:(true)

–昇順ソート
set aArray to NSArray’s arrayWithArray:fList
set desc1 to NSSortDescriptor’s sortDescriptorWithKey:"self" ascending:true selector:"localizedCaseInsensitiveCompare:"
set bArray to aArray’s sortedArrayUsingDescriptors:{desc1}

–ファイル名検索
set aIndex to (SMSForder’s indexesOfItem:fileName inArray:bArray inverting:false) as list
if aIndex = {} then
  display notification "Error: File Not Found"
  
return
end if

set bIndex to (contents of first item of aIndex) + 1 + 1 –0 based to 1 based conversion & next one
set aLen to length of (bArray as list)
if bIndex > aLen then
  display notification "Error: Out of bounds"
  
return
end if

set newFile to contents of item bIndex of (bArray as list)
set newPath to parentFol & "/" & newFile

tell application "CotEditor"
  set oldDoc to front document
  
  
open (POSIX file newPath) as alias
  
tell window 1
    set bounds to aBounds
  end tell
  
  
close oldDoc without saving
end tell

makeWinVertical() of me –縦書き表示

–指定フォルダ内の指定文字列を含むファイル名のlistを抽出する
on getFilesByIncludedStringInName:(fileNameStr as string) fromDirectory:(sourceFolder) exceptPackages:(packageF as boolean)
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to (NSDirectoryEnumerationSkipsPackageDescendants as integer) + (NSDirectoryEnumerationSkipsHiddenFiles as integer) + (NSDirectoryEnumerationSkipsSubdirectoryDescendants as integer)
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_("lastPathComponent CONTAINS %@", fileNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat with i in foundItemList
    set j to contents of i
    
set {theResult, isDirectory} to (j’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value))
    
    
–Collect files
    
if (isDirectory as boolean = false) then
      (anArray’s addObject:j)
    else if (packageF = false) then
      –Allow Package files?
      
set {theResult, isPackage} to (j’s getResourceValue:(reference) forKey:(NSURLIsPackageKey) |error|:(missing value))
      
if (isPackage as boolean) = true then
        (anArray’s addObject:j)
      end if
    end if
    
  end repeat
  
  
return (anArray’s valueForKey:"lastPathComponent") as list
end getFilesByIncludedStringInName:fromDirectory:exceptPackages:

–Make CotEditor’s front window to Vertical display mode (Tategaki)
on makeWinVertical()
  activate application "CotEditor"
  
tell application "System Events"
    tell process "CotEditor"
      try
        click menu item 3 of menu 1 of menu item "文章の方向" of menu 1 of menu bar item "フォーマット" of menu bar 1
      end try
    end tell
  end tell
end makeWinVertical

★Click Here to Open This Script 

Posted in file GUI Scripting | Tagged 10.11savvy 10.12savvy 10.13savvy CotEditor | Leave a comment

MacDownで編集中のMarkDown書類で見出しが見出し落ちしていないかチェック

Posted on 2月 6, 2018 by Takaaki Naganoya

MacDownで編集中のMarkdown書類をいったんデスクトップにPDF書き出しして、見出しがページ末尾に位置していないか(見出し落ち)をチェックするAppleScriptです。

MacDownのAppleScript対応機能が少ないので、ほとんどAppleScriptだけで処理しています。Cocoaの機能を呼べるようになったので、とくに問題ありません。

MacDownで編集中の最前面のMarkdown書類からパスを取得し、AppleScriptで直接ファイルから内容を読み込み、正規表現で見出し一覧を取得します。

MacDownからGUI Scripting経由でメニューをコントロールしてPDF書き出しを行い、ページ単位でPDFからテキストを抽出。不要な空白文字列などを削除。


▲いわゆる「見出し落ち」の状態。ページ末尾に見出し項目が存在している

各ページのテキストが見出しの内容で終了していれば、結果出力用の変数midashiOchiListに{ページ数, 見出し名称} を追加して出力します。

–> {{2, “対象となるFramework”}}

AppleScript名:MacDownで編集中のMarkDown書類で見出しが見出し落ちしていないかチェック
— Created 2017-08-12 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property NSString : a reference to current application’s NSString
property NSCharacterSet : a reference to current application’s NSCharacterSet
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators

set docName to getFrontmostMarkdownDocName() of me
if docName = false then return

set newName to repFileNameExtension(docName, ".pdf") of me
set newPath to (POSIX path of (path to desktop)) & newName
set dRes to deleteItemAt(newPath) of me –前回実行時にデスクトップに残った同名のPDFを削除する

–Markdownのソースを元ファイルから直接読み出す
set docSourcePath to getFrontmostMarkdownFullPath() of me
set aStr to (read (docSourcePath as alias) as «class utf8»)

–getHeader List
set aList to retHeaders(aStr) of me

–Export Markdown to PDF (desktop folder)
macDownForceSave() of me

set tList to textInPDFinEachPage(newPath) of me

set pCount to 1
set midashOchiList to {}
repeat with i in tList
  set j to (contents of i) as string
  
  
repeat with ii in aList
    set jj to (contents of second item of ii) as string
    
–set jj2 to replaceText(jj, "(", "(") of me
    
–set jj3 to replaceText(jj2, ")", ")") of me
    
    
if (j ends with jj) then
      set the end of midashOchiList to {pCount, jj}
    end if
  end repeat
  
set pCount to pCount + 1
end repeat

return midashOchiList

on retHeaders(aCon)
  set tList to {}
  
set regStr to "^#{1,6}[^#]*?$"
  
  
set headerList to my findPattern:regStr inString:aCon
  
repeat with i in headerList
    set j to contents of i
    
set regStr2 to "^#{1,6}[^#]*?"
    
set headerLevel to length of first item of (my findPattern:regStr2 inString:j)
    
set tmpHeader1 to text (headerLevel + 1) thru -1 in j
    
–ヘッダーの前後から空白文字をトリミング
    
set tmpHeader2 to trimWhiteSpaceFromHeadAndTail(tmpHeader1) of me
    
    
–ヘッダー部でPDF書き出ししたときに全角文字が半角文字に置換されてしまうケースに対処
    
set tmpHeader3 to replaceText(tmpHeader2, "(", "(") of me
    
set tmpHeader4 to replaceText(tmpHeader3, ")", ")") of me
    
    
set the end of tList to {headerLevel, tmpHeader4}
  end repeat
  
  
return tList
end retHeaders

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list — so we can loop through
  
set theResult to {} — we will add to this
  
set theNSString to NSString’s stringWithString:theString
  
repeat with i from 1 to count of items of theFinds
    set theRange to (item i of theFinds)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

–指定文字列の前後から空白をトリミング
on trimWhiteSpaceFromHeadAndTail(aStr as string)
  set aString to NSString’s stringWithString:aStr
  
set bString to aString’s stringByTrimmingCharactersInSet:(NSCharacterSet’s whitespaceAndNewlineCharacterSet())
  
return bString as list of string or string –as anything
end trimWhiteSpaceFromHeadAndTail

–ファイル名の拡張子を置換する
on repFileNameExtension(origName, newExt)
  set aName to current application’s NSString’s stringWithString:origName
  
set theExtension to aName’s pathExtension()
  
if (theExtension as string) is not equal to "" then
    set thePathNoExt to aName’s stringByDeletingPathExtension()
    
set newName to (thePathNoExt’s stringByAppendingString:newExt)
  else
    set newName to (aName’s stringByAppendingString:newExt)
  end if
  
  
return newName as string
end repFileNameExtension

on textInPDFinEachPage(thePath)
  set aList to {}
  
  
set anNSURL to (current application’s |NSURL|’s fileURLWithPath:thePath)
  
set theDoc to current application’s PDFDocument’s alloc()’s initWithURL:anNSURL
  
  
set theCount to theDoc’s pageCount() as integer
  
  
repeat with i from 1 to theCount
    set thePage to (theDoc’s pageAtIndex:(i – 1))
    
set curStr to (thePage’s |string|())
    
set curStr2 to curStr’s decomposedStringWithCanonicalMapping() –Normalize Text with NFC
    
set targString to string id 13 & string id 10 & string id 32 & string id 65532 –Object Replacement Character
    
set bStr to (curStr2’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s characterSetWithCharactersInString:targString))
    
set the end of aList to (bStr as string)
  end repeat
  
  
return aList
end textInPDFinEachPage

–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceSave()
  activate application "MacDown"
  
tell application "System Events"
    tell process "MacDown"
      — File > Export > PDF
      
click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
      
–Go to Desktop Folder
      
keystroke "d" using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceSave

on getFrontmostMarkdownDocName()
  tell application "MacDown"
    set dList to every document
    
set dCount to count every item of dList
    
if dCount is not equal to 1 then
      display notification "Markdown document is not only one."
      
return false
    end if
    
    
tell document 1
      set docName to name
    end tell
    
return docName
  end tell
end getFrontmostMarkdownDocName

on getFrontmostMarkdownFullPath()
  tell application "MacDown"
    tell document 1
      set aProp to properties
    end tell
  end tell
  
  
set aPath to (file of aProp)
end getFrontmostMarkdownFullPath

–任意のデータから特定の文字列を置換
on replaceText(origData, origText, repText)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {origText}
  
set origData to text items of origData
  
set AppleScript’s text item delimiters to {repText}
  
set origData to origData as text
  
set AppleScript’s text item delimiters to curDelim
  
–set b to origData as text
  
return origData
end replaceText

–指定のPOSIX pathのファイルを強制削除(あってもなくてもいい)
on deleteItemAt(aPOSIXpath)
  set theNSFileManager to current application’s NSFileManager’s defaultManager()
  
set theResult to theNSFileManager’s removeItemAtPath:(aPOSIXpath) |error|:(missing value)
  
return (theResult as integer = 1) as boolean
end deleteItemAt

★Click Here to Open This Script 

Posted in GUI Scripting Markdown PDF Text | Tagged 10.11savvy 10.12savvy 10.13savvy MacDown | Leave a comment

CotEditorの最前面で表示中のDocumentを縦書き表示に

Posted on 2月 6, 2018 by Takaaki Naganoya

CotEditorで表示中のテキストファイルを、GUI Scripting経由で縦書き表示するAppleScriptです。

CotEditorの途中のバージョンでメニュー構成が変更になったため、本Scriptは動かなくなりました。

とりあえず、メニュー変更に追従して書き換えを行なって使っていましたが、根本的な解決策を1024jpさんに教えてもらえたので(Xattributeの書き換え)、そちらをおすすめします。

AppleScript名:CotEditorの最前面で表示中のDocumentを縦書き表示に
makeWinVertical() of me

on makeWinVertical()
  activate application "CotEditor"
  
tell application "System Events"
    tell process "CotEditor"
      try
        click menu item "縦書きで表示" of menu 1 of menu bar item "フォーマット" of menu bar 1
      end try
    end tell
  end tell
end makeWinVertical

★Click Here to Open This Script 

Posted in GUI Scripting Text | Tagged 10.11savvy 10.12savvy 10.13savvy CotEditor | Leave a comment

表示中のCotEditor書類と同じフォルダで各書類の1行目で選択して表示

Posted on 2月 6, 2018 by Takaaki Naganoya
AppleScript名:表示中のCotEditor書類と同じフォルダで各書類の1行目で選択して表示
— Created 2017-12-15 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use bPlus : script "BridgePlus"
use mdLib : script "Metadata Lib" version "1.0.0"
use jLib : script "japaneseTextEncodingDetector"

property NSURLIsDirectoryKey : a reference to current application’s NSURLIsDirectoryKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableArray : a reference to current application’s NSMutableArray
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants
property NSFileManager : a reference to current application’s NSFileManager
property |NSURL| : a reference to current application’s |NSURL|
property NSDirectoryEnumerationSkipsSubdirectoryDescendants : a reference to current application’s NSDirectoryEnumerationSkipsSubdirectoryDescendants
property NSArray : a reference to current application’s NSArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property SMSForder : a reference to current application’s SMSForder

load framework

tell application "CotEditor"
  set dCount to count every document
  
if dCount = 0 then return
  
  
tell front document
    set curPath to path
  end tell
  
  
tell window 1
    set aBounds to bounds
  end tell
end tell

set aPath to current application’s NSString’s stringWithString:curPath
set fileName to (aPath’s lastPathComponent()) –ファイル名
set pathExtension to aPath’s pathExtension() as string
set parentFol to (aPath’s stringByDeletingLastPathComponent()) as string —親フォルダ

–同じフォルダから同じ拡張子のファイルのファイル名を取得
set fList to my getFilesByIncludedStringInName:(pathExtension) fromDirectory:(parentFol) exceptPackages:(true)

–昇順ソート
set aArray to NSArray’s arrayWithArray:fList
set desc1 to NSSortDescriptor’s sortDescriptorWithKey:"self" ascending:true selector:"localizedCaseInsensitiveCompare:"
set bArray to aArray’s sortedArrayUsingDescriptors:{desc1}
set cList to bArray as list

–ダイアログ表示用のデータを作成
set ccList to {}
repeat with i in cList
  set j to POSIX path of (i as string)
  
set aStr to readJapanesTextFileWithGuessingEncoding(j) of jLib
  
if aStr = missing value or aStr = false then
    display dialog "Encoding Error"
    
return
  end if
  
  
set aPath to (current application’s NSString’s stringWithString:j)
  
set fileName to (aPath’s lastPathComponent()) as string –ファイル名  
  
set aFirst to first item of paragraphs of aStr
  
set the end of ccList to (fileName & " / " & aFirst)
end repeat

set aMes to "項目を選択してください"
set selRes to retItemFromListByItemNo(ccList, aMes) of me
if selRes = false then return –Cancel

set newPath to contents of item selRes of cList

tell application "CotEditor"
  set oldDoc to front document
  
  
open (POSIX file newPath) as alias
  
tell window 1
    set bounds to aBounds
  end tell
  
  
close oldDoc without saving
end tell

makeWinVertical() of me –縦書き表示

–Make CotEditor’s front window to Vertical display mode (Tategaki)
on makeWinVertical()
  activate application "CotEditor"
  
tell application "System Events"
    tell process "CotEditor"
      try
        click menu item "縦書きで表示" of menu 1 of menu bar item "フォーマット" of menu bar 1
      end try
    end tell
  end tell
end makeWinVertical

on retFileFormatUTI(aExt as string)
  if aExt begins with "." then set aExt to text 2 thru -1 of aExt
  
return (current application’s SMSForder’s UTIForExtension:aExt)
end retFileFormatUTI

on spotlightFindByLabels(aLabelList as list, thePath as string)
  set aList to makeRepeatinglList(length of aLabelList, "kMDItemFSLabel == %@")
  
set aStr to retStrFromArrayWithDelimiter(aList, " OR ") of me
  
set fRes to mdLib’s searchFolders:{thePath} searchString:aStr searchArgs:aLabelList
  
return fRes
end spotlightFindByLabels

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

–指定回数、指定アイテムを連結したリストを作成
on makeRepeatinglList(hitNum as integer, hitItem as string)
  set outList to {}
  
repeat hitNum times
    set the end of outList to hitItem
  end repeat
  
return outList
end makeRepeatinglList

–指定フォルダ内の指定文字列を含むファイル名のファイルをPOSIX pathのlistで抽出する
on getFileNamesByIncludedStringInName:(fileNameStr as string) fromDirectory:(sourceFolder) exceptPackages:(packageF as boolean)
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer) + ((NSDirectoryEnumerationSkipsSubdirectoryDescendants) as integer)
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_("lastPathComponent CONTAINS %@", fileNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat with i in foundItemList
    set j to contents of i
    
set {theResult, isDirectory} to (j’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value))
    
    
–Collect files
    
if (isDirectory as boolean = false) then
      (anArray’s addObject:j)
      
    else if (packageF = false) then
      –Allow Package files?
      
set {theResult, isPackage} to (j’s getResourceValue:(reference) forKey:(current application’s NSURLIsPackageKey) |error|:(missing value))
      
if (isPackage as boolean) = true then
        (anArray’s addObject:j)
      end if
    end if
    
  end repeat
  
  
return (anArray’s valueForKey:"lastPathComponent") as list
end getFileNamesByIncludedStringInName:fromDirectory:exceptPackages:

–指定フォルダ内の指定文字列を含むファイル名のファイルをPOSIX pathのlistで抽出する
on getFilesByIncludedStringInName:(fileNameStr as string) fromDirectory:(sourceFolder) exceptPackages:(packageF as boolean)
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer) + ((NSDirectoryEnumerationSkipsSubdirectoryDescendants) as integer)
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_("lastPathComponent CONTAINS %@", fileNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat with i in foundItemList
    set j to contents of i
    
set {theResult, isDirectory} to (j’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value))
    
    
–Collect files
    
if (isDirectory as boolean = false) then
      (anArray’s addObject:j)
      
    else if (packageF = false) then
      –Allow Package files?
      
set {theResult, isPackage} to (j’s getResourceValue:(reference) forKey:(current application’s NSURLIsPackageKey) |error|:(missing value))
      
if (isPackage as boolean) = true then
        (anArray’s addObject:j)
      end if
    end if
    
  end repeat
  
  
return (anArray’s valueForKey:"path") as list
end getFilesByIncludedStringInName:fromDirectory:exceptPackages:

–リストから選択してアイテム番号を返す
on retItemFromListByItemNo(aList, aMes)
  set aRes to choose from list aList with prompt aMes
  
if aRes = false then return 0
  
  
set aRes to contents of item 1 of aRes
  
set hitNum to 1
  
repeat with i in aList
    set j to contents of i
    
if j is equal to aRes then
      exit repeat
    end if
    
set hitNum to hitNum + 1
  end repeat
  
return hitNum
end retItemFromListByItemNo

★Click Here to Open This Script 

Posted in GUI Scripting Spotlight | Tagged 10.11savvy 10.12savvy 10.13savvy CotEditor | Leave a comment

Post navigation

  • Older posts

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

Google Search

Popular Posts

  • 私はロボットではありません
  • Xcode 12.2betaでIntel/ARM Universal Binaryのビルドを確認
  • Safariで表示中のWebページの最終更新日時を取得
  • ファイルパスの変換(Alias→POSIX path→NSURL→POSIX path→file→Alias)
  • フルパスからファイル名を取得する
  • 【基礎】AppleScriptの実行を(操作により)中断する
  • JPEG画像の破損チェック
  • ディスプレイの設定切り替えをAppleScriptでも行えるSwitchResX
  • セキュリティダイアログに表示するメッセージをローカライズする
  • Numbersで選択範囲のセルのデータを取得して重複データを抽出
  • 指定URLのページをwebarchive保存 v2
  • Keynoteの最前面のドキュメントの現在のスライドに指定月の日曜日はじまりカレンダーを表で作成
  • CotEditorのScript集、PowerPack & Basic Packを配布開始
  • Xcode 12上でのCocoa AppleScriptアプリケーションのUniversalビルド状況
  • macOS 11.0, Big Sur AppleScript関連の変更点
  • 指定フォルダ内の指定拡張子のファイルのうち、指定キーワードで始まるものを
  • macOS 10.14で新設されたエラーコード-1743を確認する
  • Safariで現在見えている表を抽出してCSV書き出しv3
  • Twitter投稿
  • 画面上の指定座標にマウスカーソルを強制移動させてクリック

Tags

10.11savvy (1109) 10.12savvy (1249) 10.13savvy (1387) 10.14savvy (512) 10.15savvy (303) 11.0savvy (112) CotEditor (48) Finder (43) iTunes (25) Keynote (61) NSAlert (59) NSAlertSecondButtonReturn (16) NSArray (50) NSBitmapImageRep (20) NSBundle (19) NSButton (33) NSColor (48) NSDictionary (26) NSFileManager (23) NSFont (18) NSImage (40) NSJSONSerialization (21) NSMutableArray (61) NSMutableDictionary (21) NSPredicate (36) NSRunningApplication (55) NSScreen (30) NSScrollView (22) NSString (114) NSURL (93) NSURLRequest (22) NSUTF8StringEncoding (30) NSUUID (18) NSView (33) NSWindow (17) NSWorkspace (19) Numbers (42) Pages (16) Safari (34) System Events (16) WKUserContentController (20) WKUserScript (19) WKUserScriptInjectionTimeAtDocumentEnd (17) WKWebView (21) WKWebViewConfiguration (21)

カテゴリー

  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Bluetooth
  • boolean
  • Bug
  • Calendar
  • call by reference
  • Clipboard
  • Code Sign
  • Color
  • Custom Class
  • dialog
  • drive
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • History
  • How To
  • Icon
  • Image
  • Input Method
  • Internet
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Language
  • list
  • Locale
  • Machine Learning
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • OCR
  • OSA
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • shell script
  • 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)
  • 未分類

アーカイブ

  • 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
Proudly powered by WordPress
Theme: Flint by Star Verte LLC