マクロメニュー

提供:MeryWiki
2020年6月3日 (水) 23:24時点におけるSukemaru (トーク | 投稿記録)による版 (第3版)
ナビゲーションに移動 検索に移動

標準メニューバーの「マクロ(M)」には表示されないマクロを含めて、「マクロのカスタマイズ」に登録済みのすべてのマクロをポップアップメニューで表示します。


  • 「マクロのカスタマイズ」で ☑ なし のマクロ(ツールバーのマクロメニューで非表示扱い)は、非表示マクロ」サブメニュー内に表示します。
⇒ マクロバー(マクロツールバー)や標準メニューバーの [マクロ(M)] メニューに表示されないマクロにもアクセス(実行・再編集)しやすくなります。


  • 基本的に マクロ名は ファイル名 で表示します(各ソースコード内の #title="ほげ" を読みこみません)。
  • 各マクロの実体ファイルの親フォルダ名を表示できます。
  • ファイル名 「1 マクロA.js」 「2. マクロB.js」 「3_マクロC.js」 「4 - マクロD.js」 などの先頭の連番部分と拡張子をポップアップメニュー上に表示させないように設定できるので、通常のマクロメニューと同じような状態にできます。


※ [選択] や [これを選択] で登録したマクロがメニューに表示されない場合は、 [カスタマイズ] ダイアログを開いて OK ボタンで確定すると表示されるようになります。

  • 追加コマンド [このマクロを実行] は、アクティブなタブのマクロを実行するためのコマンドです。
※ マクロ用と判別できる拡張子で 保存済み のタブでのみ使用できます。
拡張子に対して正規表現 /^(?:jse?|pls?|php|pys?|rbs?|vb[es])$/i でチェックします。


  • 追加コマンド [Macros フォルダを開く] は、Mery インストールフォルダ内の "Macros" フォルダをファイラ(エクスプローラ)で開くためのコマンドです。



マクロメニュー(第3版)

(2020/06/03)
スクリーンショット (ver 3)

  • マクロのソースコードを開く」サブメニューをあらたに設けて、GetKeyState.exe なしでもマクロの実体ファイル(ソースコード)を Mery で開けるようにしました。
※ マクロの各ソースコードが複数のフォルダに分散している(カテゴリ分けされている)ばあいは、サブメニュー内にそれぞれの親フォルダ名でカテゴライズします。
※「Macros フォルダを開く」コマンドはこのサブメニュー内に移動させました。


  • 「各マクロの拡張子を表示しない trimExt」オプションとはべつに「マクロのラベルを変換テーブルで書き換える setTitle」オプションを設けて、マクロメニューに表示させる「マクロ名」のラベルを任意に変更できるようにしました。
※ ソースコード内の配列で、表示名を変更したいマクロそれぞれごとに [ "マクロのファイル名", "マクロメニューに表示するラベル" ] を設定する必要があります。
(各実体ファイルのソースコード冒頭の #title = "ほげ" を読みこませるのは、ポップアップメニュー表示までの時間がかかりすぎるので見送りました)


  • メニュー内の連番部分の右詰め(空白埋め)用の空白文字を指定するための設定項目を設けました。
※ これまでの拙作マクロでは Windows XP の MS UI Gothic フォントにあわせて「2分アキ: EN SPASE」(U+2002) "" を使用していましたが、Meiryo UI や Segoe UI などでは半角数字と同じ幅になる空白文字種 [1] が異なるようなので、変更しやすくしました。
  • Meiryo UI ⇒ 「和字間隔 (全角空白)」 (U+3000)
  • Segoe UI ⇒ 「図形間隔: FIGURE SPACE」(U+2007)
※ ウエイト(文字の太さ)の設定によっては半角数字との幅があわないことがあります。


  • おまけ機能として「プラグイン」「外部ツール」のサブメニューと、任意の「ファイル/フォルダ/URL」を開くためのサブメニューを表示させることができます。
※ ソースコード内の設定用の配列で、表示させたい項目を [ "メニューに表示するラベル", "パスまたは URL" ] のかたちで登録する必要があります。


ソースコード

ダウンロード >> 「ファイル:マクロメニュー.zip(アイコン入り、第3版: 2020/06/03)

#title = "マクロメニュー..."
#tooltip = "全マクロをポップアップメニュー表示"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",318
// #icon = "script_with_lines+.ico"

/**
 * ---------------------------------------------------------
 * マクロメニュー
 * sukemaru, 2019/11/02 - 2020/06/03
 * (第3版: GetKeyState.exe 不要)
 * ---------------------------------------------------------
 * 非表示マクロを含めて全ての登録済みマクロをポップアップメニューで表示する。
 * 
 * 「マクロのソースコードを開く」サブメニュー内のマクロを選択したときは、
 * マクロの実体ファイル(ソースコード)を Mery で開く。
 */

// ---------- ▼ 設定項目 ▼ ---------- //
// ※「マクロのソースコードを開く」サブメニュー内では
//    "親フォルダ名/ ファイル名 (拡張子つき)" の表示で固定

// ■ ポップアップメニューを表示する位置
var menuPosMouse = true;	// (true: マウス位置 / false: キャレット位置)

// ■ 各マクロの親フォルダ名を表示する
var showParent = 2;
  // 0: 表示しない
  // 1: すべて表示する
  // 2: 「非表示マクロ」でのみ表示し、メインカテゴリ内では表示しない

// ■ 各マクロの拡張子を表示しない(※ファイル名先頭の連番部分も表示しない)
var trimExt = true;	// (true: 表示しない / false: 表示する)

// ■ マクロのラベルを変換テーブルで書き換える
var setTitle = true;	// (true: 書き換える / false: 書き換えない)

// ■「 プラグイン」と「外部ツール」のサブメニューを表示する
var showExtra = true;	// (true: 表示しない / false: 表示する)


// ■ 連番アイテムの桁埋め(右寄せ/空白埋め)用空白文字の定義
var blankChr = " ";	// " " or "\u2002" or "\u3000" or "\u2007" or ...

  /* 半角数字の幅に等しい空白文字として
     MS UI Gothic (MeiryoKe_UI Gothic) では 2分アキ=EN SPACE: "\u2002"
     Meiryo UI では 和字間隔=全角空白: "\u3000"
     Segoe UI (Yu Gothic UI も?) では 図形間隔=FIGURE SPACE: "\u2007"
     にすると具合がよさそう */

// ---------- ▲ 設定項目 ▲ ---------- //

var Fso        = new ActiveXObject( "Scripting.FileSystemObject" );
var WshShell   = new ActiveXObject( "WScript.Shell" );
var dir = {};
dir.meryPath   = editor.FullName;
dir.meryDir    = dir.meryPath.replace( /[^\\]+$/, "" );
dir.meryName   = Fso.GetBaseName( dir.meryPath );
dir.macroDir   = dir.meryDir + "Macros\\";
dir.currentDir = WshShell.CurrentDirectory;
dir.macroPath  = editor.ActiveDocument.FullName;
dir.profileDir = Fso.FileExists( dir.meryDir + dir.meryName + ".ini" )
               ? dir.meryDir
               : WshShell.SpecialFolders( "APPDATA" ) + "\\Mery\\";
dir.iniPath    = dir.profileDir + dir.meryName + ".ini";
dir.settingDir = dir.profileDir + "Macros\\MacroSettings";
dir.includeDir = dir.meryDir + "Macros\\include";
var filePath   = dir.macroPath;

// 連番の桁埋め(右寄せ/空白埋め)用
var b1 = ( ! blankChr || ( "" + blankChr ).length > 1 )
       ? " " : blankChr;

// ポップアップメニューを表示
var mm = MacroMenu( dir, showParent, trimExt, setTitle, showExtra );
var id = mm.menu.Track( menuPosMouse );

// このマクロを実行
if ( id == 1 ) {
  // ショートカットキーを使用しない		macroPath = filePath;
  WshShell.Run( '"' + dir.meryPath
  + '" /mf "' + dir.macroPath + '" "' + filePath + '"' );
}

// フォルダを開く
else if ( id >= 2 && id <= 4 ) {
  var folder = ( id == 2 ) ? dir.macroDir	// Macros フォルダ
             : ( id == 3 ) ? dir.includeDir	// include フォルダ
             : ( id == 4 ) ? dir.settingDir	// MacroSettings フォルダ
             : ""
  Status = " " + folder;
  if ( Fso.FolderExists( folder ) ) {
    WshShell.Run( '"' + folder + '"' );
  }
}

// 選択したマクロの実体ファイルを Mery で開く(編集モード)
else if ( id >= 16144 ) {
  // ※ MacroMenu() の戻り値の配列内のパスは相対パス
  WshShell.CurrentDirectory = dir.meryDir;
  var macroPath = Fso.GetAbsolutePathName( mm.macros[ id - 16144 ].path );
  WshShell.CurrentDirectory = dir.currentDir;
  Status = " " + macroPath;
  if ( Fso.FileExists( macroPath ) ) {
    WshShell.Run( '"' + dir.meryPath + '" "' + macroPath + '"' );
  }
}

// マクロまたはコマンドを実行
else if ( id >= 2048 ) {
  var commandReg = RegExp( "^" + blankChr + "*\\d*&\\d:" + blankChr
                         + "| ?\\(&.\\)|\\*+", "g" );
  Status = " " + mm.menu.GetText( id ).replace( commandReg, "" );
  editor.ExecuteCommandByID( id );
}

// 「ファイル/フォルダ/URL」を開く
else if ( id >=1500 ) {
  RunAppendedItem( AppendixArray(), id - 1500, dir.currentDir );
}


// ----------------------------------------

/**
 * 関数 MacroMenu( folders: object, showParentFolderName: boolean, trimExtension: boolean, setTitle: boolean, showExtra: boolean )
 * 戻り値はオブジェクト型
 * { menu: objPopupMenu, macros: macroArray, hotKey: strHotKey }
 * ※ macroArray の各要素は { id: int, path: macroPath, visible: int } で
 *    id は Mery.ini に記述されている Macros\\Macro## の連番( 0 ~ )
 *    path は Mery.ini に記述されているままの相対パス
 *    visible は 数値 1 または 0
 * ※ hotKey は「実行(R)」のショートカットキーを wshSell.SendKeys() 用に整形した文字列
 */
function MacroMenu( dir, showParent, trimExt, setTitle, showExtra ) {
  var start = new Date();
  var menu = CreatePopupMenu();
  var b3 = blankChr + blankChr + blankChr;
  var id,  num,  label;
  var d = editor.ActiveDocument;

  // Mery.ini のパスを取得
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  var WshShell    = new ActiveXObject( "WScript.Shell" );
  var meryDirName = Fso.GetBaseName( dir.meryDir );

  // Mery.ini からデータを取得
  var Adodb = new ActiveXObject( "ADODB.Stream" );
  var adTypeText = 2,  adReadAll = -1;
  Adodb.Type = adTypeText,  Adodb.Charset = "UTF-8";
  Adodb.Open();
  Adodb.LoadFromFile( dir.iniPath );
  var iniText = Adodb.ReadText( adReadAll );
  Adodb.Close();  Adodb = null;

  // 「実行」のショートカットキーを取得
  var hotKey = "";
  // 「このマクロを実行」コマンドの準備
  var ext = d.Name.substr( d.Name.lastIndexOf( "\." ) + 1 );
  var reg2 = /^(?:jse?|pls?|php|pys?|rbs?|vb[es])$/i;
  var macroName = reg2.test( ext ) ? d.Name : "";
  var runFlag = ( macroName && d.Saved ) ? 0 : meMenuGrayed;

  // マクロのパスを抽出
  var reg3 = /\r\n\[Macros\\Macro\d+\]\r\nFileName=[^\r\n]+\r\nVisible=\d/g;
  // \r\n [Macros\Macro##] \r\n FileName=MacroPath \r\n Visible=#
  var macroArray = [],  tmpArray = iniText.match( reg3 ) || [];
  var mcCount = tmpArray.length;
  if ( mcCount ) {
    var invisible = false;
    // index 番号と MacroPath と Visible フラグを抽出
    var reg4 = /\r\n\[Macros\\Macro(\d+)\]\r\nFileName=([^\r\n]+)\r\nVisible=(\d)/;
    var reg5 = /(?:[^\\]+?\\)*?(?:([^\\]+)\\)?([^\\]+)$/;
    for ( var i = 0, _dir, ret1; i < mcCount; i ++ ) {
      // { path: MacroPath, visible: #int }		※ path は相対パスの場合がある
      ret1 = reg4.exec( tmpArray[i] );
      id = + ret1[1];  path = ret1[2];  visible = + ret1[3];
      ret2 = reg5.exec( path );
      _dir = ret2[1] || "";  name = ret2[2];
      macroArray[ id ] = {
        id: id,  path: path,  visible: visible,  dir: _dir,  name: name
      };
      if ( ! invisible && visible == 0 ) { invisible = true; }
    }
    tmpArray = null;

    // ポップアップメニュー項目
    // マクロのファイル名一覧を作成 (非表示マクロをサブメニュー化)
    // ※ Add() の第2引数 id は 6144 からの連番
    if ( invisible ) {
      menu.AddPopup( "非表示マクロ(&H)", subMenu = CreatePopupMenu() );
      menu.Add( "-----", 0, meMenuSeparator );
    }

    for ( var i = j = k = 0, mcWidth = ( "" + mcCount ).length,
    limit = Math.min( mcCount, 1024 ), macro, mcName, 
    mcReg = /^\d+[-._ ]+|\.js$/g; i < limit; i ++ ) {	// >
      id     = i + 6144;
      macro  = macroArray[i];
      mcName = ( trimExt )	// ※ trimExt = true ならマクロのタイトルラベルを変換
             ? ( setTitle ? GetMacroTitle( macro.name ).replace( mcReg, "" )
                          : macro.name.replace( mcReg, "" ) )
             : macro.name;
      // 可視マクロと不可視マクロの振り分け
      if ( macro.visible === 1 ) {	// Visible フラグ
        num = ( b3 + ( ++ j ) ).slice( - mcWidth ).replace( /\d$/, "&$&:" + blankChr );
        label = ( showParent == 1 ?  macro.dir + "/ " : "" ) + mcName;
        menu.Add( num + label.replace( /&/g, "&&" ), id );
      }
      else if ( invisible && macro.visible === 0 ) {
        num = ( b3 + ( ++ k ) ).slice( - mcWidth ).replace( /\d$/, "&$&:" + blankChr );
        label = ( showParent ?  macro.dir + "/ " : "" ) + mcName;
        subMenu.Add( num + label.replace( /&/g, "&&" ), id );
      }
    }

    menu.Add( "-----", 0, meMenuSeparator );
    if ( invisible ) {
      subMenu.Add( "-----", 0, meMenuSeparator );
      subMenu.Add( "キャンセル\t& ", 0 );
    }
  }

  // 標準コマンド
  menu.Add( "編集(&E)  " + ScriptName, 2173 );
  menu.Add( "選択(&L)...", 2174 );
  menu.Add( "これを選択(&T)", 2175, ! d.Name *2 );
  if ( d.FullName != ScriptFullName ) {	// && hotKey 
    menu.Add( "このマクロを実行(&G)*  " + macroName, 1, runFlag );
  }
  menu.Add( "カスタマイズ(&C)...", 2176 );
  menu.Add( "-----", 0, meMenuSeparator );

  // 「マクロのソースコードを開く」サブメニュー
  var emMenu = MacroMenuED( 16144, macroArray, mcCount, mcWidth, dir, blankChr, Fso );
  menu.AddPopup( "マクロのソースコードを開く(&M)**", emMenu );
  menu.Add( "-----", 0, meMenuSeparator );
  if ( ! showExtra ) { menu.Add( "キャンセル\t& ", 0 ); }

  var plCount, tlCount;
  if ( showExtra ) {
    // プラグインのパスを取得する
    // \r\n [Plugins\Plugin##] \r\n FileName=PluginPath を抽出
    var reg6 = /\r\n\[Plugins\\Plugin\d+\]\r\nFileName=[^\r\n]+/g;
    var pluginArray = [],  tmpArray = iniText.match( reg6 ) || [];
    // index 番号と PluginName を抽出
    var reg7 = /\r\n\[Plugins\\Plugin(\d+)\]\r\nFileName=(?:[^\\]+\\)*([^\r\n]+)/;
    for ( var i = 0, len = tmpArray.length, re; i < len; i ++ ) {
      re = reg7.exec( tmpArray[i] );
      pluginArray[ + re[1] ] = re[2];
    }
    // 外部ツール名を取得する
    // \r\n [Tools\Tool##] \r\n Caption=ToolName を抽出
    var reg8 = /\r\n\[Tools\\Tool\d+\]\r\nCaption=[^\r\n]+/g;
    var toolArray = [],  tmpArray = iniText.match( reg8 ) || [];
    // index 番号と ToolName を抽出
    var reg9 = /\r\n\[Tools\\Tool(\d+)\]\r\nCaption=([^\r\n]+)/;
    for ( var i = 0, len = tmpArray.length, re; i < len; i ++ ) {
      re = reg9.exec( tmpArray[i] );
      toolArray[ + re[1] ] = re[2];
    }
    tmpArray = null;
    plCount = pluginArray.length,  plWidth = ( "" + plCount ).length;
    tlCount = toolArray.length,    tlWidth = ( "" + tlCount ).length;
  }

  // プラグイン名一覧を作成	※ Add() の第2引数 id は 7168 からの連番
  if ( plCount ) {
    // 有効/無効の ✔ チェックマーク表示は無理
    // Mery Wiki に登録されたプラグインは名前で表示
    menu.AddPopup( "プラグイン(&P)", plMenu = CreatePopupMenu() );
    for ( var i = 0, limit = Math.min( plCount, 1024 ); i < limit; i ++ ) {
      id    = i + 7168;
      label = GetPluginName( pluginArray[i] ).replace( /&/g, "&&" );
      num   = ( b3 + ( i + 1 ) ).slice( - plWidth ).replace( /\d$/, "&$&:" + blankChr );
      plMenu.Add( num + label, id );
    }
      plMenu.Add( "-----", 0, meMenuSeparator );
      plMenu.Add( "プラグインの設定(&C)...", 2181 );
    if ( ! tlCount ) {
    menu.Add( "-----", 0, meMenuSeparator );
    menu.Add( "キャンセル\t& ", 0 );
    }
  }
  // 外部ツール名一覧を作成	※ Add() の第2引数 id は 8192 からの連番
  if ( tlCount ) {
    menu.AddPopup( "外部ツール(&X)", etMenu = CreatePopupMenu() );
    for ( var i = 0, limit = Math.min( tlCount, 1024 ); i < limit; i ++ ) {
      id    = i + 8192;
      label = toolArray[i].replace( /&/g, "&&" );
      num   = ( b3 + ( i + 1 ) ).slice( - tlWidth ).replace( /\d$/, "&$&:" + blankChr );
      etMenu.Add( num + label, id );
    }
      etMenu.Add( "-----", 0, meMenuSeparator );
      etMenu.Add( "外部ツールの設定(&C)...", 2182 );
      etMenu.Add( "-----", 0, meMenuSeparator );
      etMenu.Add( "ツールジョブの中止(&J)", 2183 );

    // 「ファイル/フォルダ/URL」サブメニュー
    AppendixMenu( menu, 1500, "A", false );

    menu.Add( "-----", 0, meMenuSeparator );
    menu.Add( "キャンセル\t& ", 0 );
  }

  // ステータスを表示
  Status = " 「マクロメニュー」 "
         // + "** " + ( $ctrl ? "編集" : "起動" ) + "モード ** "
         + " " + j + " 件 + " + k + " 件  [ "
         + ( ( new Date() - start ) / 1000 ).toFixed( 3 ).replace( /\./, ". " )
         + " 秒 ]";
  return { menu: menu, macros: macroArray, hotKey: hotKey };
}


/**
 * 関数 MacroMenuED( startID, macroArray, macroCount, mcWidth, objFolders, blankChr, Fso )
 * 2020/05/20 - 2020/05/23: 「マクロのソースコードを編集」サブメニュー
 * マクロが複数のフォルダに分散しているときは親フォルダ名でサブメニュー化する。
 * ※ PopupMenu オブジェクトを返す
 * ※ 各マクロのメニュー ID は 16144 ~
 * 
 * ※ 親フォルダ名ごとに振り分けるさいに、上位階層のパスまではチェックしない
 * ※ マクロとして登録されていない include ライブラリのアイテムも表示できるが、
 *   Fso.GetFolder() で取得することになるのでパフォーマンスが低下する
 */
function MacroMenuED( startID, mcArray, mcCount, mcWidth, dir, blankChr, Fso ) {
  // var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  var incExists  = Fso.FolderExists( dir.includeDir );
  /* Macros/include フォルダ直下の .JS ファイルを配列に追加 */
	  // if ( incExists ) {
	  //   var incFiles = new Enumerator( Fso.GetFolder( dir.includeDir ).Files );
	  //   for ( ; ! incFiles.atEnd(); incFiles.moveNext() ) {
	  //     if ( /\.js$/i.test( incFiles.item() ) ) {
	  //       mcArray.push( {
	  //         id: mcCount ++,
	  //         path: incFiles.item().Path,  dir: "include",
	  //         name: /([^\\]+)$/.exec( incFiles.item().Path )[1]
	  //       } );
	  //     }
	  //   }
	  // }

  var id, label, num, macro, _dir, b3 = blankChr + blankChr + blankChr;
  var mcDirArray = [];
  GetFolderNameLoop:	// マクロの親フォルダ名を収集する
  for ( var i = 0, limit = Math.min( mcCount, 1024 ); i < limit; i ++ ) {
    _dir = mcArray[i].dir;
    for ( var j = hit = 0; j < mcDirArray.length; j ++ ) {
      if ( _dir == mcDirArray[j] ) {
        hit ++;  continue GetFolderNameLoop;
      }
    }
    if ( ! hit ) { mcDirArray.push( _dir ); }
  }
  var mcDirCount = mcDirArray.length;
  var scrollEnable = ( mcCount > 40 );

  // ポップアップメニュー項目
  var emMenu = CreatePopupMenu();	// 「マクロのソースコードを開く」
  emMenu.Add( "Macros フォルダを開く(&M)", 2 );
  if ( incExists ) {
    emMenu.Add( "include フォルダを開く(\u2006&I\u2006)", 3 );
  }
  if ( Fso.FolderExists( dir.settingDir ) ) {
    emMenu.Add( "MacroSettings フォルダを開く(&S)", 4 );
  }
  emMenu.Add( "-----", 0, meMenuSeparator );	// **

  if ( mcDirCount == 1 ) {	// 全マクロがひとつのフォルダ内にある場合
    for ( var i = 0, j = 1; i < mcCount; i ++, j ++ ) {
      id    = i + startID;
      macro = mcArray[i];
      label = ( macro.name ).replace( /&/g, "&&" );
      num   = ( b3 + ( j ) ).slice( - mcWidth ).replace( /\d$/, "&$&:" + blankChr );
      emMenu.Add( num + label, id );
      if ( j % 10 == 0 && j != mcCount ) {
        emMenu.Add( "", 0, meMenuSeparator );
        if ( scrollEnable && j % 20 == 0 &&  j < mcCount - 5 ) {
          emMenu.Add( "キャンセル\t& ", 0 );	// スペースキーでスクロール可
          emMenu.Add( "-----", 0, meMenuSeparator );
        }
      }
    }
  }
  else {	// マクロが複数の親フォルダに分かれている場合
    mcDirArray.sort();		// 親フォルダ名でソート
    AddFolderCategoryLoop:	// 親フォルダ名のサブメニュー項目をつくる
    for ( var i = 0, j = 1, tLen; i < mcDirCount; i ++, j ++ ) {
      _dir  = mcDirArray[i];
      num   = ( b3 + j ).slice( -2 ).replace( /\d$/, "&$&:" + blankChr );
      label = _dir.replace( /&/g, "&&" ) + " / ";	// 
      emMenu.AddPopup( num + label, subMenu = CreatePopupMenu() );
      if ( j % 5 == 0 && j != mcDirCount ) {
        emMenu.Add( "", 0, meMenuSeparator );
      }
      GetMacroFileLoop:		// 配下のマクロを収集する
      for ( var k = 0, tmpArray = []; k < mcCount; k ++ ) {
        if ( mcArray[k].dir == _dir ) {
          tmpArray.push( mcArray[k] );
        }
      }
      tLen = tmpArray.length;
      tmpArray.sort( function( a, b ) {	// マクロのファイル名でソート
        return ( a.name === b.name )
             ? ( ( a.path < b.path ) ? -1 : 1 )
             : ( ( a.name < b.name ) ? -1 : 1 );
      } );
      AddMacroFileLoop:
      for ( var m = 0, n = 1; m < tLen; m ++, n ++ ) {
        macro = tmpArray[m];
        id    = macro.id + startID;
        label = macro.name.replace( /&/g, "&&" );
        num   = ( b3 + n ).slice( - mcWidth ).replace( /\d$/, "&$&:" + blankChr );
        subMenu.Add( num + label, id );
        if ( n % 10 == 0 && n != tLen ) {
          subMenu.Add( "", 0, meMenuSeparator );
        }
      }
    }
  }
  emMenu.Add( "-----", 0, meMenuSeparator );	// **
  emMenu.Add( "キャンセル\t& ", 0 );	// **
  return emMenu;
}

/**
 * 関数 AppendixMenu( objMenu, startID, accelerator, addSeparator )
 * 任意の ファイル/フォルダパス または URL のサブメニュー
 * 第4引数は「ファイル/フォルダ/URL」項目の下にセパレータの追加を指定する Boolean
 */
function AppendixMenu( objMenu, startID, accelerator, addSeparator ) {
  var appendixArray = AppendixArray() || [];
  var apCount = appendixArray.length;
  if ( apCount > 2 ) {
    var apMenu = CreatePopupMenu();
    objMenu.AddPopup( "ファイル/フォルダ/URL"
    + ( accelerator ? "(&" + accelerator + ") " : " " ) + "**"
    , apMenu );

    if ( addSeparator ) {
      objMenu.Add( "-----", 0, meMenuSeparator );
    }

    // 最初のアイテム(サンプル)をスキップ
    for ( var i = 1, j = startID, limit = Math.min( apCount - 1, 100 );
    i < limit; i ++ ) {
      appendix = appendixArray[i];
      if ( ! appendix[0] || ! /\w/.test( appendix[0] ) ) {
        apMenu.Add( "-----", 0, meMenuSeparator );
        continue;
      }
      else if ( ! appendix[1] ) {
        apMenu.Add( appendix[0], 0, meMenuGrayed );
        continue;
      }
      apMenu.Add( appendix[0], i + startID );
    }
    apMenu.Add( "-----", 0, meMenuSeparator );
    apMenu.Add( "キャンセル\t& ", 0 );
    return;
  }
}

/**
 * 関数 RunAppendedItem( Array, index, currentDirectory )
 * AppendixMenu() のコマンドを実行する
 * (AppendixArray() の配列内の ファイルパス または URL を開く)
 * 
 * Array の 各要素の形式は
 * [ strings, Path/URL [, application [, parameter ]] ] 
 * または  [ strings, 1, 0, function(){ ... } ] 
 * ※ ファイル/アプリケーションの実在確認はしない
 */
function RunAppendedItem( appendixArray, id, currentDir ) {
  var cmd = appendixArray[id];
  var currentDirBuckup;
  var WshShell = new ActiveXObject( "WScript.Shell" );

  if ( ! /^(?:w+:\/\/|\w:\\)/.test( cmd[1] ) ) {
    currentDirBuckup = currentDir;
    WshShell.CurrentDirectory = editor.ActiveDocument.Path
                             || editor.FullName.replace( /[^\\]+$/, "" );
  }
  try {
    // cmd[3] を function とみなして実行
    if ( ! cmd[2] && cmd[3] && typeof cmd[3] === "function" ) {
      cmd[3]();
    }
    // cmd[3] をコマンドラインオプションとみなして実行
    else if ( cmd[2] && cmd[3] ) {
      WshShell.Run( '"' + cmd[2] + '" ' + cmd[3] + '"' + cmd[1] + '"' ) ;
    }
    // applicationPath + filePath とみなして実行
    else if ( cmd[2] ) {
      WshShell.Run( '"' + cmd[2] + '" "' + cmd[1] + '"' ) ;
    }
    // 既定のプログラムで開く
    else { WshShell.Run( '"' + cmd[1] + '"' ); }
  }
  catch( e ) { Alert( e ); }
  finally {
    if ( currentDirBuckup ) {
      WshShell.CurrentDirectory = currentDirBuckup;
    }
  }
}

/**
 * 関数 GetMacroTitle( macroName )
 * マクロのファイル名から「タイトル」の文字列を返す
 */
function GetMacroTitle( macroName ) {
  var mcTitleArray = [

//  [ "マクロのファイル名", "マクロメニューに表示するラベル" ],

    [ "コンパクトメニュー", "コンパクトメニュー..." ], 
    [ "マクロメニュー", "マクロメニュー..." ],	// ← 後ろにカンマ(必須)


  [] ];
  for ( var i = 0, len = mcTitleArray.length; i < len; i ++ ) {
    if ( macroName === mcTitleArray[i][0]
    || macroName.replace( /\.[^.]+$/, "" ) === mcTitleArray[i][0] ) {
      return mcTitleArray[i][1];
    }
  }
  return macroName;
}

/**
 * 関数 GetPluginName( dllName )
 * プラグイン.DLL のファイル名から「プラグイン名」を返す
 */
function GetPluginName( dll ) {
  var plTitleArray = [
    [ "Outline", "アウトライン" ],
    [ "KureiKei", "プロ生ちゃん" ],
    [ "EditorConfig", "EditorConfig" ],
    [ "LiveStyle", "Emmet LiveStyle" ],
    [ "AlphaBlend", "半透明" ],
    [ "ClassView", "クラスビュー" ],
    [ "Conversion", "半角/全角 変換" ],
    [ "Favorites", "お気に入り" ],
    [ "HSPRun", "HSPコンパイル実行" ],
    [ "Snippets", "スニペット" ],
    [ "WebPreview", "Webプレビュー" ],
    [ "ClipboardHistory", "クリップボード履歴" ],
    [ "PathPaste", "コピーしたファイルやフォルダのパスを貼り付け" ],
    [ "RectangleInput", "矩形選択範囲の各行の先頭に文字を挿入" ],
    [ "OutputBarHook", "OutputBar拡張" ],
    [ "MeryRelay2Plugin", "プロセス中継" ],
    [ "MeryRelay", "別プロセスで編集" ],
    [ "CheckModified", "文書の変更をチェック" ],
    [ "CloseSplitEditor", "分割されたエディタを閉じる" ],
    [ "IPMessenger", "IPメッセンジャー" ],
    [ "MeryCsvList", "MeryCsvList" ],
    [ "SearchToolbar", "検索バー" ]
  ];
  for ( var i = 0, len = plTitleArray.length; i < len; i ++ ) {
    if ( dll.indexOf( plTitleArray[i][0] ) > -1 ) {
      dll = plTitleArray[i][1];  break;
    }
  }
  return dll.replace( /\.dll$/i, "" );
}

/**
 * 関数 AppendixArray()
 * 任意の ファイル/フォルダパス または URL を開くためのサブメニュー項目
 * (メニューラベル、ファイルパスまたは URL の定義用配列)
 * 配列の各要素の形式は [ strings, Path/URL [, application [, parameter]] ] 
 * 
 * ※ 任意のマクロコマンドを実行させる項目を登録するなら以下のような配列にする
 *  [ strings, 1, 0, function(){ ... } ]
 */
function AppendixArray() {
  var appendixArray = [

// ---------- ▼ メニューに追加するファイルまたは URL ▼ ---------- //

// ■ 配列で「ラベル」「パスまたは URL」「アプリケーション (省略可)」を指定する
// ※ ファイルパスの「\」は二重「\\」にすること
// ※ 空の配列  [],  を置くと、メニュー内にセパレータ(区切りの横罫線)を挿入
// ※ 登録できるアイテムの最大数はセパレータ含めて 100 件まで


//【サンプル】	※ この最初のアイテム(サンプル)はメニューに表示されない
[ "サンプル URL を開く(&W)..."
, "http://www.example.com/"
, "C:\\Program Files\\Firefox\\firefox.exe" ],	// ← 最後にカンマ(必須)
// [],

[ "Mery フォルダ (&F)...", editor.FullName.replace( /[^\\]+$/, "" ) ],
[],

[ "マクロリファレンス:3 - MeryWiki (&R)...", 
  "https://www.haijin-boys.com/wiki/マクロリファレンス:3" ],
[ "マクロライブラリ - MeryWiki (&L)...", 
  "https://www.haijin-boys.com/wiki/マクロライブラリ" ],
[ "マクロメニュー - MeryWiki (&M)...", 
  "https://www.haijin-boys.com/wiki/マクロメニュー" ],

// [],
// [ 'Alert( "ほげ" ); ...', 1, 0, function(){ Alert("ほげ"); } ],

// ---------- ▲ メニューに追加するファイルまたは URL ▲ ---------- //

  [] ];
  return appendixArray;
}


マクロメニュー(第2版)

(2020/05/02)
スクリーンショット (ver 2)

  • GetKeyState.exe(キー状態取得実行ファイル)」を導入すると、Ctrl キーを押しながらこのマクロを起動したときに、ポップアップメニュー内のマクロ名を選択(クリック/Enter)したさいの処理を変更できます。
    ⇒ 選択したマクロを Mery で開く(編集する)
Mery インストールフォルダ内の Macros フォルダに実行ファイル GetKeyState.exe を配置してください
(または、設定項目 var gksPath でフルパスを指定)。
※ GetKeyState.exe を導入していないばあいは、選択したマクロをそのまま実行します。
マクロの自作・編集をしない人には設定項目 var gksEnable = false; にすることをおすすめします(ポップアップメニューの表示が高速化するはず)。


ソースコード

ダウンロード >> 「マクロメニュー.zip(アイコン入り、第2版: 2020/05/02)
リンク先ダウンロードページ内の「ファイルの履歴」2020年5月2日 (土) 03:38 をクリックすると、第2版をダウンロードできます。

#title = "マクロメニュー..."
#tooltip = "全マクロをポップアップメニュー表示"
// #icon = "script_with_lines+.ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",318

/**
 * ---------------------------------------------------------
 * マクロメニュー
 * sukemaru, 2019/11/02 - 2020/05/02
 * ---------------------------------------------------------
 * 非表示マクロを含めて全ての登録済みマクロをポップアップメニューで表示する。
 * 
 * Ctrl キーを押しながら「マクロメニュー」マクロを起動して (要 GetKeyState.exe)
 * ポップアップメニュー内のマクロを選択したときは、ソースコードを Mery で開く。
 */

// ---------- ▼ 設定項目 ▼ ---------- //

// ■ ポップアップメニューを表示する位置
var menuPosMouse = true;	// (true: マウス位置 / false: キャレット位置)

// ■ 各マクロの親フォルダ名を表示する
var showParent = false;	// (true: 表示する / false: 表示しない)

// ■ 各マクロの拡張子を表示しない(※ファイル名先頭の連番部分も表示しない)
var trimExt = false;	// (true: 表示しない / false: 表示する)

// ■ GetKeyState.exe による機能拡張を使用する
var gksEnable = true;

// ■ GetKeyState.exe のフルパスを指定する場合
var gksPath = "";
  // 未指定 "" なら、Mery インストールフォルダの Macros\GetKeyState.exe
  // ※ GetKeyState.exe なしのときも "" にする
  // ※ \ 記号はふたつがさね「\\」で記述すること

// ---------- ▲ 設定項目 ▲ ---------- //

var Fso			 = new ActiveXObject( "Scripting.FileSystemObject" );
var WshShell	 = new ActiveXObject( "WScript.Shell" );
var meryPath	 = editor.FullName;
var meryDir		 = meryPath.replace( /[^\\]+$/, "" );
var macroDir	 = meryDir + "Macros\\";
var currentDir	 = WshShell.CurrentDirectory;
var macroPath	 = filePath = editor.ActiveDocument.FullName;

// GetKeyState.exe
var gks   = gksPath || macroDir + "GetKeyState.exe";
var $ctrl = ( gksEnable && Fso.FileExists( gks ) &&
            WshShell.Run( '"' + gks + '" ctrl', 0, true ) != 0 )
          ? true : false;


// ポップアップメニューを表示
var mm = MacroMenu( showParent, trimExt, $ctrl );
var id = mm.menu.Track( menuPosMouse );

// 「このマクロを実行」
if ( id == 1 ) {
  WshShell.Run( '"' + meryPath + '" /mf "' + macroPath + '" "' + filePath + '"' );
}

// 「Macros フォルダを開く」
else if ( id == 2 ) {
  WshShell.Run( '"' + macroDir + '"' );
  Status = " " + macroDir;
}

// Ctrl キーを押しながらマクロメニューを起動したとき
else if ( $ctrl && id >= 6144 && id < 7168 ) {
  WshShell.CurrentDirectory = meryDir;
  macroPath = Fso.GetAbsolutePathName( mm.macros[id - 6144].path ); // 相対パスの解決
  WshShell.CurrentDirectory = currentDir;
  if ( Fso.FileExists( macroPath ) ) {
    // 選択したマクロの実体ファイルを Mery で開く(編集モード)
    WshShell.Run( '"' + meryPath + '" "' + macroPath + '"' );
    Status = " " + macroPath;
  }
}

// マクロまたはコマンドを実行
else if ( id > 2 ) {
  editor.ExecuteCommandByID( id );
}

/**
 * 関数 MacroMenu( [ showParentFolderName: boolean [, trimExtension: boolean [, ctrl ]]] )
 * 戻り値は { menu: objPopupMenu, macros: macroArray }
 * ※ macroArray の各要素は { path: macroPath, visible: int } で
 *    path は Mery.ini に記述されているままの相対パス
 *    visible は 数値 1 または 0
 */
function MacroMenu( showParent, trimExt, $ctrl ) {
  var start = new Date();
  var menu = CreatePopupMenu();
  var b3 = "\u2002\u2002\u2002";	// EN SPACE 「 」(U+2002)
  var d = editor.ActiveDocument;

  // Mery.ini のパスを取得
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  var WshShell = new ActiveXObject( "WScript.Shell" );
  var meryPath = editor.FullName;
  var meryDir = Fso.GetParentFolderName( meryPath ) + "\\";
  var meryName = Fso.GetBaseName( meryPath );
  var meryDirName = Fso.GetBaseName( meryDir );
  var iniPath = meryDir + meryName + ".ini";
  if ( ! Fso.FileExists( iniPath ) ) {
    iniPath = WshShell.SpecialFolders( "APPDATA" )
            + "\\Mery\\" + meryName + ".ini";
  }

  // Mery.ini からデータを取得
  var Adodb = new ActiveXObject( "ADODB.Stream" );
  var adTypeText = 2,  adReadAll = -1;
  Adodb.Type = adTypeText,  Adodb.Charset = "UTF-8";
  Adodb.Open();
  Adodb.LoadFromFile( iniPath );
  var iniText = Adodb.ReadText( adReadAll );
  Adodb.Close();

  // 「このマクロを実行」コマンドの準備
  var ext = d.Name.substr( d.Name.lastIndexOf( "\." ) + 1 );
  var reg2 = /^(?:jse?|pls?|php|pys?|rbs?|vb[es])$/i;
  var macroName = reg2.test( ext ) ? d.Name : "";
  var runFlag = ( macroName && d.Saved ) ? 0 : meMenuGrayed;

  // マクロのパスを抽出
  var reg3 = /\r\n\[Macros\\Macro\d+\]\r\nFileName=[^\r\n]+\r\nVisible=\d/g;
  // \r\n [Macros\Macro##] \r\n FileName=MacroPath \r\n Visible=#
  var macroArray = iniText.match( reg3 ) || [];
  var mcCount = macroArray.length;
  if ( mcCount ) {
    var invisible = false;
    var reg4 = /\r\nFileName=([^\r\n]+)\r\n/;
    for ( var i = 0; i < mcCount; i ++ ) {
      // path: MacroPath, visible: #int		※ path は相対パスの場合がある
      macroArray[i] = {
        path:    reg4.exec( macroArray[i] )[1],
        visible: + macroArray[i].slice( -1 )
      };
      if ( ! invisible && macroArray[i].visible == 0 ) { invisible = true; }
    }

    // マクロのファイル名一覧を作成 (非表示マクロをサブメニュー化)
    if ( invisible ) {
      menu.AddPopup( "非表示マクロ(&H)", subMenu = CreatePopupMenu() );
      menu.Add( "-----", 0, meMenuSeparator );
    }
    var reg5 = /(?:[^\\]+?\\)*?(?:([^\\]+)(?:\\))?([^\\]+)$/;
    var reg6 = /([^\/]*\/ )?(?:\d+[-._ ]+)?([^\/]+)(?:\.[^.]+?)$/;
    var id,  num,  label,  j = 0,  k = 0;
    var mcWidth = ( "" + mcCount ).length;
    for ( var i = 0; i < mcCount; i ++ ) {
      // "親フォルダ名/ ファイル名"
      label = ( showParent )
            ? macroArray[i].path.replace( reg5, "$1/ $2" )
            : /([^\\]+)$/.exec( macroArray[i].path )[1];
      if ( label.charAt( 0 ) == "/" ) {
        label = meryDirName + "/ " + label.slice( 1 );
      }
      // 起動モードでは ファイル名先頭の連番 と 拡張子 を削除
      // 編集モードでは拡張子を大文字に
      label = ( ( trimExt && ! $ctrl ) ? label.replace( reg6, "$1$2" )
              : ( $ctrl ) ? label.replace( /[^.\\]+$/, function( tmp ) {
                                  return tmp.toUpperCase(); } )
              : label
              ).replace( /&/g, "&&" );

      // 可視マクロと不可視マクロの振り分け (Visible= 1 or 0)
      id = i + 6144;
      if ( macroArray[i].visible == 1 ) {
        num = ( b3 + ( ++ j ) ).slice( - mcWidth ).replace( /\d$/, "&$&:\u2002" );
        menu.Add( num + label, id );
      }
      else {
        num = ( b3 + ( ++ k ) ).slice( - mcWidth ).replace( /\d$/, "&$&:\u2002" );
        subMenu.Add( num + label, id );
      }
    }
    menu.Add( "-----", 0, meMenuSeparator );
    if ( invisible ) {
      subMenu.Add( "-----", 0, meMenuSeparator );
      subMenu.Add( "キャンセル\t& ", 0 );
    }
    if ( gksEnable ) {
      menu.Add( b3 + "▲ 選択した"
                + ( $ctrl ? "ソースコードを編集" : "マクロを実行" )
                + " ▲", 0, meMenuGrayed );
      menu.Add( "-----", 0, meMenuSeparator );
    }
  }

  // 標準コマンド
  menu.Add( "編集(&E)  " + ScriptName, 2173 );
  menu.Add( "選択(&L)...", 2174 );
  menu.Add( "これを選択(&T)", 2175, ! d.Name *2 );
  if ( d.FullName != ScriptFullName ) {
    menu.Add( "このマクロを実行(&G)*  " + macroName, 1, runFlag );
  }
  menu.Add( "カスタマイズ(&C)...", 2176 );
  menu.Add( "-----", 0, meMenuSeparator );
  menu.Add( "Macros フォルダを開く(&F)*", 2 );
  menu.Add( "-----", 0, meMenuSeparator );
  menu.Add( "キャンセル\t& ", 0 );

  // ステータスを表示
  Status = " 「マクロメニュー」 "
         + "** " + ( $ctrl ? "編集" : "起動" ) + "モード **  "
         + j + " 件 + " + k + " 件  [ "
         + ( ( new Date() - start ) / 1000 ).toFixed( 3 ).replace( /\./, ". " )
         + " 秒 ]";
  return { menu: menu, macros: macroArray };
}


メモ

◆ 不具合など、お気づきの点がありましたらお知らせください ◆ (sukemaru)

  •  


スポンサーリンク