アラートダイアログ上に指定月のカレンダー1か月分を表示してユーザーの日付選択(複数可)をもとめるAppleScriptです。
NSDatePickerを用いて日付選択のダイアログを作っていたら、日付がとても小さくて現代のMacの画面解像度にとても合っておらず、もっと大きめのカレンダーで選択したいと考えて作ったものです。
(1)カレンダーの表示サイズを大型化
現代のMacの画面にNSDatePickerをそのまま表示すると、とても小さくて見ていられないので、表示サイズを自由に変えられるカレンダー選択部品を作ってみました。どれか1つの日付を選択するものではなく、複数の日付を選択して、リストで結果を返してきます。
▲画面上で見やすいようにカレンダー部品を大型化
(2)選択無効日指定
あらかじめ、日付選択の対象外とする日付を指定できるようにしました。
(3)過去日付の選択無効
過去の日付を表示・選択できないようにする指定です。
▲過去日付を無効に指定すると、現在日時よりも過去の日付は選択できない
(4)曜日を現在のLocale情報から変更
曜日に用いる文字列を現在のLocale情報から取得するようにしてみました。本来であれば、日曜日はじまり「ではない」カレンダーを用いている場所もあるので(香港とかフランスとか?)、どの曜日から表示開始するかを自由選択にしたいところですが、そこまでは作り込んでいません。
▲Localeに合わせた曜日が表示される(ただし、年/月は固定フォーマット)
実用性を考えれば、Calendar.appのように日付選択とスケジュール確認が行えるものが望ましいのですが、そこまで作り込む手間をかけるほどのものでもないですし、表示対象月の移動を行おうとしてclickイベントを拾いつつカレンダー再描画に失敗した「残骸」です。見た目はいいのに完成度がいまひとつというあたりが残念です。
AppleScript名:アラートダイアログ上にカレンダー1か月分を表示して選択 有効日指定あり v3a |
— Created 2019-10-15 by Takaaki Naganoya — 2019 Piyomaru Software use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" property NSFont : a reference to current application’s NSFont property NSView : a reference to current application’s NSView property NSAlert : a reference to current application’s NSAlert property NSColor : a reference to current application’s NSColor property NSButton : a reference to current application’s NSButton property NSOnState : a reference to current application’s NSOnState property NSOffState : a reference to current application’s NSOffState property NSTextField : a reference to current application’s NSTextField property NSMutableArray : a reference to current application’s NSMutableArray property NSButtonTypeOnOff : a reference to current application’s NSButtonTypeOnOff property NSMutableDictionary : a reference to current application’s NSMutableDictionary property NSRunningApplication : a reference to current application’s NSRunningApplication property NSFontAttributeName : a reference to current application’s NSFontAttributeName property NSKernAttributeName : a reference to current application’s NSKernAttributeName property NSMutableParagraphStyle : a reference to current application’s NSMutableParagraphStyle property NSLigatureAttributeName : a reference to current application’s NSLigatureAttributeName property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString property NSUnderlineStyleAttributeName : a reference to current application’s NSUnderlineStyleAttributeName property NSParagraphStyleAttributeName : a reference to current application’s NSParagraphStyleAttributeName property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName property theResult : 0 property returnCode : 0 property bArray : {} –Checkbox button object array property tYear : 0 property tMonth : 0 property theAlert : missing value property theView : missing value on run set tYear to 2019 set tMonth to 11 set ignoreP to true set ndList to {2, 3, 4, 9, 10, 13, 16, 17, 23, 24, 25, 30} –選択表示させない日付 set paramObj to {myMessage:"", mySubMessage:"適切な日付を以下からえらんでください", targetYear:tYear, targetMonth:tMonth, nodisp:ndList, ignorePast:ignoreP} –Detect Past Date Error set curDate to current date set dLimit to getMlen(tYear, tMonth) of me set tmpDate to getDateInternational(tYear, tMonth, dLimit) of me if (tmpDate < curDate) and (ignoreP = true) then error (("Target month is past (" & tYear as string) & "/" & tMonth as string) & "/1 is past. Today is " & date string of curDate & ")" –my chooseItemByCheckBox:paramObj –for Debugging my performSelectorOnMainThread:"chooseItemByCheckBox:" withObject:(paramObj) waitUntilDone:true return my sort1DNumList:(my theResult) ascOrder:true –> {1, 3, 5, 7, 9, 11} end run on chooseItemByCheckBox:(paramObj) set aMainMes to (myMessage of paramObj) as string set aSubMes to (mySubMessage of paramObj) as string set aMatList to (matrixTitleList of paramObj) as list set tYear to (targetYear of paramObj) as integer set tMonth to (targetMonth of paramObj) as integer set noDispList to (nodisp of paramObj) as list set ignoreP to (ignorePast of paramObj) as boolean if aMainMes = "" then set aMainMes to "日付を選択してください" end if set theView to makeCalendarView(tYear, tMonth, noDispList, ignoreP) of me –Select the first radio button item set my theResult to {} — 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 –for Help Button –its setShowsHelp:(true) its setDelegate:(me) set aWin to its |window|() end tell aWin’s setLevel:(current application’s NSFloatingWindowLevel) — 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 chooseItemByCheckBox: on doModal:aParam set (my returnCode) to aParam’s runModal() end doModal: on clicked:aParam set aTag to (tag of aParam) as integer –clicked if aTag is not in (my theResult) then set the end of (my theResult) to aTag else set theResult to my deleteItem:aTag fromList:theResult end if end clicked: on deleteItem:anItem fromList:theList set theArray to NSMutableArray’s arrayWithArray:theList theArray’s removeObject:anItem return theArray as list end deleteItem:fromList: –1D List(数値)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート on sort1DNumList:theList ascOrder:aBool tell current application’s NSSet to set theSet to setWithArray_(theList) tell current application’s NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_(missing value, true) set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor} return (sortedList) as list end sort1DNumList:ascOrder: –Help Button Clicked Event Handler on alertShowHelp:aNotification set aRes to display dialog "Do you change all checkbox state?" buttons {"All Off", "All On", "Cancel"} default button 3 with icon 1 set bRes to (button returned of aRes) as string if bRes = "All Off" then set bLen to bArray’s |count|() set theResult to {} repeat with i from 0 to bLen ((bArray’s objectAtIndex:i)’s setState:(current application’s NSOffState)) end repeat else if bRes = "All On" then set bLen to bArray’s |count|() set theResult to {} repeat with i from 0 to bLen ((bArray’s objectAtIndex:i)’s setState:(current application’s NSOnState)) set the end of theResult to i + 1 end repeat end if return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする end alertShowHelp: –書式つきテキストを組み立てる on makeRTFfromParameters(aStr as string, fontName as string, aFontSize as real, aKerning as real, aLineSpacing as real) set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize set aKey1 to (NSFontAttributeName) set aVal2 to NSColor’s blackColor() set aKey2 to (NSForegroundColorAttributeName) set aVal3 to aKerning set akey3 to (NSKernAttributeName) set aVal4 to 0 set akey4 to (NSUnderlineStyleAttributeName) set aVal5 to 2 –all ligature ON set akey5 to (NSLigatureAttributeName) set aParagraphStyle to NSMutableParagraphStyle’s alloc()’s init() aParagraphStyle’s setMinimumLineHeight:(aLineSpacing) aParagraphStyle’s setMaximumLineHeight:(aLineSpacing) set akey7 to (NSParagraphStyleAttributeName) set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey7} set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aParagraphStyle} set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary return attrStr end makeRTFfromParameters on makeNSTextField(xPos as integer, yPos as integer, myWidth as integer, myHeight as integer, editableF as boolean, setVal as string, backgroundF as boolean, borderedF as boolean) set aNSString to NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(xPos, yPos, myWidth, myHeight)) aNSString’s setEditable:(editableF) aNSString’s setStringValue:(setVal) aNSString’s setDrawsBackground:(backgroundF) aNSString’s setBordered:(borderedF) return aNSString end makeNSTextField –指定月のカレンダーを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 –ローカライズされた曜日名称を返す on getLocalizedDaynames(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set dayNames to df’s standaloneWeekdaySymbols() as list return dayNames end getLocalizedDaynames –ローカライズされた月名称を返す on getLocalizedMonthnames(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set monthNames to df’s standaloneMonthSymbols() as list return monthNames end getLocalizedMonthnames –ローカライズされた曜日の名称を返す on getLocalizedWeekdaySymbol(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set dayNames to df’s weekdaySymbols() return dayNames as list end getLocalizedWeekdaySymbol –ローカライズされた曜日の名称(短縮版)を返す on getLocalizedShortWeekdaySymbol(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set dayNames to df’s shortWeekdaySymbols() return dayNames as list end getLocalizedShortWeekdaySymbol –ローカライズされた曜日の名称(短縮記号)を返す on getLocalizedVeryShortWeekdaySymbol(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set dayNames to df’s veryShortWeekdaySymbols() return dayNames as list end getLocalizedVeryShortWeekdaySymbol –ローカライズされた曜日の名称を返す on getLocalizedVeryStandaloneWeekdaySymbols(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set dayNames to df’s standaloneWeekdaySymbols() return dayNames as list end getLocalizedVeryStandaloneWeekdaySymbols –ローカライズされた曜日の名称を返す on getLocalizedShortStandaloneWeekdaySymbols(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set dayNames to df’s shortStandaloneWeekdaySymbols() return dayNames as list end getLocalizedShortStandaloneWeekdaySymbols –ローカライズされた曜日の名称を返す on getLocalizedVeryShortStandaloneWeekdaySymbols(aLoc) set df to current application’s NSDateFormatter’s alloc()’s init() df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc) set dayNames to df’s veryShortStandaloneWeekdaySymbols() return dayNames as list end getLocalizedVeryShortStandaloneWeekdaySymbols –カレンダー表示ビューを作成 on makeCalendarView(tYear as integer, tMonth as integer, noDispList as list, ignoreP as boolean) set colNum to 7 set rowNum to 8 set aLen to (colNum * rowNum) set aButtonCellWidth to 70 –56 set aButtonCellHeight to 40 set viewWidth to aButtonCellWidth * colNum set viewHeight to aButtonCellHeight * rowNum –define the matrix size where you’ll put the radio buttons set matrixRect to current application’s NSMakeRect(0.0, 0.0, viewWidth, viewHeight) set aView to NSView’s alloc()’s initWithFrame:(matrixRect) set aCount to 1 set aFontSize to 30 set bArray to current application’s NSMutableArray’s new() –Make Header (Month, Year) set x to 1 set tmpB to (NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum) * (aButtonCellHeight), aButtonCellWidth * 4, aButtonCellHeight))) (tmpB’s setEditable:false) (tmpB’s setBordered:false) (tmpB’s setDrawsBackground:false) (tmpB’s setAlignment:(current application’s NSLeftTextAlignment)) (tmpB’s setStringValue:((tYear as string) & " / " & tMonth as string)) (tmpB’s setFont:(NSFont’s fontWithName:"Times-Roman" |size|:40)) (bArray’s addObject:tmpB) set aCount to aCount + 1 –Make Header (Weekday) set curLocale to current application’s NSLocale’s currentLocale() set aDS10 to (curLocale’s objectForKey:(current application’s NSLocaleIdentifier)) as string set wdList to getLocalizedShortStandaloneWeekdaySymbols(aDS10) of me set y to 2 repeat with x from 1 to 7 set j to contents of item x of wdList set tmpB to (NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum – 1) * (aButtonCellHeight), aButtonCellWidth, aButtonCellHeight / 2))) (tmpB’s setEditable:false) (tmpB’s setAlignment:(current application’s NSCenterTextAlignment)) (tmpB’s setStringValue:(j)) (bArray’s addObject:tmpB) end repeat set aCalList to retListCalendar(tYear, tMonth) of me set aCount to 1 –Make Calendar (Calendar Body) set curDat to current date repeat with y from 3 to rowNum repeat with x from 1 to colNum set j to contents of item aCount of aCalList set tmpB to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum – 2) * aButtonCellHeight, aButtonCellWidth, aButtonCellHeight))) set tmpDate to getDateInternational(tYear, tMonth, j as integer) of me set tmpF to (tmpDate < curDat) as boolean if ignoreP = false then set dispF to true else if ignoreP = true and tmpF = false then set dispF to true else set dispF to false end if if (j as integer) is not in noDispList then if (dispF = true) then (tmpB’s setTitle:(j as string)) (tmpB’s setFont:(NSFont’s fontWithName:"ArialMT" |size|:aFontSize)) –set attrTitle to makeRTFfromParameters((aCount as string), "ArialMT", aFontSize, 0, (aFontSize * 1.2)) of me –(tmpB’s setAttributedTitle:(attrTitle)) (tmpB’s setShowsBorderOnlyWhileMouseInside:true) (tmpB’s setAlignment:(current application’s NSCenterTextAlignment)) (tmpB’s setEnabled:(j ≠ "")) (tmpB’s setTarget:me) (tmpB’s setAction:("clicked:")) (tmpB’s setButtonType:(NSButtonTypeOnOff)) (tmpB’s setHidden:(j = "")) if j is not equal to "" then (tmpB’s setTag:(j)) (bArray’s addObject:tmpB) end if end if end if set aCount to aCount + 1 end repeat end repeat (aView’s setSubviews:bArray) return aView end makeCalendarView |
More from my site
(Visited 173 times, 1 visits today)