「「クリップボード履歴」メニューのマクロ化」の版間の差分
ナビゲーションに移動
検索に移動
細 →更新履歴 |
第3版 |
||
| 7行目: | 7行目: | ||
:「スニペット」機能は [[プラグイン:スニペットプラグイン|スニペットプラグイン]] の設定ファイル '''snippets.txt''' を読み書きするというかたちにしています。 | :「スニペット」機能は [[プラグイン:スニペットプラグイン|スニペットプラグイン]] の設定ファイル '''snippets.txt''' を読み書きするというかたちにしています。 | ||
<br> | <br> | ||
<div class="warningbox"> | |||
* <b style="color:#c00;">このマクロは Mery 2.8.1 以降でしか利用できません。</b> | |||
* あらかじめ「[[includeライブラリ]]」の導入が必要です。 | * あらかじめ「[[includeライブラリ]]」の導入が必要です。 | ||
</div> | |||
* 外部実行ファイル「[[GetKeyState.exe(キー状態取得実行ファイル)|GetKeyState.exe]] 」で機能を拡張できます。 | |||
* 「[[プラグイン:スニペットプラグイン|スニペットプラグイン]]」を導入していないでも「スニペット(定型文)」機能を利用できます。 | * 「[[プラグイン:スニペットプラグイン|スニペットプラグイン]]」を導入していないでも「スニペット(定型文)」機能を利用できます。 | ||
* 動作確認は Windows XP sp3 (32bit) × Mery ベータ版 2.8.2 - 2.8.3 (ポータブル) でしかしていません。 | * 動作確認は Windows XP sp3 (32bit) × Mery ベータ版 2.8.2 - 2.8.3 (ポータブル) でしかしていません。 | ||
<br> | <br> | ||
| 27行目: | 29行目: | ||
・snippets.txt 内の空行を無視。 | ・snippets.txt 内の空行を無視。 | ||
・クリップボード履歴に長大なデータがあるときの動作速度を少しだけ改善。 | ・クリップボード履歴に長大なデータがあるときの動作速度を少しだけ改善。 | ||
• 2019/11/01: | |||
・ステータス表示を追加 | |||
・クリップボードにテキストデータがないときのメニュー構成を変更 | |||
・アイテムの行数(改行数+1)をメニュー内に追加表示 | |||
・メニュー内に表示する改行コードの矢印を「↲」(U+21B2) に変更 | |||
・スニペット機能での「ピン止め/貼り付け」のさいの、改行コードやタブコードではない文字列「\n」や「\t」の扱いを再修正 | |||
・snippets.txt がないときのエラーを修正 | |||
== ソースコード == | == ソースコード == | ||
; ダウンロード: 「[[ファイル:クリップボード履歴.zip ]]」(アイコン入り) | ; ダウンロード: 「[[ファイル:クリップボード履歴.zip ]]」(アイコン入り) | ||
<source lang="javascript" style="height: | <source lang="javascript" style="height:60em; overflow:auto;"> | ||
#title = "クリップボード履歴..." | #title = "クリップボード履歴..." | ||
#tooltip = "クリップボード履歴 と スニペット" | #tooltip = "クリップボード履歴 と スニペット" | ||
#icon = "clipboard_history[1].ico" | |||
#include "include/IO.js" | #include "include/IO.js" | ||
/** | /** | ||
* -------------------------------------------------- | * -------------------------------------------------- | ||
* 「クリップボード履歴 と スニペット」マクロ | * 「クリップボード履歴 と スニペット」マクロ | ||
* sukemaru, 2019/08/01 - 2019/ | * sukemaru, 2019/08/01 - 2019/11/01 | ||
* -------------------------------------------------- | * -------------------------------------------------- | ||
* 「クリップボード履歴」メニューと「スニペット」プラグインと同等の機能を | * 「クリップボード履歴」メニューと「スニペット」プラグインと同等の機能を | ||
| 46行目: | 57行目: | ||
* | * | ||
* 【このマクロの仕様・制限事項】 | * 【このマクロの仕様・制限事項】 | ||
* ・ver 2.8. | * ・ver 2.8.1 以前の Mery では使用できません。 | ||
* ・「クリップボード履歴」は Mery 本体のメモリストアのものを使用します。 | * ・「クリップボード履歴」は Mery 本体のメモリストアのものを使用します。 | ||
* ※「クリップボードのすべての履歴を削除」したあとでも、 | |||
* Windows OS のクリップボードの最新の履歴にテキストデータが残っているときは | |||
* クリップボード履歴メニューに1件だけ表示します。 | |||
* | |||
* ・「スニペット」プラグインの設定ファイル snippets.txt の読み書きをします。 | * ・「スニペット」プラグインの設定ファイル snippets.txt の読み書きをします。 | ||
* | * ※「include ライブラリ」が必要です(snippets.txt ファイルの読み書きに使用)。 | ||
* ※「スニペット」プラグインを導入していない場合でも、 | * ※「スニペット」プラグインを導入していない場合でも、 | ||
* 「ピン止めアイテム/スニペット」機能を使用できます。 | * 「ピン止めアイテム/スニペット」機能を使用できます。 | ||
| 59行目: | 70行目: | ||
* (インストーラ版では %AppData%\Mery\Plugins\Snippets フォルダ)。 | * (インストーラ版では %AppData%\Mery\Plugins\Snippets フォルダ)。 | ||
* | * | ||
* ・「GetKeyState.exe(キー状態取得実行ファイル) 」で機能を拡張できます。 | |||
* → クリップボード履歴のアイテムを Ctrl+クリックした場合、 | |||
* アイテムを貼り付けし、貼り終えたアイテムを削除します。 | |||
* ※「GetKeyState.exe」は Mery\Macros フォルダに配置してください | * ※「GetKeyState.exe」は Mery\Macros フォルダに配置してください | ||
* (Macros フォルダ以外の場所にあるなら設定項目でパスを指定する)。 | * (Macros フォルダ以外の場所にあるなら設定項目でパスを指定する)。 | ||
*/ | */ | ||
| 78行目: | 83行目: | ||
// ■ ピン止めアイテムをサブメニュー化するアイテム数の閾値 | // ■ ピン止めアイテムをサブメニュー化するアイテム数の閾値 | ||
var snSubMenu = 5; // | var snSubMenu = 5; // これよりアイテム数が多いときはサブメニューに表示する | ||
// ■ ポップアップメニューを表示する位置 | // ■ ポップアップメニューを表示する位置 | ||
| 87行目: | 92行目: | ||
// ■ GetKeyState.exe のフルパスを指定する場合(「\」記号はふたつがさね「\\」で) | // ■ GetKeyState.exe のフルパスを指定する場合(「\」記号はふたつがさね「\\」で) | ||
// ※ 未指定 "" なら、 editor.FullName.replace( /[^\\]+$/, "Macros\\GetKeyState.exe" ); | |||
var getKeyStatePath = ""; | |||
// ---------- ▲ 設定項目 ▲ ---------- // | // ---------- ▲ 設定項目 ▲ ---------- // | ||
var start = new Date(); | |||
var Fso = new ActiveXObject( "Scripting.FileSystemObject" ); | var Fso = new ActiveXObject( "Scripting.FileSystemObject" ); | ||
var WshShell = new ActiveXObject( "WScript.Shell" ); | var WshShell = new ActiveXObject( "WScript.Shell" ); | ||
// | // ClipboardData.Get/ClearData() に引数を指定できるのは ver 2.8.1 以降 | ||
if ( | if ( VersionCheck( "2.8.1" ) ) { | ||
var meryPath = editor.FullName; | |||
var meryPath = editor.FullName; | var meryDir = meryPath.replace( /[^\\]+$/, "" ); | ||
var | var isPortable = Fso.FileExists( meryPath.replace( /\.exe$/i, ".ini" ) ); | ||
var | var profileDir = ( isPortable ) | ||
var profileDir = | ? meryDir | ||
: WshShell.SpecialFolders( "APPDATA" ) + "\\Mery\\"; | |||
var snPath = profileDir + "Plugins\\Snippets\\Snippets.txt"; | |||
var snIsExist = Fso.FileExists( snPath ); | |||
var gksPath = getKeyStatePath || meryDir + "Macros\\GetKeyState.exe"; | |||
var | var gksIsExist = Fso.FileExists( gksPath ); | ||
var snIsExist = Fso.FileExists( | var $ctrl = 0; | ||
var | |||
var gksIsExist = Fso.FileExists( | |||
var $ctrl = 0; | |||
var d = editor.ActiveDocument; | var d = editor.ActiveDocument; | ||
var s = d.selection; | var s = d.selection; | ||
var st = s.Text; | var st = s.Text; | ||
var $status = Status; | |||
// ピン止めアイテム (スニペット) | // ピン止めアイテム (スニペット) | ||
var snippets = "", snCount = 0; | var snippets = "", snArray = [], snCount = 0 , sn; | ||
if ( snIsExist ) { | if ( snIsExist ) { | ||
snippets = IO.LoadFromFile( | snippets = IO.LoadFromFile( snPath, "utf-8" ) || ""; | ||
snArray = snippets.replace( /^\n/gm, "" ).replace( /\n$/, "" ) | |||
.split( "\n" ); | |||
snCount = snArray.length; | snCount = snArray.length; | ||
sn = ( snCount > snSubMenu ); | |||
} | } | ||
// クリップボード履歴 | // クリップボード履歴 | ||
var cb = ClipboardData; | var cb = ClipboardData; | ||
var cbArray = []; | var cbData = cb.GetData(); | ||
for ( var i = 0; ; i ++ ) { | var cbArray = []; | ||
var cbData0 = cb.GetData( 0 ); | |||
if ( cbData0 ) { | |||
for ( var i = 0, cbItem; ; i ++ ) { | |||
cbItem = cb.GetData( i ) || ""; | |||
if ( ! cbItem ) { break; } | |||
// 重複するアイテムを履歴から削除する | |||
if ( delDupEnable ) { | |||
for ( var j = i + 1, cbNextItem; ; j ++ ) { | |||
cbNextItem = cb.GetData( j ) || ""; | |||
if ( ! cbNextItem ) { break; } | |||
if ( cbItem == cbNextItem ) { | |||
cb.ClearData( j -- ); | |||
} | |||
} | } | ||
} | } | ||
// 履歴アイテムを配列に収納する | |||
cbArray.push( cbItem ); | |||
} | } | ||
} | } | ||
var cbCount = cbArray.length; | var cbCount = cbArray.length; | ||
var width = String( Math.max( snCount, cbCount ) ).length; | var width = String( Math.max( snCount, cbCount || 0 ) ).length; | ||
// ポップアップメニューの準備 | // ポップアップメニューの準備 | ||
var menu = CreatePopupMenu(); | var menu = CreatePopupMenu(); | ||
var menuFlags = d.ReadOnly ? meMenuGrayed : 0; | var menuFlags = d.ReadOnly ? meMenuGrayed : 0; | ||
var t = ( | var t = ( snSubMenu && snArray[0] ) ? "▼ " : ""; | ||
menu.AddPopup( t + "ピン止めアイテム/スニペット (& | menu.AddPopup( t + "ピン止めアイテム/スニペット (&S) " + t, sm0 = CreatePopupMenu() ); | ||
// スニペットのアイテムをメニューに | // スニペットのアイテムをメニューに | ||
if ( snCount ) { | if ( snCount ) { | ||
for ( var i = 0, id, label, lines; i < snCount; i ++ ) { | |||
for ( var i = 0, id, label; i < snCount; i ++ ) { | |||
id = i + 1; | id = i + 1; | ||
label = snArray[i].replace( /\t[\s\S]*/, "" ); | lines = snArray[i].replace( /[^\t]*\t/, "" ).split( "\\n" ).length; | ||
label = MenuKey( snArray[i].replace( /\t[\s\S]*/, "" ), id, width, true ) | |||
+ "\t" + lines + " 行"; | |||
if ( label ) { | if ( label ) { | ||
if ( i < snSubMenu ) { | |||
menu.Add( label, id + 400, menuFlags ); | |||
if ( sn ) { | |||
sm0.Add( label, id + 400, menuFlags ); | |||
} | |||
} | |||
else { | |||
sm0.Add( label, id + 400, menuFlags ); | |||
} | |||
} | } | ||
} | } | ||
if ( sn ) { | if ( sn ) { | ||
sm0.Add( "", 0, meMenuSeparator ); | |||
} | } | ||
} | } | ||
if ( st ) { | if ( st ) { | ||
sm0.Add( "選択範囲を登録 (&P)", 300 ); | sm0.Add( "選択範囲を登録 (&P)", 300 ); | ||
| 183行目: | 197行目: | ||
sm0.Add( "スニペットを編集 (&E) ...", 400 ); | sm0.Add( "スニペットを編集 (&E) ...", 400 ); | ||
} | } | ||
sm0.Add( "", 0, meMenuSeparator ); | if ( sn || st || snIsExist ) { | ||
sm0.Add( "", 0, meMenuSeparator ); | |||
sm0.Add( "キャンセル & ", 0 ); | |||
} | |||
menu.Add( "", 0, meMenuSeparator ); | |||
// クリップボード履歴のアイテムをメニューに | // クリップボード履歴のアイテムをメニューに | ||
if ( cbCount ) { | if ( cbCount ) { | ||
menu. | menu.AddPopup( "▼ クリップボード履歴の一覧 (&C) ▼", sm1 = CreatePopupMenu() ); | ||
var sm2 = CreatePopupMenu(); | |||
for ( var i = 0, id, label; i < cbCount; i ++ ) { | var sm3 = CreatePopupMenu(); | ||
for ( var i = 0, id, label, lines; i < cbCount; i ++ ) { | |||
id = i + 1; | id = i + 1; | ||
label = MenuKey( cbArray[i], id, width ); | lines = cbArray[i].split( "\n" ).length; | ||
label = MenuKey( cbArray[i], id, width ) | |||
+ "\t" + lines + " 行"; | |||
menu.Add( label, id, menuFlags ); | menu.Add( label, id, menuFlags ); | ||
sm2. Add( label, id + 100, menuFlags ); | |||
sm3. Add( label, id + 200 ); | |||
} | } | ||
menu.Add( "", 0, meMenuSeparator ); | menu.Add( "", 0, meMenuSeparator ); | ||
menu.Add( "クリップボードのすべての履歴を削除する (&E)", 500 ); | |||
sm1.AddPopup( "貼り付けしてからアイテムを削除 (&M)", sm2 ); | |||
sm1.AddPopup( "履歴からアイテムを削除 (&D)", sm3 ); | |||
sm1.Add( "", 0, meMenuSeparator ); | |||
sm1.Add( "すべての履歴を削除 (&E)", 500 ); | |||
} | |||
else if ( cbData.length ) { | |||
menu.Add( "▼ クリップボード ▼", 0, meMenuGrayed ); | |||
lines = cbData.split( "\n" ).length; | |||
label = MenuKey( cbData, 1, width ) | |||
+ "\t" + lines + " 行"; | |||
menu.Add( label, 17, menuFlags ); | |||
} | |||
else { | |||
menu.Add( "※ クリップボードにテキストデータはありません ※" | |||
, 0, meMenuGrayed ); | |||
} | } | ||
menu.Add( "", 0, meMenuSeparator ); | menu.Add( "", 0, meMenuSeparator ); | ||
menu.Add( "キャンセル & ", 0 ); | menu.Add( "キャンセル & ", 0 ); | ||
// ステータスバーの表示 | |||
if ( menuFlags ) { | |||
Status = " ドキュメントは書き換え禁止です。"; | |||
} | |||
else if ( gksIsExist && cbCount ) { | |||
Status = "履歴アイテムの Ctrl+クリック で 「貼り付けしてからアイテムを削除」"; | |||
} | |||
else { | |||
Status = "「クリップボード履歴 と スニペット」マクロ"; | |||
} | |||
Status += " [ " | |||
+ ( ( new Date() - start ) / 1000 ).toFixed( 3 ).replace( /\./, ". " ) | |||
+ " 秒 ]"; | |||
// ポップアップメニューを表示 | // ポップアップメニューを表示 | ||
var r = menu.Track( + menuPosMouse ); | var r = menu.Track( + menuPosMouse ); | ||
var confirmStr = | var confirmStr = "クリップボードのすべての履歴を削除しますか? "; | ||
if ( r == 0 ) { | if ( r == 0 ) { | ||
; | Status = $status; | ||
} | } | ||
// 1 ~ | // 1 ~ 17: 貼り付け | ||
else if ( r < | else if ( r <= 16 ) { | ||
d.Write( cbArray[ r -1 ] ); | d.Write( cbArray[ r -1 ] ); | ||
Status = ""; | |||
// Ctrl キーを押しながらのときは、貼り付けたアイテムを履歴から削除する | // Ctrl キーを押しながらのときは、貼り付けたアイテムを履歴から削除する | ||
if ( gksIsExist ) { | if ( gksIsExist ) { | ||
$ctrl = WshShell.Run( "\"" + | $ctrl = WshShell.Run( "\"" + gksPath + "\" c", 0, true ); | ||
if ( $ctrl == 1 ) { | |||
cb.ClearData( r -1 ); | |||
Status = " 履歴からアイテムを削除しました。"; | |||
} | |||
} | } | ||
} | |||
else if ( r == 17 ) { | |||
d.Write( cbData ); | |||
Status = ""; | |||
} | } | ||
| 235行目: | 285行目: | ||
d.Write( cbArray[ r -101 ] ); | d.Write( cbArray[ r -101 ] ); | ||
cb.ClearData( r -101 ); | cb.ClearData( r -101 ); | ||
Status = " 履歴からアイテムを削除しました。"; | |||
} | } | ||
| 240行目: | 291行目: | ||
else if ( r > 200 && r < 300 ) { | else if ( r > 200 && r < 300 ) { | ||
cb.ClearData( r -201 ); | cb.ClearData( r -201 ); | ||
Status = " 履歴からアイテムを削除しました。"; | |||
} | } | ||
// | |||
else if ( r > | // 401 ~ 499: ピン止めアイテムを貼り付け | ||
var str = snArray[ r - | else if ( r > 400 && r < 500 ) { | ||
.replace( /(\\r) | var str = snArray[ r -401 ].replace( /^[^\t]*\t/, "" ) | ||
.replace( /\\ | .replace( /(^|[^\\])\\t/g, "$1\t" ) | ||
.replace( /(^|[^\\])\\r/g, "$1\r" ) | |||
.replace( /(^|[^\\])\\n/g, "$1\n" ) | |||
.replace( /\\\\/g, "\\" ); | |||
d.Write( str ); | d.Write( str ); | ||
Status = ""; | |||
} | } | ||
// 300: 選択範囲を登録(ピン止め) | // 300: 選択範囲を登録(ピン止め) | ||
else if ( r == 300 ) { | else if ( r == 300 ) { | ||
var snippetsDir = Fso.GetParentFolderName( | var snippetsDir = Fso.GetParentFolderName( snPath ); | ||
var pluginsDir = Fso.GetParentFolderName( snippetsDir ); | var pluginsDir = Fso.GetParentFolderName( snippetsDir ); | ||
if ( ! Fso.FolderExists( pluginsDir ) ) { | if ( ! Fso.FolderExists( pluginsDir ) ) { | ||
| 262行目: | 318行目: | ||
var str = snippets | var str = snippets | ||
+ ( snippets ? "\n" : "" ) | + ( snippets ? "\n" : "" ) | ||
+ st.replace( /\t/g, "\\t" ) | + st.replace( /\\/g, "\\\\" ) | ||
.replace( /\t/g, "\\t" ) | |||
.replace( /\r?\n/g, "\\n" ); | .replace( /\r?\n/g, "\\n" ); | ||
IO.SaveToFile( | IO.SaveToFile( snPath, str, "utf-8", true ); | ||
Status = " 選択範囲をスニペットに登録しました。"; | |||
} | } | ||
// 400: ピン止めアイテムを編集(snippets.txt を開く) | // 400: ピン止めアイテムを編集(snippets.txt を開く) | ||
else if ( r == 400 ) { | else if ( r == 400 ) { | ||
WshShell.Run( "\"" + editor.FullName + "\" \"" + | WshShell.Run( "\"" + editor.FullName + "\" \"" + snPath + "\"" ); | ||
Status = " " + snPath; | |||
} | } | ||
| 276行目: | 335行目: | ||
for ( var i = 0; i < cbCount; i ++ ) { | for ( var i = 0; i < cbCount; i ++ ) { | ||
cb.ClearData( i ); | cb.ClearData( i ); | ||
Status = " 履歴からすべてのアイテムを削除しました。"; | |||
} | } | ||
} | } | ||
| 295行目: | 355行目: | ||
} ) | } ) | ||
}; | }; | ||
var editorVer = + ( Pad2( editor.Version ).replace( /\./g, "" ).slice( 0, 6 ) ); | var editorVer = + ( Pad2( editor.Version ).replace( /\./g, "" ) | ||
var requirement = + ( Pad2( versionStr ).replace( /\./g, "" ).slice( 0, 6 ) ); | .slice( 0, 6 ) ); | ||
var requirement = + ( Pad2( versionStr ).replace( /\./g, "" ) | |||
.slice( 0, 6 ) ); | |||
return ( editorVer >= requirement ); | return ( editorVer >= requirement ); | ||
} | } | ||
/** | /** | ||
* 関数 MenuKey( str, num, width ) | * 関数 MenuKey( str, num, width, convLF ) | ||
* ポップアップメニューに表示するラベルを生成する | * ポップアップメニューに表示するラベルを生成する | ||
* ・行頭空白を除去、空白文字を圧縮 | * ・行頭空白を除去、空白文字を圧縮: →「›」(U+203A) | ||
* ・改行記号を可視化: →「↲」(U+21B2) または「⏎」(U+23CE) | |||
* ・削られてしまう「&」を補完 | * ・削られてしまう「&」を補完 | ||
* ・「¥」(U+005C) を「∖」に置換: | * ・「¥」(U+005C) を「∖」に置換: →「∖」(U+2216) | ||
* ・判別しづらいメタ文字を全角に置換: !"%'(),.:;@[]`{|} | * ・ゼロ幅や特殊な空白文字を豆腐に置換: →「⊠」(U+22A0) または「▯」(U+25AF) | ||
* ・判別しづらいメタ文字を全角に置換: !"%'(),.:;@[]`{|} | |||
* ・「a-z」を全角に置換 | * ・「a-z」を全角に置換 | ||
* ・行番号を空白でケタ埋め: EN SPACE「 」(U+2002) | * ・行番号を空白でケタ埋め: EN SPACE「 」(U+2002) | ||
* ・文字数を切り詰め | * ・文字数を切り詰め | ||
*/ | */ | ||
function MenuKey( str, num, width ) { | function MenuKey( str, num, width, convLF ) { | ||
// 2019/08/05 : slice メソッドを追加 | |||
if ( convLF ) { | |||
str = str.replace( /(?:\\r)?\\n/g, " ↲ " ); // ⏎ | |||
} | |||
var reg = /[\u00A0\u1680\u180e\u2000-\u200A\u2028\u2029\u202F\u205F\uFEFF]/g; | |||
var menuKey = str.replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " ) | |||
.slice( 0, menuWidth + 1 ) | |||
.replace( /\r?\n/g, " ↲ " ) // ⏎ | |||
.replace( /[&]/g, "&&" ) | .replace( /[&]/g, "&&" ) | ||
.replace( /[\\]/g, "∖" ) | .replace( /[\\]/g, "∖" ) | ||
.replace( /([^∖!"',.:;`|\t ])([∖]+)/g, "$1 $2" ) | // .replace( /([^∖!"',.:;`|\t ])([∖]+)/g, "$1 $2" ) | ||
.replace( reg, "▯" ) | |||
.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g, | .replace( /[!"%'(),.:;@\[\]`a-z{|}]/g, | ||
function( tmp ) { | function( tmp ) { | ||
2019年11月1日 (金) 21:49時点における版
公式フォーラム[1] にて「クリップボード履歴」メニューの将来的に廃止されることが 予告?検討? されていますが、後継機能としては 「クリップボード履歴」プラグイン の登場により今後も安泰のようですね。
ということで、試験運用中だった自家用マクロを再度リサイクルして「クリップボード履歴とスニペット」の統合版ポップアップメニューにしてみました。
- 「クリップボード履歴」機能は Mery 2.8.1 以降の『延命措置』の新パラメータ[2] を利用したもの。
- 「スニペット」機能は スニペットプラグイン の設定ファイル snippets.txt を読み書きするというかたちにしています。
- このマクロは Mery 2.8.1 以降でしか利用できません。
- あらかじめ「includeライブラリ」の導入が必要です。
- 外部実行ファイル「GetKeyState.exe 」で機能を拡張できます。
- 「スニペットプラグイン」を導入していないでも「スニペット(定型文)」機能を利用できます。
- 動作確認は Windows XP sp3 (32bit) × Mery ベータ版 2.8.2 - 2.8.3 (ポータブル) でしかしていません。
一見して 誰得? なマクロですが、「クリップボード履歴」を「スニペット」機能とまとめてツールバーアイコン化できるという素敵なメリットがあります。
- cf. マクロライブラリには「定型文を挿入」機能に特化したマクロも別途あります。
更新履歴
• 2019/08/01: 初版
• 2019/08/06: ・「ピン止めアイテムを貼り付け」コードの変数ミスを修正 ・クリップボード履歴内の重複アイテムを削除する設定を追加。 ・ピン止めアイテムが少ないときはメインメニューにアイテムを表示。 ・snippets.txt 内の空行を無視。 ・クリップボード履歴に長大なデータがあるときの動作速度を少しだけ改善。
• 2019/11/01: ・ステータス表示を追加 ・クリップボードにテキストデータがないときのメニュー構成を変更 ・アイテムの行数(改行数+1)をメニュー内に追加表示 ・メニュー内に表示する改行コードの矢印を「↲」(U+21B2) に変更 ・スニペット機能での「ピン止め/貼り付け」のさいの、改行コードやタブコードではない文字列「\n」や「\t」の扱いを再修正 ・snippets.txt がないときのエラーを修正
ソースコード
- ダウンロード: 「ファイル:クリップボード履歴.zip」(アイコン入り)
#title = "クリップボード履歴..."
#tooltip = "クリップボード履歴 と スニペット"
#icon = "clipboard_history[1].ico"
#include "include/IO.js"
/**
* --------------------------------------------------
* 「クリップボード履歴 と スニペット」マクロ
* sukemaru, 2019/08/01 - 2019/11/01
* --------------------------------------------------
* 「クリップボード履歴」メニューと「スニペット」プラグインと同等の機能を
* ひとつのポップアップメニューに統合します。
*
* 【このマクロの仕様・制限事項】
* ・ver 2.8.1 以前の Mery では使用できません。
* ・「クリップボード履歴」は Mery 本体のメモリストアのものを使用します。
* ※「クリップボードのすべての履歴を削除」したあとでも、
* Windows OS のクリップボードの最新の履歴にテキストデータが残っているときは
* クリップボード履歴メニューに1件だけ表示します。
*
* ・「スニペット」プラグインの設定ファイル snippets.txt の読み書きをします。
* ※「include ライブラリ」が必要です(snippets.txt ファイルの読み書きに使用)。
* ※「スニペット」プラグインを導入していない場合でも、
* 「ピン止めアイテム/スニペット」機能を使用できます。
* snippets.txt ファイルの保存場所は Mery\Plugins\Snippets フォルダです
* (インストーラ版では %AppData%\Mery\Plugins\Snippets フォルダ)。
*
* ・「GetKeyState.exe(キー状態取得実行ファイル) 」で機能を拡張できます。
* → クリップボード履歴のアイテムを Ctrl+クリックした場合、
* アイテムを貼り付けし、貼り終えたアイテムを削除します。
* ※「GetKeyState.exe」は Mery\Macros フォルダに配置してください
* (Macros フォルダ以外の場所にあるなら設定項目でパスを指定する)。
*/
// ---------- ▼ 設定項目 ▼ ---------- //
// ■ クリップボード履歴内の重複アイテムを削除する
var delDupEnable = true; // true: 削除する / false: 削除しない
// ■ ピン止めアイテムをサブメニュー化するアイテム数の閾値
var snSubMenu = 5; // これよりアイテム数が多いときはサブメニューに表示する
// ■ ポップアップメニューを表示する位置
var menuPosMouse = true; // true: マウス位置 / false: キャレット位置
// ■ ポップアップメニューに表示する文字数
var menuWidth = 60;
// ■ GetKeyState.exe のフルパスを指定する場合(「\」記号はふたつがさね「\\」で)
// ※ 未指定 "" なら、 editor.FullName.replace( /[^\\]+$/, "Macros\\GetKeyState.exe" );
var getKeyStatePath = "";
// ---------- ▲ 設定項目 ▲ ---------- //
var start = new Date();
var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
var WshShell = new ActiveXObject( "WScript.Shell" );
// ClipboardData.Get/ClearData() に引数を指定できるのは ver 2.8.1 以降
if ( VersionCheck( "2.8.1" ) ) {
var meryPath = editor.FullName;
var meryDir = meryPath.replace( /[^\\]+$/, "" );
var isPortable = Fso.FileExists( meryPath.replace( /\.exe$/i, ".ini" ) );
var profileDir = ( isPortable )
? meryDir
: WshShell.SpecialFolders( "APPDATA" ) + "\\Mery\\";
var snPath = profileDir + "Plugins\\Snippets\\Snippets.txt";
var snIsExist = Fso.FileExists( snPath );
var gksPath = getKeyStatePath || meryDir + "Macros\\GetKeyState.exe";
var gksIsExist = Fso.FileExists( gksPath );
var $ctrl = 0;
var d = editor.ActiveDocument;
var s = d.selection;
var st = s.Text;
var $status = Status;
// ピン止めアイテム (スニペット)
var snippets = "", snArray = [], snCount = 0 , sn;
if ( snIsExist ) {
snippets = IO.LoadFromFile( snPath, "utf-8" ) || "";
snArray = snippets.replace( /^\n/gm, "" ).replace( /\n$/, "" )
.split( "\n" );
snCount = snArray.length;
sn = ( snCount > snSubMenu );
}
// クリップボード履歴
var cb = ClipboardData;
var cbData = cb.GetData();
var cbArray = [];
var cbData0 = cb.GetData( 0 );
if ( cbData0 ) {
for ( var i = 0, cbItem; ; i ++ ) {
cbItem = cb.GetData( i ) || "";
if ( ! cbItem ) { break; }
// 重複するアイテムを履歴から削除する
if ( delDupEnable ) {
for ( var j = i + 1, cbNextItem; ; j ++ ) {
cbNextItem = cb.GetData( j ) || "";
if ( ! cbNextItem ) { break; }
if ( cbItem == cbNextItem ) {
cb.ClearData( j -- );
}
}
}
// 履歴アイテムを配列に収納する
cbArray.push( cbItem );
}
}
var cbCount = cbArray.length;
var width = String( Math.max( snCount, cbCount || 0 ) ).length;
// ポップアップメニューの準備
var menu = CreatePopupMenu();
var menuFlags = d.ReadOnly ? meMenuGrayed : 0;
var t = ( snSubMenu && snArray[0] ) ? "▼ " : "";
menu.AddPopup( t + "ピン止めアイテム/スニペット (&S) " + t, sm0 = CreatePopupMenu() );
// スニペットのアイテムをメニューに
if ( snCount ) {
for ( var i = 0, id, label, lines; i < snCount; i ++ ) {
id = i + 1;
lines = snArray[i].replace( /[^\t]*\t/, "" ).split( "\\n" ).length;
label = MenuKey( snArray[i].replace( /\t[\s\S]*/, "" ), id, width, true )
+ "\t" + lines + " 行";
if ( label ) {
if ( i < snSubMenu ) {
menu.Add( label, id + 400, menuFlags );
if ( sn ) {
sm0.Add( label, id + 400, menuFlags );
}
}
else {
sm0.Add( label, id + 400, menuFlags );
}
}
}
if ( sn ) {
sm0.Add( "", 0, meMenuSeparator );
}
}
if ( st ) {
sm0.Add( "選択範囲を登録 (&P)", 300 );
}
if ( snIsExist ) {
sm0.Add( "スニペットを編集 (&E) ...", 400 );
}
if ( sn || st || snIsExist ) {
sm0.Add( "", 0, meMenuSeparator );
sm0.Add( "キャンセル & ", 0 );
}
menu.Add( "", 0, meMenuSeparator );
// クリップボード履歴のアイテムをメニューに
if ( cbCount ) {
menu.AddPopup( "▼ クリップボード履歴の一覧 (&C) ▼", sm1 = CreatePopupMenu() );
var sm2 = CreatePopupMenu();
var sm3 = CreatePopupMenu();
for ( var i = 0, id, label, lines; i < cbCount; i ++ ) {
id = i + 1;
lines = cbArray[i].split( "\n" ).length;
label = MenuKey( cbArray[i], id, width )
+ "\t" + lines + " 行";
menu.Add( label, id, menuFlags );
sm2. Add( label, id + 100, menuFlags );
sm3. Add( label, id + 200 );
}
menu.Add( "", 0, meMenuSeparator );
menu.Add( "クリップボードのすべての履歴を削除する (&E)", 500 );
sm1.AddPopup( "貼り付けしてからアイテムを削除 (&M)", sm2 );
sm1.AddPopup( "履歴からアイテムを削除 (&D)", sm3 );
sm1.Add( "", 0, meMenuSeparator );
sm1.Add( "すべての履歴を削除 (&E)", 500 );
}
else if ( cbData.length ) {
menu.Add( "▼ クリップボード ▼", 0, meMenuGrayed );
lines = cbData.split( "\n" ).length;
label = MenuKey( cbData, 1, width )
+ "\t" + lines + " 行";
menu.Add( label, 17, menuFlags );
}
else {
menu.Add( "※ クリップボードにテキストデータはありません ※"
, 0, meMenuGrayed );
}
menu.Add( "", 0, meMenuSeparator );
menu.Add( "キャンセル & ", 0 );
// ステータスバーの表示
if ( menuFlags ) {
Status = " ドキュメントは書き換え禁止です。";
}
else if ( gksIsExist && cbCount ) {
Status = "履歴アイテムの Ctrl+クリック で 「貼り付けしてからアイテムを削除」";
}
else {
Status = "「クリップボード履歴 と スニペット」マクロ";
}
Status += " [ "
+ ( ( new Date() - start ) / 1000 ).toFixed( 3 ).replace( /\./, ". " )
+ " 秒 ]";
// ポップアップメニューを表示
var r = menu.Track( + menuPosMouse );
var confirmStr = "クリップボードのすべての履歴を削除しますか? ";
if ( r == 0 ) {
Status = $status;
}
// 1 ~ 17: 貼り付け
else if ( r <= 16 ) {
d.Write( cbArray[ r -1 ] );
Status = "";
// Ctrl キーを押しながらのときは、貼り付けたアイテムを履歴から削除する
if ( gksIsExist ) {
$ctrl = WshShell.Run( "\"" + gksPath + "\" c", 0, true );
if ( $ctrl == 1 ) {
cb.ClearData( r -1 );
Status = " 履歴からアイテムを削除しました。";
}
}
}
else if ( r == 17 ) {
d.Write( cbData );
Status = "";
}
// 101 ~ 116: 貼り付けして、そのアイテムをクリップボード履歴から削除する
else if ( r > 100 && r < 200 ) {
d.Write( cbArray[ r -101 ] );
cb.ClearData( r -101 );
Status = " 履歴からアイテムを削除しました。";
}
// 201 ~ 216: アイテムをクリップボード履歴から削除する
else if ( r > 200 && r < 300 ) {
cb.ClearData( r -201 );
Status = " 履歴からアイテムを削除しました。";
}
// 401 ~ 499: ピン止めアイテムを貼り付け
else if ( r > 400 && r < 500 ) {
var str = snArray[ r -401 ].replace( /^[^\t]*\t/, "" )
.replace( /(^|[^\\])\\t/g, "$1\t" )
.replace( /(^|[^\\])\\r/g, "$1\r" )
.replace( /(^|[^\\])\\n/g, "$1\n" )
.replace( /\\\\/g, "\\" );
d.Write( str );
Status = "";
}
// 300: 選択範囲を登録(ピン止め)
else if ( r == 300 ) {
var snippetsDir = Fso.GetParentFolderName( snPath );
var pluginsDir = Fso.GetParentFolderName( snippetsDir );
if ( ! Fso.FolderExists( pluginsDir ) ) {
Fso.CreateFolder( pluginsDir );
}
if ( ! Fso.FolderExists( snippetsDir ) ) {
Fso.CreateFolder( snippetsDir );
}
var str = snippets
+ ( snippets ? "\n" : "" )
+ st.replace( /\\/g, "\\\\" )
.replace( /\t/g, "\\t" )
.replace( /\r?\n/g, "\\n" );
IO.SaveToFile( snPath, str, "utf-8", true );
Status = " 選択範囲をスニペットに登録しました。";
}
// 400: ピン止めアイテムを編集(snippets.txt を開く)
else if ( r == 400 ) {
WshShell.Run( "\"" + editor.FullName + "\" \"" + snPath + "\"" );
Status = " " + snPath;
}
// 500: クリップボード履歴をすべて削除する
else if ( r == 500 && Confirm( confirmStr ) ) {
for ( var i = 0; i < cbCount; i ++ ) {
cb.ClearData( i );
Status = " 履歴からすべてのアイテムを削除しました。";
}
}
}
// ---------- ▼ 関数 ▼ ---------- //
/**
* 関数 VersionCheck( versionStr )
* Mery 本体が引数で指定したバージョン以上かチェックする( i.e. "2.6.9" )
* 戻り値は、真偽値 true/false
*/
function VersionCheck( versionStr ) {
var Pad2 = function( str ) {
return str.replace( /[0-9]+/g , function( digit ) {
return digit.length < 2 ? "0" + digit : digit
} )
};
var editorVer = + ( Pad2( editor.Version ).replace( /\./g, "" )
.slice( 0, 6 ) );
var requirement = + ( Pad2( versionStr ).replace( /\./g, "" )
.slice( 0, 6 ) );
return ( editorVer >= requirement );
}
/**
* 関数 MenuKey( str, num, width, convLF )
* ポップアップメニューに表示するラベルを生成する
* ・行頭空白を除去、空白文字を圧縮: →「›」(U+203A)
* ・改行記号を可視化: →「↲」(U+21B2) または「⏎」(U+23CE)
* ・削られてしまう「&」を補完
* ・「¥」(U+005C) を「∖」に置換: →「∖」(U+2216)
* ・ゼロ幅や特殊な空白文字を豆腐に置換: →「⊠」(U+22A0) または「▯」(U+25AF)
* ・判別しづらいメタ文字を全角に置換: !"%'(),.:;@[]`{|}
* ・「a-z」を全角に置換
* ・行番号を空白でケタ埋め: EN SPACE「 」(U+2002)
* ・文字数を切り詰め
*/
function MenuKey( str, num, width, convLF ) {
// 2019/08/05 : slice メソッドを追加
if ( convLF ) {
str = str.replace( /(?:\\r)?\\n/g, " ↲ " ); // ⏎
}
var reg = /[\u00A0\u1680\u180e\u2000-\u200A\u2028\u2029\u202F\u205F\uFEFF]/g;
var menuKey = str.replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " )
.slice( 0, menuWidth + 1 )
.replace( /\r?\n/g, " ↲ " ) // ⏎
.replace( /[&]/g, "&&" )
.replace( /[\\]/g, "∖" )
// .replace( /([^∖!"',.:;`|\t ])([∖]+)/g, "$1 $2" )
.replace( reg, "▯" )
.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g,
function( tmp ) {
return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
} );
num = ( " " + num ).slice( - width ).replace( /\d$/, "&$&" );
menuKey = ( menuKey.length > menuWidth )
? menuKey.slice( 0, menuWidth ) + " ..."
: menuKey;
return num + ": " + menuKey;
}
スポンサーリンク