「ブックマーク一覧ジャンプ」の版間の差分

提供: MeryWiki
ナビゲーションに移動 検索に移動
Sukemaru (トーク | 投稿記録)
include版: スクリーンショットを追加
Sukemaru (トーク | 投稿記録)
sukemaru版(通常版/include版)を増補改訂
106行目: 106行目:
= sukemaru 版 =
= sukemaru 版 =


goat 版を元にして、ポップアップメニューを表示するまでの速度をすこしだけ改善しました。
goat 版を元にして、ポップアップメニューを表示するまでの速度を改善し、動作設定をカスタマイズできるようにしました。
 
<br>


;▼ goat 版からの変更点 ▼
;▼ goat 版からの変更点 ▼
115行目: 115行目:
* ポップアップメニューの体裁を変更(「[[ポップアップメニューで検索先にジャンプ#sukemaru_版|検索ジャンプ]]」マクロにあわせた)
* ポップアップメニューの体裁を変更(「[[ポップアップメニューで検索先にジャンプ#sukemaru_版|検索ジャンプ]]」マクロにあわせた)
* ステータスバーにブックマーク件数などの情報を表示
* ステータスバーにブックマーク件数などの情報を表示
* 「ブックマークがありません」メニュー(先頭行か最終行にジャンプ可)。
* 「ブックマークがありません」メニュー。
* ジャンプしたときのスクロール位置を調整。
* ジャンプしたときのスクロール位置を調整。
* ジャンプ先まで範囲選択するときの先頭/末尾を調整。
* ジャンプ先まで範囲選択するときの先頭/末尾を調整。
* 「先頭行/最終行 へジャンプ」をメニューにピン止め表示。
* 「先頭行/最終行 へジャンプ」をメニューにピン止め表示。
* 「ブックマークを設定/解除」をメニューの先頭にピン止め表示。
* ポップアップメニューだと読み取りづらい半角英小文字・と ascii 記号の一部を全角で表示。
<br>
[[ファイル:Mery_ブックマークジャンプ(通常版)_SS.png|frameless|640px|thumb|submenuenable = 2]]
<br clear=all>




Mery でプログラミング用のフォントを使っている人だと、ポップアップメニュー内で半角 '''¥''' 記号 (U+005C) が'''バックスラッシュ''' (U+005C) にならないのは気落ち悪いかも? ということで、ソースコード内の関数 MenuKey() のコードのコメントアウトしてある行を有効化すると "擬似的に" '''バックスラッシュ'''化できます <br> (※「∖」(U+2216) に置換して表示します)。
: Mery でプログラミング用のフォントを使っている人だと、ポップアップメニュー内で半角 <q>'''¥'''</q> 記号 (U+005C) が'''バックスラッシュ''' (U+005C) にならないのは気落ち悪いかも? ということで、ソースコード内の関数 MenuKey() のコードのコメントアウトしてある行を有効化すると "擬似的に" '''バックスラッシュ'''化できます <br> (※ <q>'''∖'''</q> (U+2216) に置換して表示します)。


== ダウンロード ==
== ダウンロード ==


'''ダウンロード:''' 「[[ファイル:ブックマークジャンプ.zip]]」(アイコン入り) 
'''ダウンロード:''' 「[[ファイル:ブックマークジャンプ.zip]]」(アイコン入り) 
<br> 最終更新: 2019/04/16(Mery 2.7.0 以降への対応と [[#include版|include版]] を追加)
<br> 最終更新: 2019/12/05(Mery 2.7.0 以降への対応と [[#include版|include版]] を追加)
: オマケとして [ 編集 ] メニューのブックマーク関連コマンドのアイコン化用マクロ×4




== ソースコード ==
== ソースコード ==


* このマクロではポップアップメニューの表示に EN SPACE「 」(U+2002) などを使用します。ファイルに保存するさいは、文字コードをシフト JIS にしないでください('''UTF-8''' や Unicode で保存する)。
* このマクロではポップアップメニューの表示に EN SPACE <q> </q> (U+2002) などを使用します。ファイルに保存するさいは、文字コードを shift_JIS にしないでください('''UTF-8''' や Unicode で保存する)。
<source lang="javascript" style="height:60em; overflow:auto;">
<source lang="javascript" style="height:80em; overflow:auto;" highlight="28-60">
#title = "ブックマーク一覧ジャンプ"
#title = "ブックマーク一覧ジャンプ"
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
#icon = "bookmark_list.ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243
// または 244


/* ---------------------------------------------------------
/**
* ---------------------------------------------------------
  * 「ブックマーク一覧ジャンプ」
  * 「ブックマーク一覧ジャンプ」
  *  Orginal created by: goat (Last update:  2012/09/24)
  *  Orginal created by: Kuro, goat (2012/06/22 - 2012/09/24)
  *  Modified by: sukemaru (2018/08/08 - 2019/04/16)
  *  Modified by:       sukemaru   (2018/08/08 - 2019/12/05)
  * --------------------------------------------------------- */
  * ---------------------------------------------------------
* ブックマーク一覧をポップアップメニューで表示し、選択した行へジャンプする。
*
* ・ 行番号は "表示行" / "論理行" の設定可。
* ・ ブックマーク行の検索 ~ ポップアップメニュー表示 の速度を改善。
* ・ ポップアップメニューの体裁を変更。
* ・ ステータスバーに情報を表示。
* ・「ブックマークがありません」メニュー。
* ・ ジャンプしたときのスクロール位置を調整。
* ・ ジャンプ先まで範囲選択するときの先頭/末尾を調整。
* ・「先頭行/行頭/最終行 へジャンプ」をメニューにピン止め表示
* ・「ブックマークを設定/解除」をメニューの先頭にピン止め表示
*
*/


var start = new Date(); // 所要時間計測(開始)
var start = new Date(); // 所要時間計測(開始)


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


// ■ ジャンプする前に選択範囲があったときはジャンプ先の行まで範囲選択する?
// ■ ジャンプする前に選択範囲があったときはジャンプ先の行まで範囲選択する?
var flgBlockEnable = false; // (true 範囲選択する / false 範囲選択しない)
var blockSelectEnable = false; // (true 範囲選択する / false 範囲選択しない)


  // ■ 下の行にジャンプしたとき、ジャンプ先の行全体(論理行)を選択範囲に含める?
    // ■ もとの選択範囲をジャンプ後の範囲選択に含める?
  var endOfBookmarkLine = false; // (true 含める / false 含めない)
    var keepInitialSelect = false; // (true 含める / false 含めない)


// ■ 行の表示方法を Mery.ini から「自動」で読み込むか?
    // ■ 下の行にジャンプしたとき、ジャンプ先の行全体(論理行)を選択範囲に含める?
var autoLineModeEnable = false; // (true 読み込む / false 読み込まない)
    var endOfBookmarkLine = false; // (true 含める / false 含めない)


  // ■ 行の表示方法(手動設定用)
// ■ 行番号の表示方法を Mery.ini から「自動」で読み込む?
  var lineColumnView = 0; // ( 0 論理行 / 1 表示行 )
var autoLineModeEnable = false; // (true 自動読み込み / false 手動設定)
 
    // ■ 行番号の表示方法(手動設定用)
    var lineColumnView = 0; // ( 0 論理行 / 1 表示行 )


// ■ 「ブックマークがありません」メニューを表示する?
// ■ 「ブックマークがありません」メニューを表示する?
var extraMenuEnable = true; // (true する / false しない)
var extraMenuEnable = true; // (true 表示する / false 表示しない)


// ■ ポップアップメニューに「先頭行/最終行 へジャンプ」をピン止め表示する?
// ■ ポップアップメニューに「先頭行/最終行 へジャンプ」をピン止め表示する?
var menuPinEnable = true; // (true する / false しない)
var menuPinEnable = true; // (true 表示する / false 表示しない)


// ■ ポップアップメニューに表示する文字数
// ■ ポップアップメニューに表示する文字数の目安
var menuWidth = 65; // (全角/半角の区別なし)
var menuWidth = 65; // (全角/半角の区別なし)


// ---------- ▲ 設定項目 ▲ ----------
// ■ 半角英文字を全角で表示する [!"%'(),.:;@\[\]`a-z{|}]
var toFullWidth = true; // (true 置換する / false 置換しない)
 
// ■ ポップアップメニューを表示する位置
var menuPosMouse = true; // ( true マウス位置 / false カーソル位置 )


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


// Mery.ini から「行の表示方法」を取得する( 表示行 = 1 / 論理行 = 0 )
// Mery.ini から「行の表示方法」を取得する( 表示行 = 1 / 論理行 = 0 )
184行目: 212行目:
var linesWidth = lines.toString().length;
var linesWidth = lines.toString().length;
// カーソルと選択範囲の位置を保存
// カーソルと選択範囲の位置を保存
var s = editor.ActiveDocument.selection;
var s = d.selection;
var act = s.GetActivePos(),  anc = s.GetAnchorPos();
var aY = s.GetActivePointY( posMode );
var aY = s.GetActivePointY( posMode );
var act = s.GetActivePos();
var anc = s.GetAnchorPos();
var sX = ScrollX,  sY = ScrollY;
var sX = ScrollX,  sY = ScrollY;
// ブックマーク行の変数
// ブックマーク行の変数
var bmY;
var bmArray = [],  flag = 0,  isBookmarked = 0;
var bmStr;
var bmY, bmStr;
var bmArray = [];


// ブックマーク行を検索
// ブックマーク行を検索
Redraw = false;
Redraw = false;
// 先頭行がブックマークされているか
s.SetActivePoint( mePosLogical, 1, 2 );
s.SetActivePoint( mePosLogical, 1, 2 );
if ( s.PreviousBookmark() ) {
if ( s.PreviousBookmark() ) {
   bmY = s.GetActivePointY( posMode );
   bmY = s.GetActivePointY( posMode );
   bmStr = d.GetLine( bmY, lineMode );
   bmStr = d.GetLine( bmY, lineMode );
   bmArray.push( [ MenuKey( bmStr, bmY ), bmY ] );
  if ( bmY == aY ) { flag = 1; isBookmarked = 1; }
   bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
}
}
// 2行目以降のブックマークを探す
s.SetActivePos( 0 );
s.SetActivePos( 0 );
while ( s.NextBookmark() ) {
while ( s.NextBookmark() ) {
   bmY = s.GetActivePointY( posMode );
   bmY = s.GetActivePointY( posMode );
   bmStr = d.GetLine( bmY, lineMode );
   bmStr = d.GetLine( bmY, lineMode );
   bmArray.push( [ MenuKey( bmStr, bmY ), bmY ] );
  if ( bmY == aY ) { flag = 1; isBookmarked = 1; }
  else { flag = 0; }
   bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
}
}
// ブックマークの件数
// ブックマークの件数
215行目: 246行目:
s.SetAnchorPos( anc );
s.SetAnchorPos( anc );
ScrollX = sX,  ScrollY = sY;
ScrollX = sX,  ScrollY = sY;
Redraw = true;
// ステータスラベル
var status1 = ( bmCount )
            ? " ブックマーク: " + bmCount + " 件 /"
            : " ブックマークがありません。";
var status2 = " 全体の行数: "
            + lines.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )
            + ( lineColumnView ? " 行 (表示行)" : " 行 (論理行)" );


// ポップアップメニューの準備
// ポップアップメニューの準備
var bmMenu = CreatePopupMenu();
var bmMenu = CreatePopupMenu();
var Check = function( num ) {
// アクティブ行のチェックマーク (meMenuChecked)
  return num == aY ? meMenuChecked : 0;
var Check = function( num ) { return ( num == aY ) *1; }
}
 
// ピン止めアイテム
var bmPin = ( "          " + aY ).slice( - linesWidth )
          + ":  ブックマークを設定/解除 (&B)";
if ( bmCount ) {
if ( bmCount ) {
  var countPin = ( "          " ).slice( - linesWidth )
              + " ▼ " + status1.slice( 0, -1 ) + " ▼";
   var cancelPin = ( "          " ).slice( - linesWidth )
   var cancelPin = ( "          " ).slice( - linesWidth )
                 + "  キャンセル\t& ";
                 + "  キャンセル\t& ";
233行目: 278行目:
             + ":     行頭へジャンプ (&A)";
             + ":     行頭へジャンプ (&A)";
}
}
// ステータスラベル
 
var bmStatus1 = bmCount
// 「ブックマークを設定/解除」をピン止め
              ? " ブックマーク: " + bmCount + " 件 /"
if ( bmCount || extraMenuEnable ) {
              : " ブックマークがありません。";
  bmMenu.Add( bmPin, 20000000, isBookmarked );
var bmStatus2 = " 全体の行数: "
  bmMenu.Add( "-----", 0, meMenuSeparator );
              + lines.toString()
}
                    .replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )  
              + ( lineColumnView ? " 行 (表示行)"
                                : " 行 (論理行)" );


// 「ブックマーク一覧」ポップアップメニュー
// 「ブックマーク一覧」ポップアップメニュー
if ( bmCount ) {
if ( bmCount ) {
   bmMenu.Add( cancelPin, 0 );
  // メニュー上部のピン止めアイテム
   bmMenu.Add( cancelPin, 0 ); // 「キャンセル」
  bmMenu.Add( "-----", 0, meMenuSeparator );
   if ( menuPinEnable && bmArray[0][1] > 1 ) {
   if ( menuPinEnable && bmArray[0][1] > 1 ) {
    bmMenu.Add( "", 0, meMenuSeparator );
     bmMenu.Add( topPin, 1, Check( 1 ) ); // 「先頭行へジャンプ」
     bmMenu.Add( topPin, 1, Check( 1 ) );
   }
   }
  if ( ! isBookmarked && aY > 1 && aY < lines ) {
    bmMenu.Add( actPin, aY, meMenuChecked ); // 「行頭へジャンプ」
  }
  bmMenu.Add( "-----", 0, meMenuSeparator );
  bmMenu.Add( countPin, 0, 0 ); // ▼ ブックマーク: n 件 ▼
  // ブックマーク行をポップアップメニューに追加
   for ( var i = 0, flags; i < bmCount; i ++ ) {
   for ( var i = 0, flags; i < bmCount; i ++ ) {
     if ( i % 10 == 0 ) {
     if ( i != 0 && i % 10 == 0 ) {
       bmMenu.Add( "", 0, meMenuSeparator );
       bmMenu.Add( "-----", 0, meMenuSeparator ); // 10行ごとにセパレータ
     }
     }
     bmMenu.Add( bmArray[i][0], bmArray[i][1], Check( bmArray[i][1] ) );
     bmMenu.Add( bmArray[i][0], bmArray[i][1], Check( bmArray[i][1] ) );
   }
   }
  // メニュー下部のピン止めアイテム
   if ( menuPinEnable && bmArray[ bmCount - 1 ][1] !== lines ) {
   if ( menuPinEnable && bmArray[ bmCount - 1 ][1] !== lines ) {
     bmMenu.Add( "", 0, meMenuSeparator );
     bmMenu.Add( "-----", 0, meMenuSeparator );
     bmMenu.Add( endPin, lines, Check( lines ) );
     bmMenu.Add( endPin, lines, Check( lines ) ); // 「最終行へジャンプ」
   }
   }
}
}
// 「ブックマークがありません」ポップアップメニュー
// 「ブックマークがありません」ポップアップメニュー
else if ( extraMenuEnable ) {
else if ( extraMenuEnable ) {
   bmMenu.Add( "※" + bmStatus1 + "& ※", 0 );
   bmMenu.Add( "※" + status1 + "& ※", 0 );
   if ( ! menuPinEnable ) {
   if ( ! menuPinEnable ) {
     bmMenu.Add( bmStatus2 + "& ", 0 );
     bmMenu.Add( status2 + "& ", 0 );
   }
   }
  // ピン止めアイテム
   else if ( lines > 1 ) {
   else if ( lines > 1 ) {
     bmMenu.Add( "", 0, meMenuSeparator );
     bmMenu.Add( "-----", 0, meMenuSeparator );
     bmMenu.Add( topPin, 1, Check( 1 ) );
     bmMenu.Add( topPin, 1, Check( 1 ) ); // 「先頭行へジャンプ」
     bmMenu.Add( endPin, lines, Check( lines ) );
    if ( aY > 1 && aY < lines ) {
      bmMenu.Add( actPin, aY, meMenuChecked ); // 「行頭へジャンプ」
    }
     bmMenu.Add( endPin, lines, Check( lines ) );// 「最終行へジャンプ」
   }
   }
   else if ( lines == 1 ) {
   else if ( lines == 1 ) {
     bmMenu.Add( actPin, aY, meMenuChecked );
     bmMenu.Add( actPin, aY, meMenuChecked ); // 「行頭へジャンプ」
   }
   }
}
}
// ステータスバーとポップアップメニューを表示
// ステータスバーとポップアップメニューを表示
Status = bmStatus1 + bmStatus2
Status = status1 + status2 + TimerStatus( new Date(), start );
      + TimerElapsed( new Date(), start );
var y = bmMenu.Track( menuPosMouse );
var y = bmMenu.Track( mePosMouse );


// ブックマークを設定/解除
if ( y == 20000000 ) {
  editor.ExecuteCommandByID( MEID_EDIT_TOGGLE_BOOKMARK = 2126 );
}
// ジャンプ
// ジャンプ
if ( y > 0 ) {
else if ( y > 0 ) {
   if ( y == aY ) {
   Redraw = false;
    s.StartOfDocument();
    s.EndOfDocument();
  }
   s.SetActivePoint( posMode, 1, y );
   s.SetActivePoint( posMode, 1, y );


   // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択
   // アクティブ行へのジャンプではスクロール位置をリセット
   if ( flgBlockEnable && act !== anc ) {
   if ( y == aY ) { ScrollY = aY; }
    var tp = ( anc < act ) ? anc : act;
  // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択する
    var bp = ( anc < act ) ? act : anc;
  if ( blockSelectEnable && act !== anc ) {
     var jumpDown = ( aY < y );
     var jumpDown = ( aY < y );
     s.SetAnchorPos( jumpDown ? tp : bp );
     // もとの選択範囲の文字列を含んだままにする
    if ( keepInitialSelect ) {
      var tp = Math.min( anc, act );
      var bp = Math.max( anc, act );
      s.SetAnchorPos( jumpDown ? tp : bp );
    }
    // または、もとの "キャレット位置" を範囲選択の基点にする
    else { s.SetAnchorPos( act ); }
    // 下の行へジャンプしたとき、ジャンプ先の行全体を選択範囲に含める
     if ( jumpDown && endOfBookmarkLine ) {
     if ( jumpDown && endOfBookmarkLine ) {
       s.EndOfLine( true, mePosLogical );
       s.EndOfLine( true, mePosLogical );
       ScrollX = 1;
       ScrollX = 1; // 水平スクロールを左端にリセット
     }
     }
   }
   }
   ScrollY = ( ScrollY == sY )
   ScrollY = ( ScrollY == sY )
           ? sY : s.GetActivePointY( mePosView );
           ? sY : s.GetActivePointY( mePosView );
  Redraw = true;
}
}
Redraw = true;


// ---------- ▼ 関数 ▼ ----------
// ---------- ▼ 関数 ▼ ---------- //


/**
/**
311行目: 375行目:
  * ポップアップメニューに表示するラベルを生成する
  * ポップアップメニューに表示するラベルを生成する
  *  
  *  
  * ・行頭空白を除去
  * ・行頭空白を除去、空白文字を圧縮
  * ・空白文字を圧縮
  * ・「¥」(U+005C) を「∖」に置換: 「∖」(U+2216) または 「╲」(U+2572) 「﹨」(U+FE68)
  * ・削られてしまう「&」を補完
  * ・「&」 を補完
  * ・判別しづらいメタ文字を全角に置換
  * ・「a-z」 と判別しづらいメタ文字を全角に置換  →  条件つきに変更
  * ・文字数を切り詰め
  * ・文字数を切り詰め
  * ・行番号をケタ埋め: EN SPACE「 」(U+2002)
  * ・行番号をケタ埋め: EN SPACE 「 」(U+2002)
*  ▼ 2019/03/30 追加 ▼
* ・「¥」(U+005C) を「∖」に置換:「∖」(U+2216) または「╲」(U+2572) 「﹨」(U+FE68)
* ・「a-z」を全角に置換
  */
  */
function MenuKey( str, num ) {
function MenuKey( str, num ) {
   var menuKey = str.replace( /^[\t  ]+/, "" )
   var menuKey = ( str ) ? /^[\t  ]+$/.test( str ) ? "( 空白行 )" : str
                        : "( 空行 )";
  menuKey = menuKey.replace( /^[\t  ]+/, "" )
                   .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " )
                   .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " )
                   .replace( /&/g, "&&" )
                   .slice( 0, menuWidth *2 )
                 // .replace( /[\\]/g, "∖" )
                 // .replace( /[\\]/g, "∖" )
                // .replace( /([^∖!"',.:;`|\t  ])([∖]+)/g, "$1 $2" )
                  .replace( /&/g, "&&" );
                  .replace( /[!"%'(),.:;@\[\]`a-z{|}]/g,
  if ( toFullWidth ) {
                    function( $0 ) {
    menuKey = menuKey.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g, function( tmp ) {
                      return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
        return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
                    } );
    } );
  }
   menuKey = ( menuKey.length > menuWidth )
   menuKey = ( menuKey.length > menuWidth )
           ? menuKey.slice( 0, menuWidth ) + " ..."
           ? menuKey.slice( 0, menuWidth ) + " ..." : menuKey;
          : menuKey;
   return ( "          &" + num ).slice( - linesWidth - 1 ) + ":  " + menuKey;
   return ( "          &" + num ).slice( - linesWidth - 1 ) + ":  " + menuKey;
}
}


/**
/**
  * 関数 TimerElapsed( end, start )
  * 関数 TimerStatus( end, start )
  * start からの経過時間計測
  * start からの経過時間計測
  * [ s.sss 秒 ] で返す
  * [ s. sss 秒 ] で返す
  */
  */
function TimerElapsed( end, start ) {
function TimerStatus( end, start ) {
   var elapsed = ( end - start ) / 1000;
   var elapsedSec = ( ( end - start ) / 1000 ).toFixed( 3 );
   return " [ "
   return " [ " + elapsedSec.replace( /\./, ". " ) + " 秒 ]";
        + ( elapsed + "000" ).replace( /(\.)(\d{3})(0*)/, "$1 $2" )
        + " 秒 ]";
}
}


/**
/**
  * 関数 GetIniOptionNum( key )
  * 関数 GetIniOptionNum( key )
  * 引数で指定された設定項目の「値」を返す(※数値1ケタ)
  * 引数で指定された設定項目の「値」を返す(※数値のみ)
  */
  */
function GetIniOptionNum( key ) {
function GetIniOptionNum( key ) {
  var regIniKey = new RegExp( "^" + key + "=\\d+$", "m" );
   var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
   var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
   // Mery.ini を探す
   // Mery.ini を探す
   var iniPath = editor.FullName.replace( /\.exe$/i, ".ini" );
   var meryPath = editor.FullName;
  var mery    = Fso.GetBaseName( meryPath );
  var iniPath  = meryPath.replace( /\.exe$/i, ".ini" );
   if ( ! Fso.FileExists( iniPath ) ) {
   if ( ! Fso.FileExists( iniPath ) ) {
     var WshShell = new ActiveXObject( "WScript.Shell" );
     var WshShell = new ActiveXObject( "WScript.Shell" );
     iniPath = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
     iniPath = WshShell.SpecialFolders( "APPDATA" )
             + "\\Mery\\Mery.ini";
             + "\\Mery\\" + mery + ".ini";
   }
   }
   // Mery.ini を読み込む
   // Mery.ini を読み込む
367行目: 429行目:
   var iniText = iniFile.ReadAll();
   var iniText = iniFile.ReadAll();
   iniFile.Close();
   iniFile.Close();
   // 項目の値を取得する
   // 項目の値を返す
   var dist = + iniText.match( regIniKey )
   return + RegExp( "^" + key + "=(\\d+)$", "m" ).exec( iniText )[1];
                      .shift()
                      .replace( /^.+=(\d+)$/, "$1" );
  return dist;
}
}
/**
* ---------- ▼ 更新履歴 ▼ ----------
* 2019/03/29:
* ・sukemaru版をマクロライブラリに投稿。
* 2019/04/08:
* ・Quit() の削除と、軽微な変更。
* 2019/04/16:
* ・Mery 2.7.0 でのブックマークの仕様変更に対応。【include版】を追加。
* 2019/12/05:
* ・「ブックマークを設定/解除」 をメニューに追加
* ・メニュー内にブックマーク件数を表示
* ・「メニューを表示する位置」 の設定を追加
* ・「もとの選択範囲をジャンプ後の範囲選択に含める」 を設定項目に追加
* ・【include版】を大幅に変更。
*/
</source>
</source>


378行目: 453行目:
== include版 ==
== include版 ==


[[ダウンロード|ZIP 書庫]]内の「'''include版'''」フォルダの JS ファイルを使用すると、「[[ポップアップメニューで検索先にジャンプ#include版|ポップアップメニューで検索先にジャンプ]]」マクロと同様に、<span style="color:#c00;">マクロの動作設定の変更をポップアップメニュー内の項目のチェック ON/OFF でできるようになります。 </span>
*[[ダウンロード|ZIP 書庫]]内の「'''include版'''」フォルダの JS ファイルを使用すると、「[[ポップアップメニューで検索先にジャンプ#include版|ポップアップメニューで検索先にジャンプ]]」マクロと同様に、<span style="color:#c00;">マクロの動作設定の変更をポップアップメニュー内の項目のチェック ON/OFF でできるようになります。 </span>
 
<br>
[[ファイル:Mery_ブックマークジャンプ_SS(1).png|link=https://www.haijin-boys.com/wiki/images/9/96/Mery_%E3%83%96%E3%83%83%E3%82%AF%E3%83%9E%E3%83%BC%E3%82%AF%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%97_SS.png|スクリーンショット]]<br><br>
[[ファイル:Mery_ブックマークジャンプ_SS(1).png|link=https://www.haijin-boys.com/wiki/images/9/96/Mery_%E3%83%96%E3%83%83%E3%82%AF%E3%83%9E%E3%83%BC%E3%82%AF%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%97_SS.png|設定変更サブメニュー]]
[[includeライブラリ]] を使用し、設定内容は外部ファイルに保存します(◆settingEnable 変数で設定。デフォルト: 有効)。
<br clear=all><br>
[[ファイル:Mery_ブックマークジャンプ_SS(2).png|frameless|254px|thumb|right|分割サブメニュー]]
*ブックマークの件数が多いときはサブメニューに分割表示します。
:初期値では 20 件超で分割サブメニューを表示する設定にしてありますが、モニターの高さに合わせて分割単位を変更することができます(設定変更サブメニューで変更可)。
<br clear=all><br>


*[[includeライブラリ]] を使用し、設定内容は外部ファイルに保存します(◆settingEnable 変数で設定。デフォルト: 有効)。<br>
<div id="注1" class="warningbox">
<div id="注1" class="warningbox">
* あらかじめ [[includeライブラリ]] をインストールしてください。
* あらかじめ [[includeライブラリ]] をインストールしてください。
* 設定内容の保存場所は '''Mery\Macros\MacroSettings\<ブックマーク一覧ジャンプ>.json''' <br> または '''%AppData%\Mery\MacroSettings\<ジャンプ>.jso'''n です。<br> (※ <ブックマーク一覧ジャンプ> の部分はこのマクロのファイル名)
* 設定内容の保存場所は '''Mery\Macros\MacroSettings\<ブックマーク一覧ジャンプ>.json''' <br> または '''%AppData%\Mery\MacroSettings\<ジャンプ>.json''' です。<br> (※ <ブックマーク一覧ジャンプ> の部分はこのマクロのファイル名)
</div>
</div>
 
: 「▼ 通常版 設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。
* 「▼ 通常版 設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。<br> ただし、settingEnable 変数を完全に無効にする場合は、ソースコード内で変更する必要があります。<br> → settingEnable を無効にした場合は「通常版」と同じ状態になり、「設定変更」サブメニューが表示されなくなります。
: ※ ただし、settingEnable 変数を完全に無効にする場合は、ソースコード内で変更する必要があります。<br> → settingEnable を無効にした場合は「通常版」と同じ状態になり、「設定変更」サブメニューが表示されなくなります。




<source lang="javascript" style="height:60em; overflow:auto;">
<source lang="javascript" style="height:80em; overflow:auto;">
#title = "ブックマーク一覧ジャンプ"
#title = "ブックマークジャンプ"
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
#include "include/IO.js"
#icon = "bookmark_list.ico"
#icon = "bookmark_list.ico"
#include "include/IO.js"
#include "include/MeryInfo.js"
// ▲ includeライブラリを導入していない場合は #include の2行をコメントアウトすること▲
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243


403行目: 481行目:
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 「ブックマーク一覧ジャンプ」
  * 「ブックマーク一覧ジャンプ」
  *  Orginal created by: goat (Last update:  2012/09/24)
  *  Orginal created by: Kuro, goat (2012/06/22 - 2012/09/24)
  *  Modified by: sukemaru     (2018/08/08 - 2019/04/15)
  *  Modified by:       sukemaru   (2018/08/08 - 2019/12/05)
  *  【include版】              (2019/04/15 - )  
  *  【include 版】                  (2019/04/15 - 2019/12/05)  
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 【include版】
  * 【include 版】
  * ・「設定変更」サブメニューを表示
  * ・「設定変更」サブメニューを表示
  * ・ 設定内容を外部ファイル(JSON)に保存
  * ・ 設定内容を外部ファイル(JSON)に保存
* ・ ブックマーク件数が多いときはサブメニューに分割表示
*
*    設定ファイルの保存場所は
*      Mery\Macros\MacroSettings\<jsonName>.json
*    または
*      %AppData%\Mery\MacroSettings\<jsonName>.json
*    ※ <jsonName> の部分はこのマクロのファイル名(設定項目で変更可)
  */
  */


var start = new Date(); // 所要時間計測(開始)
var start = new Date(); // 所要時間計測(開始)


// ---------- ▼ 【include版】 初期設定項目 ▼ ----------
var setting = {};
// ---------- ▼ 【include版】 初期設定項目 ▼ ---------- //


// ◆ 「設定変更」サブメニューを表示するか?
// ◆ 「設定変更」サブメニューを表示するか?
//(includeライブラリを導入していない場合は false にすること)
setting.settingEnable = true; // (true する / false しない)
var settingEnable = true; // (true する / false しない)


// ◆ JSON ファイルのベース名
// ◆ JSON ファイルのベース名
426行目: 511行目:
// jsonName = "ブックマーク一覧ジャンプ";
// jsonName = "ブックマーク一覧ジャンプ";


// ---------- ▼ 通常版 設定項目 ▼ ----------
// ---------- ▼ 通常版 設定項目 ▼ ---------- //
// 【include版】ではポップアップメニューから変更すること
 
setting.blockSelectEnable = false;
setting.keepInitialSelect = false;
setting.endOfBookmarkLine = false;
setting.autoLineModeEnable = false;
setting.lineColumnView = 0; // ( 0 論理行 / 1 表示行 )
setting.extraMenuEnable = true;
setting.menuPinEnable = true;
setting.menuScrollEnable = true; // スペースキーでスクロール
setting.subMenuEnable = true; // 分割サブメニュー
setting.subMenuHeight = 20; // 初期値 = 20; (20 件* ずつに分割)
setting.menuWidth = 55; // (30-100 程度 ※半角文字だけの行では×2)
setting.toFullWidth = false; // [!"%'(),.:;@\[\]`a-z{|}]
setting.menuPosMouse = true; // ( true マウス位置 / false カーソル位置 )


// 【include版】ではポップアップメニューから変更すること
// ---------- ▲ 設定項目 ▲ ---------- //
var flgBlockEnable = false;
var endOfBookmarkLine = false;
var autoLineModeEnable = false;
var lineColumnView = 0;
var extraMenuEnable = true;
var menuPinEnable = true;
var menuWidth = 65;


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


// menuWidth = ( menuWidth < 30 )  ? 25
//          : ( menuWidth > 100 ) ? 105
//                                : menuWidth;


/* ▼ 【include版】の追加コード ▼ */  
// ソースコードの「設定項目」内の初期値を保存する
var initialSettings = { settingEnable: false };
var initialSettings = setting;
if ( settingEnable ) {
if ( initialSettings.settingEnable ) {
  // ソースコードの「設定項目」内の初期値を保存する
  var setting = {
    settingEnable: settingEnable,
    flgBlockEnable: flgBlockEnable,
    endOfBookmarkLine: endOfBookmarkLine,
    extraMenuEnable: extraMenuEnable,
    menuPinEnable: menuPinEnable,
    autoLineModeEnable: autoLineModeEnable,
    lineColumnView: lineColumnView,
    menuWidth: menuWidth
  };
  // 「設定変更」メニューの表示許可や JSON の初期化に使用する初期設定
  initialSettings = setting;
   // JSON ファイルから設定を読み込む
   // JSON ファイルから設定を読み込む
   jsonName = jsonName || ScriptName.replace( /\.js$/i, "" );
   jsonName = jsonName || ScriptName.replace( /\.js$/i, "" );
   setting = IO.Deserialize( setting, jsonName );
   setting = IO.Deserialize( setting, jsonName );
   // 通常版の設定変数に再代入
   // 通常版の設定変数に再代入
   settingEnable = setting.settingEnable;
   var settingEnable = setting.settingEnable;
   flgBlockEnable = setting.flgBlockEnable;
   var blockSelectEnable = setting.blockSelectEnable;
   endOfBookmarkLine = setting.endOfBookmarkLine;
   var keepInitialSelect = setting.keepInitialSelect;
   extraMenuEnable = setting.extraMenuEnable;
  var endOfBookmarkLine = setting.endOfBookmarkLine;
   menuPinEnable = setting.menuPinEnable;
   var extraMenuEnable = setting.extraMenuEnable;
   autoLineModeEnable = setting.autoLineModeEnable;
   var menuPinEnable = setting.menuPinEnable;
   lineColumnView = setting.lineColumnView;
   var menuScrollEnable = setting.menuScrollEnable;
   menuWidth = setting.menuWidth;
  var autoLineModeEnable = setting.autoLineModeEnable;
   var lineColumnView = setting.lineColumnView;
   var subMenuEnable = setting.subMenuEnable;
  var subMenuHeight = setting.subMenuHeight;
  var menuWidth = setting.menuWidth;
  var toFullWidth = setting.toFullWidth;
  var menuPosMouse = setting.menuPosMouse;
}
}
/* ▲ 【include版】の追加コード ▲ */


// Mery.ini から「行の表示方法」を取得する( 表示行 = 1 / 論理行 = 0 )
do {
if ( autoLineModeEnable ) {
  if ( y ) { start = new Date(); } // ループしたときはタイマーをリセット
  lineColumnView = GetIniOptionNum( "LineColumnView" );
 
}
  // Mery.ini から「行の表示方法」を取得する
// 表示行/論理行 の定数
  if ( autoLineModeEnable ) {
var posMode = lineColumnView ? mePosView : mePosLogical;
    lineColumnView = GetIniOptionNum( "LineColumnView" );
var lineMode = lineColumnView ? meGetLineView : 0;
  }
// 全体の行数
  // 表示行/論理行 の定数
var d = editor.ActiveDocument;
  var posMode = lineColumnView ? mePosView : mePosLogical;
var lines = d.GetLines( lineMode );
  var lineMode = lineColumnView ? meGetLineView : 0;
var linesWidth = lines.toString().length;
  // 全体の行数
// カーソルと選択範囲の位置を保存
  var d = editor.ActiveDocument;
var s = editor.ActiveDocument.selection;
  var lines = d.GetLines( lineMode );
var aY = s.GetActivePointY( posMode );
  var linesWidth = lines.toString().length;
var act = s.GetActivePos();
  // カーソルと選択範囲の位置を保存
var anc = s.GetAnchorPos();
  var s = d.selection;
var sX = ScrollX, sY = ScrollY;
  var act = s.GetActivePos(),  anc = s.GetAnchorPos();
var isBookmarked = false;
  var aY = s.GetActivePointY( posMode );
// ブックマーク行の変数
  var sX = ScrollX, sY = ScrollY;
var bmY;
  // ブックマーク行の変数
var bmStr;
  var bmArray = [],  flag = 0,  isBookmarked = 0;
var bmArray = [];
  var bmY,  bmStr;


// ブックマーク行を検索
  // ブックマーク行を検索
Redraw = false;
  Redraw = false;
// アクティブ行がブックマークされているか
   // 先頭行がブックマークされているか
s.SetActivePoint( mePosLogical, 1, aY + 1 );
  s.SetActivePoint( mePosLogical, 1, 2 );
if ( s.PreviousBookmark()
  if ( s.PreviousBookmark() ) {
    && s.GetActivePointY( posMode ) == aY ) {
    bmY = s.GetActivePointY( posMode );
   isBookmarked = true;
    bmStr = d.GetLine( bmY, lineMode );
}
    if ( bmY == aY ) { flag = 1;  isBookmarked = 1; }
// 先頭行がブックマークされているか
    bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
s.SetActivePoint( mePosLogical, 1, 2 );
  }
if ( s.PreviousBookmark() ) {
  // 2行目以降のブックマークを探す
  bmY = s.GetActivePointY( posMode );
  s.SetActivePos( 0 );
  bmStr = d.GetLine( bmY, lineMode );
  while ( s.NextBookmark() ) {
  bmArray.push( [ MenuKey( bmStr, bmY ), bmY ] );
    bmY = s.GetActivePointY( posMode );
}
    bmStr = d.GetLine( bmY, lineMode );
// 2行目以降のブックマークを探す
    if ( bmY == aY ) { flag = 1;  isBookmarked = 1; }
s.SetActivePos( 0 );
    else { flag = 0; }
while ( s.NextBookmark() ) {
    bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
  bmY = s.GetActivePointY( posMode );
  }
  bmStr = d.GetLine( bmY, lineMode );
  // ブックマークの件数
  bmArray.push( [ MenuKey( bmStr, bmY ), bmY ] );
  var bmCount = bmArray.length;
}
// ブックマークの件数
var bmCount = bmArray.length;


// カーソル位置と選択範囲を復帰
  // カーソル位置と選択範囲を復帰
s.SetActivePos( act );
  s.SetActivePos( act ); s.SetAnchorPos( anc );
s.SetAnchorPos( anc );
  ScrollX = sX; ScrollY = sY;
ScrollX = sX, ScrollY = sY;
  Redraw = true;


// ポップアップメニューの準備
   // ステータスラベル
var bmMenu = CreatePopupMenu();
  var status1 = bmCount
var Check = function( num ) {
  return num == aY ? meMenuChecked : 0;
}
if ( bmCount ) {
  var cancelPin = ( "          " ).slice( - linesWidth )
                + "  キャンセル\t& ";
}
if ( menuPinEnable ) {
  var topPin = ( "          " + 1 ).slice( - linesWidth )
            + ":  ** 先頭行へジャンプ (&T) **";
   var endPin = ( "          " + lines ).slice( - linesWidth )
            + ":  ** 最終行へジャンプ (&E) **";
  var actPin = ( "          " + aY ).slice( - linesWidth )
            + ":     行頭へジャンプ (&A)";
}
// ステータスラベル
var bmStatus1 = bmCount
               ? " ブックマーク: " + bmCount + " 件 /"
               ? " ブックマーク: " + bmCount + " 件 /"
               : " ブックマークがありません。";
               : " ブックマークがありません。";
var bmStatus2 = " 全体の行数: "  
  var status2 = " 全体の行数: "
               + lines.toString()
               + lines.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )
                    .replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )  
               + ( lineColumnView ? " 行 (表示行)" : " 行 (論理行)" );
               + ( lineColumnView ? " 行 (表示行)"
 
                                : " 行 (論理行)" );
  // ポップアップメニューの準備
  var bmMenu = CreatePopupMenu();
 
  // 「ブックマーク一覧」ポップアップメニュー
  // または「ブックマークがありません」ポップアップメニュー
  BookmarkMenu( bmMenu, bmArray, aY );


/* 【include版】の設定変更サブメニュー */  
  // ステータスバーとポップアップメニューを表示
if ( initialSettings.settingEnable
  Status = status1 + status2
     && ( bmCount || extraMenuEnable ) ) {
        + TimerElapsed( new Date(), start );
   SettingsMenu( bmMenu );
  var y = bmMenu.Track( menuPosMouse );
 
  /* 【include版】の設定変更サブメニュー項目を選択した場合 */
  if ( y >= 10000000 && y <= 10000018 ) {
     SettingsChange( y );
  }
// 設定変更した場合はメニューを再表示する
} while ( ! menuPosMouse && y >= 10000000 && y <= 10000015 );
 
// ブックマークを設定/解除
if ( y == 20000000 ) {
   editor.ExecuteCommandByID( MEID_EDIT_TOGGLE_BOOKMARK = 2126 );
}
}


// 「ブックマーク一覧」ポップアップメニュー
// ジャンプ
if ( bmCount ) {
else if ( y && y < 10000000 ) {
   bmMenu.Add( cancelPin, 0 );
   Redraw = false;
   if ( menuPinEnable && ( bmArray[0][1] > 1
  s.SetActivePoint( posMode, 1, y );
      || ! isBookmarked && aY > 1 && aY < lines ) ) {
 
     bmMenu.Add( "", 0, meMenuSeparator );
  // アクティブ行へのジャンプではスクロール位置をリセット
     if ( bmArray[0][1] > 1 ) {
  if ( y == aY ) { ScrollY = aY; }
       bmMenu.Add( topPin, 1, Check( 1 ) );
  // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択する
   if ( blockSelectEnable && act !== anc ) {
     var jumpDown = ( aY < y );
    // もとの選択範囲の文字列を含んだままにする
     if ( keepInitialSelect ) {
       var tp = Math.min( anc, act );
      var bp = Math.max( anc, act );
      s.SetAnchorPos( jumpDown ? tp : bp );
     }
     }
     if ( ! isBookmarked && aY > 1 && aY < lines ) {
    // または、もとの "キャレット位置" を範囲選択の基点にする
       bmMenu.Add( actPin, aY, meMenuChecked );
    else { s.SetAnchorPos( act ); }
    // 下の行へジャンプしたとき、ジャンプ先の行全体を選択範囲に含める
     if ( jumpDown && endOfBookmarkLine ) {
       s.EndOfLine( true, mePosLogical );
      ScrollX = 1; // 水平スクロールを左端にリセット
     }
     }
   }
   }
   for ( var i = 0, flags; i < bmCount; i ++ ) {
   ScrollY = ( ScrollY == sY )
     if ( i % 10 == 0 ) {
          ? sY : s.GetActivePointY( mePosView );
      bmMenu.Add( "", 0, meMenuSeparator );
  Redraw = true;
}
 
// ---------- ▼ 関数 ▼ ---------- //
 
/**
* 関数 BookmarkMenu( objMenu, objArray )
* 「ブックマーク一覧」ポップアップメニュー
* または「ブックマークがありません」ポップアップメニュー
*
* ※各変数はグローバルスコープのものを利用する
*/
function BookmarkMenu( bmMenu, bmArray ) {
  // アクティブ行のチェックマークフラグ (meMenuChecked = 1)
  var Check = function( num ) { return ( num == aY ) *1; }
  // ピン止めアイテム
  var bmPin = MenuIndent( aY, linesWidth )
          + ":  ブックマークを設定/解除 (&B)";
  var countPin = MenuIndent()
              + " ▼ " + status1.slice( 0, -1 ) + " ▼";
  var cancelPin = MenuIndent()
              + "  キャンセル\t& ";
  var topPin = MenuIndent( 1, linesWidth )
            + ":  ** 先頭行へジャンプ (&T) **";
  var endPin = MenuIndent( lines, linesWidth )
            + ":  ** 最終行へジャンプ (&E) **";
  var actPin = MenuIndent( aY, linesWidth )
            + ":    行頭へジャンプ (&A)";
 
  // メニューの最上部に「設定/解除」をピン止め
  if ( bmCount || extraMenuEnable ) {
     bmMenu.Add( bmPin, 20000000, isBookmarked );
    bmMenu.Add( "----", 0, meMenuSeparator );
 
    /* 【include版】の設定変更サブメニュー */
    if ( initialSettings.settingEnable ) {
      SettingsMenu( bmMenu );
     }
     }
    bmMenu.Add( bmArray[i][0], bmArray[i][1], Check( bmArray[i][1] ) );
   }
   }
   if ( menuPinEnable && bmArray[ bmCount - 1 ][1] !== lines ) {
 
     bmMenu.Add( "", 0, meMenuSeparator );
  // 「ブックマーク一覧」ポップアップメニュー
     bmMenu.Add( endPin, lines, Check( lines ) );
   if ( bmCount ) {
  }
  // メニュー上部のピン止めアイテム
}
    bmMenu.Add( cancelPin, 0 ); // 「キャンセル」
// 「ブックマークがありません」ポップアップメニュー
     bmMenu.Add( "----", 0, meMenuSeparator );
else if ( extraMenuEnable ) {
     bmMenu.Add( "----", 0, meMenuSeparator );
  bmMenu.Add( "※" + bmStatus1 + " ※\t& ", 0 );
    // メニュー上部のピン止めアイテム
  if ( ! menuPinEnable ) {
    if ( menuPinEnable && ( bmArray[0][1] > 1
    bmMenu.Add( bmStatus2, 0 );
        || ! isBookmarked && aY > 1 && aY < lines ) ) {
  }
      if ( bmArray[0][1] > 1 ) {
  if ( menuPinEnable && lines > 1 ) {
        bmMenu.Add( topPin, 1, Check( 1 ) ); // 「先頭行へジャンプ」
    bmMenu.Add( "", 0, meMenuSeparator );
      }
    bmMenu.Add( topPin, 1, Check( 1 ) );
      if ( ! isBookmarked && aY > 1 && aY < lines ) {
    if ( aY > 1 && aY < lines ) {
        bmMenu.Add( actPin, aY, meMenuChecked ); // 「行頭へジャンプ」
       bmMenu.Add( actPin, aY, meMenuChecked );
      }
      if ( bmCount > subMenuHeight && bmArray[ bmCount -1 ][1] !== lines ) {
        bmMenu.Add( endPin, lines, Check( lines ) ); // 「最終行へジャンプ」
      }
       bmMenu.Add( "----", 0, meMenuSeparator );
     }
     }
    bmMenu.Add( endPin, lines, Check( lines ) );
  }
  else if ( menuPinEnable && lines == 1 ) {
    bmMenu.Add( actPin, aY, meMenuChecked );
  }
}


// ステータスバーとポップアップメニューを表示
    // 「分割サブメニュー」をピン止め
Status = bmStatus1 + bmStatus2
    if ( subMenuEnable && bmCount > subMenuHeight ) {
      + TimerElapsed( new Date(), start );
      var subMenu = DevidedSubMenuBM( bmMenu, bmArray );
var y = bmMenu.Track( mePosMouse );
    }


/* 【include版】の設定変更サブメニュー項目を選択した場合 */
    // ブックマーク一覧
if ( y >= 10000000 ) {
    bmMenu.Add( countPin, 0, 0 ); // ▼ ブックマーク: n 件 ▼
  SettingsChange( y );
    for ( var i = 0; i < bmCount; i ++ ) {
}
      if ( i % 10 == 0 && i > 0 ) {
        bmMenu.Add( "----", 0, meMenuSeparator ); // 10行ごとにセパレータ
        if ( menuScrollEnable && i % subMenuHeight == 0 ) {
          bmMenu.Add( cancelPin, 0 ); // 20行* ごとに「キャンセル」
          bmMenu.Add( "----", 0, meMenuSeparator );
        }
      }
      // ブックマーク行をポップアップメニューに追加
      bmMenu.Add( bmArray[i][0], bmArray[i][1], Check( bmArray[i][1] ) );
    }


// ジャンプ
    // メニュー下部のピン止めアイテム
else if ( y > 0 ) {
    if ( menuPinEnable && bmArray[ bmCount -1 ][1] !== lines ) {
  if ( y == aY ) {
      bmMenu.Add( "----", 0, meMenuSeparator );
    s.StartOfDocument();
      bmMenu.Add( endPin, lines, Check( lines ) ); // 「最終行へジャンプ」
    s.EndOfDocument();
    }
    if ( menuScrollEnable && bmCount > subMenuHeight ) {
      bmMenu.Add( "----", 0, meMenuSeparator );
      bmMenu.Add( cancelPin, 0 ); // 「キャンセル」
    }
   }
   }
  s.SetActivePoint( posMode, 1, y );


   // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択
   // 「ブックマークがありません」ポップアップメニュー
   if ( flgBlockEnable && act !== anc ) {
   else if ( extraMenuEnable ) {
     var tp = ( anc < act ) ? anc : act;
     bmMenu.Add( "----", 0, meMenuSeparator );
     var bp = ( anc < act ) ? act : anc;
     // ステータスラベルをピン止め
     var jumpDown = ( aY < y );
    bmMenu.Add( "※ " + status1 + "  ※\t& ", 0 ); // 「ブックマークがありません」
     s.SetAnchorPos( jumpDown ? tp : bp );
     if ( ! menuPinEnable ) {
    if ( jumpDown && endOfBookmarkLine ) {
      bmMenu.Add( " " + status2, 0 ); // 全体の行数
       s.EndOfLine( true, mePosLogical );
    }
       ScrollX = 1;
    // ピン止めアイテム
     if ( menuPinEnable ) {
      if ( lines > 1 ) {
        bmMenu.Add( "----", 0, meMenuSeparator );
        bmMenu.Add( topPin, 1, Check( 1 ) ); // 「先頭行へジャンプ」
        if ( aY > 1 && aY < lines ) {
          bmMenu.Add( actPin, aY, meMenuChecked ); // 「行頭へジャンプ」
        }
        bmMenu.Add( endPin, lines, Check( lines ) );// 「最終行へジャンプ」
       } else {
        bmMenu.Add( actPin, aY, meMenuChecked ); // 「行頭へジャンプ」
       }
     }
     }
   }
   }
  ScrollY = ( ScrollY == sY )
          ? sY : s.GetActivePointY( mePosView );
}
}
Redraw = true;


// ---------- ▼ 関数 ▼ ----------
/**
* 関数 MenuIndent( str )
* メニューラベルの字下げ
*/
function MenuIndent( str ) {
  str = str || "";
  var appendix = /^&\d/.test( str ) ? 1 : 0;
  return ( "          " + str ).slice( - linesWidth - appendix );
}


/**
/**
640行目: 795行目:
  * ポップアップメニューに表示するラベルを生成する
  * ポップアップメニューに表示するラベルを生成する
  *  
  *  
  * ・行頭空白を除去
  * ・行頭空白を除去、空白文字を圧縮
  * ・空白文字を圧縮
  * ・「&」 を補完
  * ・削られてしまう「&」を補完
* ・「¥」(U+005C) を 「∖」(U+2216) に置換:    または 「╲」(U+2572), 「﹨」(U+FE68)
  * ・判別しづらいメタ文字を全角に置換
  * ・ゼロ幅や特殊な空白文字を豆腐に置換: → 「▯」(U+25AF) または 「⊠」(U+22A0)
* ・判別しづらい半角記号を全角に置換: !"%'(),.:;@[]`{|}
*    + 「a-z」 を全角に置換
  *   または半角記号の前後にスキマをつける: HAIR SPACE 「 」(U+200A)
  * ・文字数を切り詰め
  * ・文字数を切り詰め
  * ・行番号をケタ埋め: EN SPACE「 」(U+2002)
  * ・行番号を空白でケタ埋め: EN SPACE 「 」(U+2002)
* ・「¥」(U+005C) を「∖」に置換:「∖」(U+2216) または「╲」(U+2572) 「﹨」(U+FE68)
* ・「a-z」を全角に置換
  */
  */
function MenuKey( str, num ) {
function MenuKey( str, num ) {
   var menuKey = str.replace( /^[\t  ]+/, "" )
  var keyWidth = /^[\s ]*[\s!-~∖▯⊠ ]+$/.test( str ) ? menuWidth * 2 : menuWidth;
  var regBlankLine = /^[\s \u00A0\u1680\u180e\u2000-\u200A\u2028\u2029\u202F\u205F\uFEFF]+$/;
  var regBlank = /[\u00A0\u1680\u180E\u2000-\u200E\u2028\u2029\u202F\u205F\u2062\u2063\uFEFF]/g
   var menuKey = ( str ) ? regBlankLine.test( str ) ? "( 空白行 )" : str
                        : "( 空行 )";
  menuKey = menuKey.replace( /^[\t  ]+/, "" )
                   .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " )
                   .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " )
                   .replace( /&/g, "&&" )
                  .slice( 0, keyWidth + 1 )
                // .replace( /[\\]/g, "∖" )
                   .replace( /[&]/g, "&&" )
                // .replace( /([^∖!"',.:;`|\t  ])([∖]+)/g, "$1 $2" )
                  .replace( /[\\]/g, "∖" )
                  .replace( /[!"%'(),.:;@\[\]`a-z{|}]/g,
                  .replace( regBlank, "▯" );
                    function( $0 ) {
  if ( toFullWidth ) {
                      return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
    menuKey = menuKey.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g,
                     } );
      function( tmp ) {
   menuKey = ( menuKey.length > menuWidth )
        return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
           ? menuKey.slice( 0, menuWidth ) + " ..."
      } );
  }
  else {
    // スラッシュ 以外の ascii 記号と fijl に HAIR SPACE (U+200A) を付加
    menuKey = menuKey.replace( /[!-%'-.:-@\[-`{-~fijl¥⊠▯]|&&/g, " $& " )
                     .replace( /\u200A+/g, " " )
                    .replace( /\u2002\u200A/g, " " );
    keyWidth = keyWidth
            + ( menuKey.match( /[\u200A›↲]|&&/g ) || "" ).length;
  }
   menuKey = ( menuKey.length > keyWidth )
           ? menuKey.slice( 0, keyWidth ) + " ..."
           : menuKey;
           : menuKey;
   return ( "          &" + num ).slice( - linesWidth - 1 ) + ":  " + menuKey;
   num = MenuIndent( "&" + num ) + ":";
  return num + menuKey;
}
}


/**
/**
  * 関数 TimerElapsed( end, start )
  * 関数 TimerElapsed( end, start )
  * start からの経過時間計測
  * start からの経過時間を [ s. sss 秒 ] で返す
* [ s.sss 秒 ] で返す
  */
  */
function TimerElapsed( end, start ) {
function TimerElapsed( end, start ) {
   var elapsed = ( end - start ) / 1000;
   var elapsedSec = ( ( end - start ) / 1000 ).toFixed( 3 );
   return " [ "
   return " [ " + elapsedSec.replace( /\./, ". " ) + " 秒 ]";
        + ( elapsed + "000" ).replace( /(\.)(\d{3})(0*)/, "$1 $2" )
        + " 秒 ]";
}
}


/**
/**
  * 関数 GetIniOptionNum( key )
  * 関数 GetIniOptionNum( key )
  * 引数で指定された設定項目の「値」を返す(※数値1ケタ)
  * 引数で指定された設定項目の「値」を返す(※数値のみ)
  */
  */
function GetIniOptionNum( key ) {
function GetIniOptionNum( key ) {
  var regIniKey = new RegExp( "^" + key + "=\\d+$", "m" );
   var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
   var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
   // Mery.ini を探す
   // Mery.ini を探す
   var iniPath = editor.FullName.replace( /\.exe$/i, ".ini" );
   var meryPath = editor.FullName;
  var mery    = Fso.GetBaseName( meryPath );
  var iniPath  = meryPath.replace( /\.exe$/i, ".ini" );
   if ( ! Fso.FileExists( iniPath ) ) {
   if ( ! Fso.FileExists( iniPath ) ) {
     var WshShell = new ActiveXObject( "WScript.Shell" );
     var WshShell = new ActiveXObject( "WScript.Shell" );
     iniPath = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
     iniPath = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
             + "\\Mery\\Mery.ini";
             + "\\Mery\\" + mery + ".ini";
   }
   }
   // Mery.ini を読み込む
   // Mery.ini を読み込む
696行目: 867行目:
   iniFile.Close();
   iniFile.Close();
   // 項目の値を取得する
   // 項目の値を取得する
   var dist = + iniText.match( regIniKey )
   return + RegExp( "^" + key + "=(\\d+)$", "m" ).exec( iniText )[1];
                      .shift()
                      .replace( /^.+=(\d+)$/, "$1" );
  return dist;
}
}


// ---------- ▼ 【include版】 関数 ▼ ----------
/**
* 関数 DevidedSubMenuBM( objPopupMenu, items )
* サブメニュー「※ 20* 件ずつ表示 ※  ▶」/「1 - 20* 件目  ▶」に
* 配列の「アイテム」を分割表示する
* ※「20*」の部分の数値は設定用変数 subMenuHeight で指定
*/
function DevidedSubMenuBM( objPopupMenu, items ) {
  var len      = items.length;
  var lenWidth = len.toString().length;
  var smRow    = Math.ceil( len / subMenuHeight );
  var smSq    = subMenuHeight * subMenuHeight;
  var smArray  = []; // SubMenu Array
  var smLabelArray = []; // SubMenu-Label Array
  var smId,  _from,  _to,  tick,  ticked;
 
  var subMenu  = CreatePopupMenu();
  var popupMenu = ( subMenuEnable == 1 )
                ? subMenu : objPopupMenu;
  if ( subMenuEnable == 1 ) {
    // 「※ 20* 件ずつ表示 ※  ▶」をピン止め
    objPopupMenu.AddPopup( MenuIndent() + " ※   "
                        + subMenuHeight + " 件ずつに分割して表示 (&D)  ※"
                        , subMenu );
    objPopupMenu.Add( "", 0, meMenuSeparator );
  }
 
  // サブメニューと配列のアイテムをポップアップメニューに追加していく
  for ( var i = 0; i < len; i ++ ) {
    // セパレータと「キャンセル」
    if ( i % subMenuHeight == 0 ) {
      if ( smRow > 10 && i != 0 && i % ( subMenuHeight * 10 ) == 0 ) {
        popupMenu.Add( "", 0, meMenuSeparator );
        if ( len > smSq && i % smSq == 0 ) {
          popupMenu.Add( MenuIndent() + "  キャンセル\t& ", 0 );
          popupMenu.Add( "", 0, meMenuSeparator );
        }
      }
 
      // 配列 smArray 内にメニュー項目「_from - _to 件目  ▶」を生成
      smArray.push( CreatePopupMenu() );
      // 「_from - _to 件目  ▶」メニューの index
      smId  = smArray.length - 1;
      //  _from と _to の値
      _from = smId * subMenuHeight + 1;
      _to  = Math.min( smArray.length * subMenuHeight, len );
      // 「_from - _to 件目」メニューラベルを別途の配列 smLabelArray に
      smLabelArray.push( ( "       &" + _from ).slice( - lenWidth - 1 )
                      + " - "
                      + ( "       " + _to ).slice( - lenWidth )
                      + " 件目" );
    }
 
    // 「アイテム」の配列から「_from - _to 件目」サブメニュー配下に
    // ※ smArray[smId] は CreatePopupMenu オブジェクト
    smArray[ smId ].Add( items[i][0], items[i][1], items[i][2] );
    // アクティブ行のフラグのついた行
    if ( items[i][2] ) { ticked = smId; }
 
    // 10 行ごとにセパレータ、さいごに「キャセル」行
    if ( ( i + 1 ) % 10 == 0  || i == len - 1) {
      smArray[ smId ].Add( "", 0, meMenuSeparator );
      if ( ( i + 1 ) % subMenuHeight == 0 || i == len - 1 ) {
        smArray[ smId ].Add( MenuIndent() + "  キャンセル\t& ", 0 );
      }
    }
  } // end: for ()
 
  // 「_from - _to 件目」メニュー項目をメニューに追加
  for ( var i = 0; i < smArray.length; i ++ ) {
    // ※ AddPopup には meMenuChecked を付けられないので "*" 記号で代替
    tick = ( i == ticked ) ? "* " : "  ";
    popupMenu.AddPopup( tick + smLabelArray[i], smArray[i] );
  }
  // 「_from - _to 件目」のさいごに「キャセル」を追加
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "       ".slice( - lenWidth ) + "キャンセル\t& ", 0 );
 
  return subMenu;
}
 
 
// ---------- ▼ 【include版】 関数 ▼ ---------- //


/**
/**
  * 関数 SettingsMenu( objPopupMenu )
  * 関数 SettingsMenu( objPopupMenu )
  * ポップアップメニューに「設定変更」サブメニューを表示する
  * ポップアップメニューに「設定変更」サブメニューを表示する
  * 引数: メインコードのポップアップメニューオブジェクト
  * 引数: メインスコープのポップアップメニューオブジェクト
  */
  */
function SettingsMenu( objMenu ) {
function SettingsMenu( objMenu ) {
   var subMenu = CreatePopupMenu();
   var subMenu = CreatePopupMenu();
  var menuIndent = ( "          " ).slice( - linesWidth );
   // メニューのオプションフラグ
   // メニューのオプションフラグ
  var settingFlag = settingEnable ? 0 : meMenuChecked;
   var grayFlag = ! settingEnable ? meMenuGrayed : 0;
   var grayFlag = ! settingEnable ? meMenuGrayed : 0;
   var grayFlag1 = ( ! blockSelectEnable || ! settingEnable )
   var flgBlockFlag = flgBlockEnable ? meMenuChecked : 0;
                ? meMenuGrayed : 0;
   var grayFlag_1 = ( ! flgBlockEnable || ! settingEnable )
   var keepInitialSelectFlag = ( keepInitialSelect && blockSelectEnable )
                ? meMenuGrayed : 0;
                            ? meMenuChecked : 0;
   var endOfBookmarkLineFlag = ( endOfBookmarkLine && flgBlockEnable )
   var endOfBookmarkLineFlag = ( endOfBookmarkLine && blockSelectEnable )
                             ? meMenuChecked : 0;
                             ? meMenuChecked : 0;
   var extraMenuFlag = extraMenuEnable ? meMenuChecked : 0;
   var grayFlag2 = ( autoLineModeEnable || ! settingEnable )
  var menuPinFlag = menuPinEnable ? meMenuChecked : 0;
                ?  meMenuGrayed : 0;
  var autoLineModeFlag = autoLineModeEnable ? meMenuChecked : 0;
   var maxHeightFlag = ( ! settingEnable || ! subMenuEnable || subMenuHeight >= 100 )
  var grayFlag_2 = ( autoLineModeEnable || ! settingEnable )
                          ? meMenuGrayed : 0;
                ?  meMenuGrayed : 0;
   var minHeightFlag = ( ! settingEnable || ! subMenuEnable || subMenuHeight < 20 )
   var viewFlag = lineColumnView ? meMenuChecked : 0;
                          ? meMenuGrayed : 0;
   var logicalFlag = lineColumnView ? 0 : meMenuChecked;
   var maxWidthFlag = menuWidth > 100 ? meMenuGrayed : 0;
   var maxWidthFlag = menuWidth > 100 ? meMenuGrayed : 0;
   var minWidthFlag = menuWidth < 30 ? meMenuGrayed : 0;
   var minWidthFlag = menuWidth < 30 ? meMenuGrayed : 0;


   // 「設定変更」サブメニューのアイテム
   // 「設定変更」サブメニューのアイテム
   // objMenu.Add( "", 0, meMenuSeparator );
   objMenu.AddPopup( MenuIndent() + "  設定を変更する (&S)", subMenu );
  objMenu.AddPopup( menuIndent + "  設定を変更する (&S)", subMenu );
   objMenu.Add( "----", 0, meMenuSeparator );
   objMenu.Add( "", 0, meMenuSeparator );
 
   subMenu.Add( "  ジャンプ先の行まで 範囲選択する (&B)",
   subMenu.Add( "  ジャンプ先の行まで 範囲選択する (&B)",
               10000001, flgBlockFlag + grayFlag );
               10000001, blockSelectEnable + grayFlag );
   subMenu.Add( "  下の行にジャンプしたときに 行全体を選択 (&D)",
  subMenu.Add( "  もとの選択範囲を ジャンプ後の範囲選択に含める (&K)",
               10000002, endOfBookmarkLineFlag + grayFlag_1 );
              10000002, keepInitialSelectFlag + grayFlag1 );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "  下の行にジャンプしたときに 行全体を選択 (&E)",
               10000003, endOfBookmarkLineFlag + grayFlag1 );
  subMenu.Add( "----", 0, meMenuSeparator );
   subMenu.Add( "----", 0, meMenuSeparator );
   subMenu.Add( "  ブックマークがなくても メニューを表示する (&N)",
   subMenu.Add( "  ブックマークがなくても メニューを表示する (&N)",
               10000003, extraMenuFlag + grayFlag );
               10000004, extraMenuEnable + grayFlag );
   subMenu.Add( "「先頭行/最終行/行頭 へジャンプ」を表示 (&P)",
   subMenu.Add( "「先頭行/最終行/行頭 へジャンプ」 を表示する (&P)",
               10000004, menuPinFlag + grayFlag );
               10000005, menuPinEnable + grayFlag );
   subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "  " + subMenuHeight + " 件ごとに 「キャンセル」 を表示する (&C)",
              10000012, menuScrollEnable + grayFlag );
   subMenu.Add( "----", 0, meMenuSeparator );
   subMenu.Add( "「行の表示方法」 を自動設定する (&A)",
   subMenu.Add( "「行の表示方法」 を自動設定する (&A)",
               10000005, autoLineModeFlag + grayFlag );
               10000006, autoLineModeEnable + grayFlag );
   subMenu.Add( "  行数を 「論理行」 で表示する (&L)",
   subMenu.Add( "  行数を 「論理行」 で表示する (&L)",
               10000006, logicalFlag + grayFlag_2 );
               10000007, !lineColumnView + grayFlag2 );
   subMenu.Add( "  行数を 「表示行」 で表示する (&V)",
   subMenu.Add( "  行数を 「表示行」 で表示する (&V)",
               10000007, viewFlag + grayFlag_2 );
               10000007, lineColumnView + grayFlag2 );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "「分割サブメニュー」 を表示する (&D)"
            , 10000013, subMenuEnable + grayFlag );
  subMenu.Add( "  サブメニューの分割単位を 10行 増やす (&X) \t"
            + subMenuHeight
            + ( subMenuHeight >= 100 ? "" : " → " + ( subMenuHeight + 10 ) )
            , 10000014, maxHeightFlag );
  subMenu.Add( "  サブメニューの分割単位を 10行 減らす (&Z) \t"
            + subMenuHeight
            + ( subMenuHeight < 20 ? "" : " → " + ( subMenuHeight - 10 ) )
            , 10000015, minHeightFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
   subMenu.Add( "  メニューの表示幅を 10文字 増やす (&+)\t"
   subMenu.Add( "  メニューの表示幅を 10文字 増やす (&+)\t"
             + menuWidth
             + menuWidth
759行目: 1,022行目:
             + ( menuWidth < 30 ? "" : " → " + ( menuWidth - 10 ) ),
             + ( menuWidth < 30 ? "" : " → " + ( menuWidth - 10 ) ),
               10000009, minWidthFlag + grayFlag );
               10000009, minWidthFlag + grayFlag );
  subMenu.Add( "  半角 a-z を全角で表示する (&F)",
              10000010, toFullWidth + grayFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  入力カーソル の位置 にメニューを表示する"
            + ( menuPosMouse ? " (&M)" : "" ),
              10000011, !menuPosMouse + grayFlag );
  subMenu.Add( "  マウスポインタの位置 にメニューを表示する"
            + ( menuPosMouse ? "" : " (&M)" ),
              10000011, menuPosMouse + grayFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  設定内容をロックする (&S)", 10000000, !settingEnable*1 );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "「ジャンプ」マクロの JS ファイルを開く (&O)", 10000016 );
   subMenu.Add( " 設定内容をロックする (&S)", 10000000, settingFlag );
   subMenu.Add( "「ジャンプ」マクロの JSON ファイルを開く (&J)", 10000017 );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "----", 0, meMenuSeparator );
   subMenu.Add( "「ジャンプ」マクロの JS ファイルを開く (&O)", 10000013 );
   subMenu.Add( " 設定を初期化する (&I)", 10000018, grayFlag );
  subMenu.Add( "「ジャンプ」マクロの JSON ファイルを開く (&J)", 10000014 );
   subMenu.Add( "----", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "  設定内容を初期化する (&I)", 10000015, grayFlag );
   subMenu.Add( "  キャンセル\t& ", 0 );
   subMenu.Add( "  キャンセル\t& ", 0 );
}
}
773行目: 1,046行目:
  * 関数 SettingsChange( num )
  * 関数 SettingsChange( num )
  * ポップアップメニューで選択した項目の設定状態を変更する
  * ポップアップメニューで選択した項目の設定状態を変更する
* ※ 設定変更でのメニュー再表示 do - while ループのさいに
*    JSON の再読みこみを省略するために
*    ローカル変数 = JSON 項目 = 設定値  の形式で記述する
  */
  */
function SettingsChange( num ) {
function SettingsChange( num ) {
   var setting = IO.Deserialize( setting, jsonName );
   // var jsonPath = JsonDir() + "\\" + jsonName + ".json";
   var sIsChanged = true;
   var sIsChanged = true;
   switch ( num ) {
   switch ( num ) {
     case 10000000:
     case 10000000: // 設定内容をロックする
       setting.settingEnable = ! settingEnable;
       settingEnable = setting.settingEnable
                    = ! settingEnable;
       break;
       break;
     case 10000001:
     case 10000001: // ジャンプ先の行まで 範囲選択する
       setting.flgBlockEnable = ! flgBlockEnable;
       blockSelectEnable = setting.blockSelectEnable
                        = ! blockSelectEnable;
       break;
       break;
     case 10000002:
     case 10000002: // もとの選択範囲を ジャンプ後の範囲選択に含める
       setting.endOfBookmarkLine = ! endOfBookmarkLine;
       keepInitialSelect = setting.keepInitialSelect
                        = ! keepInitialSelect;
       break;
       break;
     case 10000003:
     case 10000003: // 下の行にジャンプしたときに 行全体を選択
       setting.extraMenuEnable = ! extraMenuEnable;
       endOfBookmarkLine = setting.endOfBookmarkLine
                        = ! endOfBookmarkLine;
       break;
       break;
     case 10000004:
     case 10000004: // ブックマークがなくても メニューを表示する
       setting.menuPinEnable = ! menuPinEnable;
       extraMenuEnable = setting.extraMenuEnable
                      = ! extraMenuEnable;
       break;
       break;
     case 10000005:
     case 10000005: // 「先頭行/最終行/行頭 へジャンプ」 を表示する
       setting.autoLineModeEnable = ! autoLineModeEnable;
       menuPinEnable = setting.menuPinEnable
                    = ! menuPinEnable;
       break;
       break;
     case 10000006:
     case 10000006: // 「行の表示方法」 を自動設定する
    case 10000007:
       autoLineModeEnable = setting.autoLineModeEnable
       setting.lineColumnView = ! lineColumnView;
                        = ! autoLineModeEnable;
       break;
       break;
     case 10000008:
     case 10000007: // 行数を 「論理行/表示行」 で表示する
       setting.menuWidth += 10;
       lineColumnView = setting.lineColumnView
                    = ! lineColumnView;
       break;
       break;
     case 10000009:
     case 10000008: // メニューの表示幅を 10文字 増やす
       setting.menuWidth -= 10;
       menuWidth = setting.menuWidth += 10;
       break;
       break;
     case 10000013:
    case 10000009: // メニューの表示幅を 10文字 減らす
      menuWidth = setting.menuWidth -= 10;
      break;
    case 10000010: // 半角英文字を全角で表示する
      toFullWidth = setting.toFullWidth
                  = ! toFullWidth;
      break;
    case 10000011: // 入力カーソル/マウスポインタ の位置 にメニューを表示する
      menuPosMouse = setting.menuPosMouse
                  = ! menuPosMouse;
      break;
    case 10000012: // 20 件* ごとに 「キャンセル」 を表示する
      menuScrollEnable = setting.menuScrollEnable
                      = ! menuScrollEnable;
      break;
     case 10000013: // 「分割サブメニュー」 を表示する
      subMenuEnable = setting.subMenuEnable
                    = ! subMenuEnable;
      break;
    case 10000014: // サブメニューの分割単位を 10行 増やす
      subMenuHeight = setting.subMenuHeight += 10;
      break;
    case 10000015: // サブメニューの分割単位を 10行 減らす
      subMenuHeight = setting.subMenuHeight -= 10;
      break;
    case 10000016: // 「ジャンプ」マクロの JS ファイルを開く
       sIsChanged = false;
       sIsChanged = false;
       Status = " " + ScriptFullName;
       Status = " " + ScriptFullName;
       OpenJumpJS( "var settingEnable = " );
       OpenJumpJS( "setting.settingEnable = " );
       break;
       break;
     case 10000014:
     case 10000017: // 「ジャンプ」マクロの JSON ファイルを開く
       sIsChanged = false;
       sIsChanged = false;
       OpenJumpJson( jsonName );
       OpenJumpJson( jsonName );
       break;
       break;
     case 10000015:
     case 10000018: // 設定を初期化する
       sIsChanged = false;
       sIsChanged = false;
       CheckJsonFile( jsonName );
       CheckJsonFile( jsonName );
825行目: 1,133行目:
   if ( sIsChanged ) {
   if ( sIsChanged ) {
     IO.Serialize( setting, jsonName );
     IO.Serialize( setting, jsonName );
     Status = "  設定の変更を保存しました。";
     Status = " 設定の変更を保存しました。";
   }
   }
}
}


/**
/**
833行目: 1,142行目:
  * このマクロの JS ファイルを開いて設定項目の行にジャンプする
  * このマクロの JS ファイルを開いて設定項目の行にジャンプする
  * 引数: 検索文字列(設定項目の行の文字列)
  * 引数: 検索文字列(設定項目の行の文字列)
* ※引数がないときは文頭
  */
  */
function OpenJumpJS( str ) {
function OpenJumpJS( str ) {
   Redraw = false;
   Redraw = false;
   var targetStr = str || "";
   var targetStr = str || "";
  // Count は 1 からの整数値、Item は 0 からの整数値
   var eCount = editors.Count;
   var eCount = editors.Count;
   var dCount,  dItem,  ee ,  dd;
   var dCount,  dItem,  ee,  dd;
   var isOpen = false;
   var isOpen = false;
   OuterLoop:
   OuterLoop:
848行目: 1,155行目:
       dItem = editors.Item( j ).documents.Item( i );
       dItem = editors.Item( j ).documents.Item( i );
       if ( dItem.FullName == ScriptFullName ) {
       if ( dItem.FullName == ScriptFullName ) {
         isOpen = true;
         js = dItem;  isOpen = true;
        ee = j;
        dd = i;
         break OuterLoop;
         break OuterLoop;
       }
       }
     }
     }
   }
   }
   if ( isOpen ) {
   if ( ! isOpen ) {
    editors.Item( ee ).documents.Item( dd ).Activate();
  }
  else {
     editor.NewFile();
     editor.NewFile();
     ee = ( editors.Count == 1 ) ? 0 : eCount;
     ee = ( editor.EnableTab ) ? editor : editors.Item( eCount );
     editors.Item( ee ).OpenFile( ScriptFullName );
     ee.OpenFile( ScriptFullName, 0 );
    js = ee.ActiveDocument;
   }
   }
   var js = editors.Item( ee ).ActiveDocument;
   js.Activate();
   var settingPos = js.Text.indexOf( targetStr );
   var settingPos = js.Text.indexOf( targetStr );
   js.selection.SetActivePos( settingPos + targetStr.length );
   js.selection.SetActivePos( settingPos + targetStr.length );
   js.selection.WordRight( true ); // true を範囲選択
   // js.selection.WordRight( true ); // true を範囲選択
   var vy = js.selection.GetActivePointY( mePosView );
   ScrollY = js.selection.GetActivePointY( mePosView );
  ScrollY = vy;
  // 狙った行にスクロールしないようなら、強引にやる
  // var ly = js.selection.GetActivePointY( mePosLogical );
  // var WshShell = new ActiveXObject( "WScript.Shell" );
  // WshShell.Run( '"' + editor.FullName + '" /l ' + ly + ' "'
  //                  + ScriptFullName + '"' );
  // ScrollY = vy;
   Redraw = true;
   Redraw = true;
}
}
882行目: 1,178行目:
  * JSON ファイルを開く
  * JSON ファイルを開く
  */
  */
function OpenJumpJson() {
function OpenJumpJson( jsonName ) {
   var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
   IO.Serialize( setting, jsonName );
  Sleep( 500 );
   var jsonDir = JsonDir();
   var jsonDir = JsonDir();
   var jsonPath = jsonDir + "\\" + jsonName + ".json";
   var jsonPath = jsonDir + "\\" + jsonName + ".json";
   IO.Serialize( setting, jsonName );
   if ( IO.Path.IsExist( jsonPath )
  Sleep( 500 );
  if ( Fso.FileExists( jsonPath )
       && JsonContents( jsonPath ).length ) {
       && JsonContents( jsonPath ).length ) {
     var confirmStr = "設定ファイルは正常です \n\n"
     var confirmStr = "設定ファイルは正常です \n\n"
895行目: 1,190行目:
     if ( Confirm( confirmStr ) ) {
     if ( Confirm( confirmStr ) ) {
       var WshShell = new ActiveXObject( "WScript.Shell" );
       var WshShell = new ActiveXObject( "WScript.Shell" );
       WshShell.Run( '"' + editor.FullName
       WshShell.Run( '"' + editor.FullName + '" "' + jsonPath + '"' );
                  + '" "' + jsonPath + '"' );
     }
     }
   }
   } else {
  else {
     Alert( "設定ファイルがありません \n"
     Alert( "設定ファイルがありません \n"
         + jsonDir + "  " );
         + jsonDir + "  " );
918行目: 1,211行目:
     Sleep( 500 );
     Sleep( 500 );
     var alertStr = ( IO.Path.IsExist( jsonPath )
     var alertStr = ( IO.Path.IsExist( jsonPath )
                    && JsonContents( jsonPath ).length )
                    && JsonContents( jsonPath ).length )
                 ? "設定ファイルは正常です \n\n"
                 ? "設定ファイルは正常です \n\n"
                   + JsonContents( jsonPath )
                   + JsonContents( jsonPath )
932行目: 1,225行目:
  */
  */
function JsonDir() {
function JsonDir() {
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
   var jsonDir;
   var jsonDir;
   if ( Fso.FileExists( editor.FullName.replace( /\.exe$/i, ".ini" ) ) ) {
   if ( IO.Path.IsExist( editor.FullName.replace( /\.exe$/i, ".ini" ) ) ) {
     jsonDir = editor.FullName.replace( /Mery\.exe$/i, "" )
     jsonDir = editor.FullName.replace( /[^\\]+$/i, "" )
             + "Macros\\MacroSettings";
             + "Macros\\MacroSettings";
   }
   } else {
  else {
     var WshShell = new ActiveXObject( "WScript.Shell" );
     var WshShell = new ActiveXObject( "WScript.Shell" );
     jsonDir = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
     jsonDir = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
947行目: 1,238行目:


/**  
/**  
  * 関数 JsonContents( jsonPath )
  * 関数 JsonContents( jsonPath [, bool] )
  * JSON ファイルの文字列をメッセージボックス用に整形する
  * JSON ファイルの文字列をメッセージボックス用に整形する
*
* 第1引数は JSON ファイルのパス
* 第2引数は真偽値(省略可、true なら \uHHHH をデコードする)
  */
  */
function JsonContents( jsonPath ) {
function JsonContents( jsonPath, bool ) {
   var contents = ( jsonPath + "  \n"
   var contents = ( jsonPath + "  \n"
   + IO.LoadFromFile( jsonPath )
   + IO.LoadFromFile( jsonPath )
    .replace( /^\{/, "\n{\n  " )
      .replace( /^\{/, "\n{\n  " )
    .replace( /\}$/g, "\n}\n\n" )
      .replace( /\}$/g, "\n}\n\n" )
    .replace( /,/g, " ,\n  " )
      .replace( /,"/g, " ,\n  \"" )
    .replace( /:/g, ": " )
      .replace( /":/g, "\": " )
  // .replace( /[a-z]/g,
  //  function( $0 ) {
  //    return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
  //  } )
   );
   );
  if ( bool ) {
    // 「テキスト整形」マクロより >> 符号化/復号化 >> \uHHHH デコード
    var Decode_uHHHH = function( str ) {
      return str.replace( /\\u([0-9A-Fa-f]{4})/g, function( s, n ) {
        return String.fromCharCode( Number( "0x" + n ) );
      } );
    };
    contents = Decode_uHHHH( contents );
  }
  if ( toFullWidth ) {
    // 半角アルファベットの小文字を全角にするなら
    contents = contents.replace( /[a-z]/g,
      function( tmp ) {
        return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
      } );
  }
   return contents;
   return contents;
}
}
/**
* ---------- ▼ 更新履歴 ▼ ----------
* 2019/03/29:
* ・sukemaru版をマクロライブラリに投稿。
*
* 2019/04/08:
* ・Quit() の削除と、軽微な変更。
*
* 2019/04/16:
* ・Mery 2.7.0 でのブックマークの仕様変更に対応。
* ・【include版】を追加。
*
* 2019/12/05:
* ・「ブックマークを設定/解除」 をメニューに追加
* ・メニュー内にブックマーク件数を表示
* ・「もとの選択範囲をジャンプ後の範囲選択に含める」 を設定項目に追加
*
*  【include版】
* ・ポップアップメニュー生成コードを関数セクションに移動
* ・「分割サブメニュー」 を追加
* ・20* 行ごとに 「キャンセル」 を追加(スペースキーでスクロール)
*  「キャンセル」 行の追加単位は 「分割サブメニュー」 の分割設定にあわせた
* ・「設定変更」 したときのメニュー表示のループ処理を追加
* ・「半角 a-z を全角表示する」 を設定項目に追加
* ・その他、設定項目の追加、動作コードの変更
*/
</source>
</source>

2019年12月6日 (金) 01:37時点における版

goat 版

ポップアップメニューでジャンプ先を選択。範囲指定中はジャンプ先まで選択範囲を拡大します。

Mery Ver.1.1.2.2840 および Ver.2.0.9.3936 での動作を確認しています。 (2012.09.24)

 #title="ブックマーク一覧"
 /*
 * ブックマーク一覧をポップアップメニューにして選択ジャンプ
 * Mery標準のブックマークが対象
 * 範囲選択中の場合は選択範囲を拡大
 */
 (function(){
   // 描画停止
   Redraw = false;
   with(document.selection){
     var flgBlock = !isEmpty;
     //カーソル位置の保存
     var activePos = GetActivePos();
     var anchorPos = GetAnchorPos();
     var sX = ScrollX;
     var sY = ScrollY;
 
     var bookmarks = new Array();
     StartOfDocument(false);
     // 先頭行がブックマークされてないか
     var isTopMarked = false;
     CharRight(false);
     if(PreviousBookmark()){
       isTopMarked = true;
       SelectLine();
       menuKey = Text.slice(0,24).replace(/\t/g, "\\t");
       Collapse();
       bookmarks.push("Line " + GetActivePointY(mePosLogical) + ":\t" + menuKey);
     } else {
       StartOfDocument(false);
     }
     while(NextBookmark()){
       SelectLine();
       menuKey = Text.slice(0, 24).replace(/\t/g, "\\t");
       Collapse();
       bookmarks.push("Line " + GetActivePointY(mePosLogical) + ":\t" + menuKey);
     }
     var bmcount = bookmarks.length;
     if(bmcount == 0){
       //カーソル位置の復帰
       SetActivePos(activePos);
       SetAnchorPos(anchorPos);
       ScrollX = sX;
       ScrollY = sY;
     } else {
       var BookmarkMenu = CreatePopupMenu();
       for(var i = 0; i < bmcount; i++){
         //メニューのアクセスキーも1から始まるほうが便利
         BookmarkMenu.Add("&" + (i+1) + " " + bookmarks[i], i+1)
       }
       var ret = BookmarkMenu.Track(0);
       if(ret == 0){
         //カーソル位置の復帰
         SetActivePos(activePos);
         SetAnchorPos(anchorPos);
         ScrollX = sX;
         ScrollY = sY;
       } else {
         StartOfDocument(false);
         if(isTopMarked)ret--;
         for(i = 0; i < ret; i++){ NextBookmark(); }
       }
     }
     if(flgBlock){
       //ジャンプ前の選択開始位置からジャンプ後のカーソル位置まで範囲選択
       SetAnchorPos(anchorPos);
     }
   }
   // 描画再開
   Redraw = true;
 })()

Mery ver 2.7.0 以降への対応

goat 版を Mery ver 2.7.0 以降で使用する場合は、20 行目付近を以下のように書き換えてください。
変更しない場合、ドキュメントの 1 行目がブックマークされていてもポップアップメニューに表示されなくなります。 (2019/04/16 sukemaru)

変更前
18.    var bookmarks = new Array();
19.    StartOfDocument(false);
20.    // 先頭行がブックマークされてないか
21.    var isTopMarked = false;
22.    CharRight(false);
23.    if(PreviousBookmark()){
24.      isTopMarked = true;
変更後
18.    var bookmarks = new Array();
19.    // StartOfDocument(false);
20.    // 先頭行がブックマークされてないか
21.    var isTopMarked = false;
22.    SetActivePoint(mePosLogical, 1, 2);
23.    if(PreviousBookmark()){
24.      isTopMarked = true;


sukemaru 版

goat 版を元にして、ポップアップメニューを表示するまでの速度を改善し、動作設定をカスタマイズできるようにしました。

▼ goat 版からの変更点 ▼
  • ソースコード内の設定項目から "表示行" / "論理行" の選択可(初期値は "論理行")
  • 設定項目で "ジャンプ先までの範囲選択" はしない状態を初期値にした
  • ブックマーク行の検索 ~ ポップアップメニューの表示 までの速度を改善
  • ポップアップメニューの体裁を変更(「検索ジャンプ」マクロにあわせた)
  • ステータスバーにブックマーク件数などの情報を表示
  • 「ブックマークがありません」メニュー。
  • ジャンプしたときのスクロール位置を調整。
  • ジャンプ先まで範囲選択するときの先頭/末尾を調整。
  • 「先頭行/最終行 へジャンプ」をメニューにピン止め表示。
  • 「ブックマークを設定/解除」をメニューの先頭にピン止め表示。
  • ポップアップメニューだと読み取りづらい半角英小文字・と ascii 記号の一部を全角で表示。


submenuenable = 2


Mery でプログラミング用のフォントを使っている人だと、ポップアップメニュー内で半角 ¥ 記号 (U+005C) がバックスラッシュ (U+005C) にならないのは気落ち悪いかも? ということで、ソースコード内の関数 MenuKey() のコードのコメントアウトしてある行を有効化すると "擬似的に" バックスラッシュ化できます
(※ (U+2216) に置換して表示します)。

ダウンロード

ダウンロード:ファイル:ブックマークジャンプ.zip」(アイコン入り) 
最終更新: 2019/12/05(Mery 2.7.0 以降への対応と include版 を追加)

オマケとして [ 編集 ] メニューのブックマーク関連コマンドのアイコン化用マクロ×4


ソースコード

  • このマクロではポップアップメニューの表示に EN SPACE (U+2002) などを使用します。ファイルに保存するさいは、文字コードを shift_JIS にしないでください(UTF-8 や Unicode で保存する)。
#title = "ブックマーク一覧ジャンプ"
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
#icon = "bookmark_list.ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243

/**
 * ---------------------------------------------------------
 * 「ブックマーク一覧ジャンプ」
 *   Orginal created by: Kuro, goat (2012/06/22 - 2012/09/24)
 *   Modified by:        sukemaru   (2018/08/08 - 2019/12/05)
 * ---------------------------------------------------------
 * ブックマーク一覧をポップアップメニューで表示し、選択した行へジャンプする。
 * 
 * ・ 行番号は "表示行" / "論理行" の設定可。
 * ・ ブックマーク行の検索 ~ ポップアップメニュー表示 の速度を改善。
 * ・ ポップアップメニューの体裁を変更。
 * ・ ステータスバーに情報を表示。
 * ・「ブックマークがありません」メニュー。
 * ・ ジャンプしたときのスクロール位置を調整。
 * ・ ジャンプ先まで範囲選択するときの先頭/末尾を調整。
 * ・「先頭行/行頭/最終行 へジャンプ」をメニューにピン止め表示
 * ・「ブックマークを設定/解除」をメニューの先頭にピン止め表示
 * 
 */

var start = new Date();		// 所要時間計測(開始)

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

// ■ ジャンプする前に選択範囲があったときはジャンプ先の行まで範囲選択する?
var blockSelectEnable = false;	// (true 範囲選択する / false 範囲選択しない)

    // ■ もとの選択範囲をジャンプ後の範囲選択に含める?
    var keepInitialSelect = false;	// (true 含める / false 含めない)

    // ■ 下の行にジャンプしたとき、ジャンプ先の行全体(論理行)を選択範囲に含める?
    var endOfBookmarkLine = false;	// (true 含める / false 含めない)

// ■ 行番号の表示方法を Mery.ini から「自動」で読み込む?
var autoLineModeEnable = false;	// (true 自動読み込み / false 手動設定)

    // ■ 行番号の表示方法(手動設定用)
    var lineColumnView = 0;		// ( 0 論理行 / 1 表示行 )

// ■ 「ブックマークがありません」メニューを表示する?
var extraMenuEnable = true;		// (true 表示する / false 表示しない)

// ■ ポップアップメニューに「先頭行/最終行 へジャンプ」をピン止め表示する?
var menuPinEnable = true;		// (true 表示する / false 表示しない)

// ■ ポップアップメニューに表示する文字数の目安
var menuWidth = 65;				// (全角/半角の区別なし)

// ■ 半角英文字を全角で表示する	[!"%'(),.:;@\[\]`a-z{|}]
var toFullWidth = true;			// (true 置換する / false 置換しない)

// ■ ポップアップメニューを表示する位置
var menuPosMouse = true;		// ( true マウス位置 / false カーソル位置 )

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

// Mery.ini から「行の表示方法」を取得する( 表示行 = 1 / 論理行 = 0 )
if ( autoLineModeEnable ) {
  lineColumnView = GetIniOptionNum( "LineColumnView" );
}
// 表示行/論理行 の定数
var posMode = lineColumnView ? mePosView : mePosLogical;
var lineMode = lineColumnView ? meGetLineView : 0;
// 全体の行数
var d = editor.ActiveDocument;
var lines = d.GetLines( lineMode );
var linesWidth = lines.toString().length;
// カーソルと選択範囲の位置を保存
var s = d.selection;
var act = s.GetActivePos(),  anc = s.GetAnchorPos();
var aY = s.GetActivePointY( posMode );
var sX = ScrollX,  sY = ScrollY;
// ブックマーク行の変数
var bmArray = [],  flag = 0,  isBookmarked = 0;
var bmY, bmStr;

// ブックマーク行を検索
Redraw = false;
// 先頭行がブックマークされているか
s.SetActivePoint( mePosLogical, 1, 2 );
if ( s.PreviousBookmark() ) {
  bmY = s.GetActivePointY( posMode );
  bmStr = d.GetLine( bmY, lineMode );
  if ( bmY == aY ) { flag = 1; isBookmarked = 1; }
  bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
}
// 2行目以降のブックマークを探す
s.SetActivePos( 0 );
while ( s.NextBookmark() ) {
  bmY = s.GetActivePointY( posMode );
  bmStr = d.GetLine( bmY, lineMode );
  if ( bmY == aY ) { flag = 1; isBookmarked = 1; }
  else { flag = 0; }
  bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
}
// ブックマークの件数
var bmCount = bmArray.length;

// カーソル位置と選択範囲を復帰
s.SetActivePos( act );
s.SetAnchorPos( anc );
ScrollX = sX,  ScrollY = sY;
Redraw = true;

// ステータスラベル
var status1 = ( bmCount )
            ? " ブックマーク: " + bmCount + " 件 /"
            : " ブックマークがありません。";
var status2 = " 全体の行数: " 
            + lines.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," ) 
            + ( lineColumnView ? " 行 (表示行)" : " 行 (論理行)" );

// ポップアップメニューの準備
var bmMenu = CreatePopupMenu();
// アクティブ行のチェックマーク (meMenuChecked)
var Check = function( num ) { return  ( num == aY ) *1; }

// ピン止めアイテム
var bmPin = ( "          " + aY ).slice( - linesWidth )
          + ":  ブックマークを設定/解除 (&B)";
if ( bmCount ) {
  var countPin = ( "          " ).slice( - linesWidth )
               + " ▼ " + status1.slice( 0, -1 ) + " ▼";
  var cancelPin = ( "          " ).slice( - linesWidth )
                + "  キャンセル\t& ";
}
if ( menuPinEnable ) {
  var topPin = ( "          " + 1 ).slice( - linesWidth )
             + ":  ** 先頭行へジャンプ (&T) **";
  var endPin = ( "          " + lines ).slice( - linesWidth )
             + ":  ** 最終行へジャンプ (&E) **";
  var actPin = ( "          " + aY ).slice( - linesWidth )
             + ":     行頭へジャンプ (&A)";
}

// 「ブックマークを設定/解除」をピン止め
if ( bmCount || extraMenuEnable ) {
  bmMenu.Add( bmPin, 20000000, isBookmarked );
  bmMenu.Add( "-----", 0, meMenuSeparator );
}

// 「ブックマーク一覧」ポップアップメニュー
if ( bmCount ) {
  // メニュー上部のピン止めアイテム
  bmMenu.Add( cancelPin, 0 );					// 「キャンセル」
  bmMenu.Add( "-----", 0, meMenuSeparator );
  if ( menuPinEnable && bmArray[0][1] > 1 ) {
    bmMenu.Add( topPin, 1, Check( 1 ) );		// 「先頭行へジャンプ」
  }
  if ( ! isBookmarked && aY > 1 && aY < lines ) {
    bmMenu.Add( actPin, aY, meMenuChecked );	// 「行頭へジャンプ」
  }
  bmMenu.Add( "-----", 0, meMenuSeparator );
  bmMenu.Add( countPin, 0, 0 );					// ▼ ブックマーク: n 件 ▼
  // ブックマーク行をポップアップメニューに追加
  for ( var i = 0, flags; i < bmCount; i ++ ) {
    if ( i != 0 && i % 10 == 0 ) {
      bmMenu.Add( "-----", 0, meMenuSeparator );	// 10行ごとにセパレータ
    }
    bmMenu.Add( bmArray[i][0], bmArray[i][1], Check( bmArray[i][1] ) );
  }
  // メニュー下部のピン止めアイテム
  if ( menuPinEnable && bmArray[ bmCount - 1 ][1] !== lines ) {
    bmMenu.Add( "-----", 0, meMenuSeparator );
    bmMenu.Add( endPin, lines, Check( lines ) );	// 「最終行へジャンプ」
  }
}

// 「ブックマークがありません」ポップアップメニュー
else if ( extraMenuEnable ) {
  bmMenu.Add( "※" + status1 + "& ※", 0 );
  if ( ! menuPinEnable ) {
    bmMenu.Add( status2 + "& ", 0 );
  }
  // ピン止めアイテム
  else if ( lines > 1 ) {
    bmMenu.Add( "-----", 0, meMenuSeparator );
    bmMenu.Add( topPin, 1, Check( 1 ) );		// 「先頭行へジャンプ」
    if ( aY > 1 && aY < lines ) {
      bmMenu.Add( actPin, aY, meMenuChecked );	// 「行頭へジャンプ」
    }
    bmMenu.Add( endPin, lines, Check( lines ) );// 「最終行へジャンプ」
  }
  else if ( lines == 1 ) {
    bmMenu.Add( actPin, aY, meMenuChecked );	// 「行頭へジャンプ」
  }
}

// ステータスバーとポップアップメニューを表示
Status = status1 + status2 + TimerStatus( new Date(), start );
var y = bmMenu.Track( menuPosMouse );

// ブックマークを設定/解除
if ( y == 20000000 ) {
  editor.ExecuteCommandByID( MEID_EDIT_TOGGLE_BOOKMARK = 2126 );
}
// ジャンプ
else if ( y > 0 ) {
  Redraw = false;
  s.SetActivePoint( posMode, 1, y );

  // アクティブ行へのジャンプではスクロール位置をリセット
  if ( y == aY ) { ScrollY = aY; }
  // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択する
  if ( blockSelectEnable && act !== anc ) {
    var jumpDown = ( aY < y );
    // もとの選択範囲の文字列を含んだままにする
    if ( keepInitialSelect ) {
      var tp = Math.min( anc, act );
      var bp = Math.max( anc, act );
      s.SetAnchorPos( jumpDown ? tp : bp );
    }
    // または、もとの "キャレット位置" を範囲選択の基点にする
    else { s.SetAnchorPos( act ); }
    // 下の行へジャンプしたとき、ジャンプ先の行全体を選択範囲に含める
    if ( jumpDown && endOfBookmarkLine ) {
      s.EndOfLine( true, mePosLogical );
      ScrollX = 1;	// 水平スクロールを左端にリセット
    }
  }
  ScrollY = ( ScrollY == sY )
          ? sY : s.GetActivePointY( mePosView );
  Redraw = true;
}

// ---------- ▼ 関数 ▼ ---------- //

/**
 * 関数 MenuKey( str, num )
 * ポップアップメニューに表示するラベルを生成する
 * 
 * ・行頭空白を除去、空白文字を圧縮
 * ・「¥」(U+005C) を「∖」に置換: 「∖」(U+2216) または 「╲」(U+2572) 「﹨」(U+FE68)
 * ・「&」 を補完
 * ・「a-z」 と判別しづらいメタ文字を全角に置換  →  条件つきに変更
 * ・文字数を切り詰め
 * ・行番号をケタ埋め:	EN SPACE 「 」(U+2002)
 */
function MenuKey( str, num ) {
  var menuKey = ( str ) ? /^[\t  ]+$/.test( str ) ? "( 空白行 )" : str
                        : "( 空行 )";
  menuKey = menuKey.replace( /^[\t  ]+/, "" )
                   .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " )
                   .slice( 0, menuWidth *2 )
                // .replace( /[\\]/g, "∖" )
                   .replace( /&/g, "&&" );
  if ( toFullWidth ) {
    menuKey = menuKey.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g, function( tmp ) {
        return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
    } );
  }
  menuKey = ( menuKey.length > menuWidth )
          ? menuKey.slice( 0, menuWidth ) + " ..." : menuKey;
  return ( "          &" + num ).slice( - linesWidth - 1 ) + ":  " + menuKey;
}

/**
 * 関数 TimerStatus( end, start )
 * start からの経過時間計測
 * [ s. sss 秒 ] で返す
 */
function TimerStatus( end, start ) {
  var elapsedSec = ( ( end - start ) / 1000 ).toFixed( 3 );
  return "  [ " + elapsedSec.replace( /\./, ". " ) + " 秒 ]";
}

/**
 * 関数 GetIniOptionNum( key )
 * 引数で指定された設定項目の「値」を返す(※数値のみ)
 */
function GetIniOptionNum( key ) {
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  // Mery.ini を探す
  var meryPath = editor.FullName;
  var mery     = Fso.GetBaseName( meryPath );
  var iniPath  = meryPath.replace( /\.exe$/i, ".ini" );
  if ( ! Fso.FileExists( iniPath ) ) {
    var WshShell = new ActiveXObject( "WScript.Shell" );
    iniPath = WshShell.SpecialFolders( "APPDATA" )
            + "\\Mery\\" + mery + ".ini";
  }
  // Mery.ini を読み込む
  var iniFile = Fso.OpenTextFile( iniPath, 1 );
  var iniText = iniFile.ReadAll();
  iniFile.Close();
  // 項目の値を返す
  return + RegExp( "^" + key + "=(\\d+)$", "m" ).exec( iniText )[1];
}

/**
 * ---------- ▼ 更新履歴 ▼ ----------
 * 2019/03/29:
 * ・sukemaru版をマクロライブラリに投稿。
 * 2019/04/08:
 * ・Quit() の削除と、軽微な変更。
 * 2019/04/16:
 * ・Mery 2.7.0 でのブックマークの仕様変更に対応。【include版】を追加。
 * 2019/12/05:
 * ・「ブックマークを設定/解除」 をメニューに追加
 * ・メニュー内にブックマーク件数を表示
 * ・「メニューを表示する位置」 の設定を追加
 * ・「もとの選択範囲をジャンプ後の範囲選択に含める」 を設定項目に追加
 * ・【include版】を大幅に変更。
 */


include版


設定変更サブメニュー

分割サブメニュー
分割サブメニュー
  • ブックマークの件数が多いときはサブメニューに分割表示します。
初期値では 20 件超で分割サブメニューを表示する設定にしてありますが、モニターの高さに合わせて分割単位を変更することができます(設定変更サブメニューで変更可)。



  • includeライブラリ を使用し、設定内容は外部ファイルに保存します(◆settingEnable 変数で設定。デフォルト: 有効)。
  • あらかじめ includeライブラリ をインストールしてください。
  • 設定内容の保存場所は Mery\Macros\MacroSettings\<ブックマーク一覧ジャンプ>.json
    または %AppData%\Mery\MacroSettings\<ジャンプ>.json です。
    (※ <ブックマーク一覧ジャンプ> の部分はこのマクロのファイル名)
「▼ 通常版 設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。
※ ただし、settingEnable 変数を完全に無効にする場合は、ソースコード内で変更する必要があります。
→ settingEnable を無効にした場合は「通常版」と同じ状態になり、「設定変更」サブメニューが表示されなくなります。


#title = "ブックマークジャンプ"
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
#include "include/IO.js"
#icon = "bookmark_list.ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243

/**
 * ---------------------------------------------------------
 * 「ブックマーク一覧ジャンプ」
 *   Orginal created by: Kuro, goat (2012/06/22 - 2012/09/24)
 *   Modified by:        sukemaru   (2018/08/08 - 2019/12/05)
 *   【include 版】                  (2019/04/15 - 2019/12/05) 
 * ---------------------------------------------------------
 * 【include 版】
 * ・「設定変更」サブメニューを表示
 * ・ 設定内容を外部ファイル(JSON)に保存
 * ・ ブックマーク件数が多いときはサブメニューに分割表示
 * 
 *    設定ファイルの保存場所は
 *      Mery\Macros\MacroSettings\<jsonName>.json 
 *    または
 *      %AppData%\Mery\MacroSettings\<jsonName>.json
 *    ※ <jsonName> の部分はこのマクロのファイル名(設定項目で変更可)
 */

var start = new Date();		// 所要時間計測(開始)

var setting = {};
// ---------- ▼ 【include版】 初期設定項目 ▼ ---------- //

// ◆ 「設定変更」サブメニューを表示するか?
setting.settingEnable = true;		// (true する / false しない)

// ◆ JSON ファイルのベース名
var jsonName = ScriptName.replace( /\.js$/i, "" );	// 既定値

// 任意の名前に変更する場合はアンコメントして書き換える
// jsonName = "ブックマーク一覧ジャンプ";

// ---------- ▼ 通常版 設定項目 ▼ ---------- //
// 【include版】ではポップアップメニューから変更すること

setting.blockSelectEnable = false;
setting.keepInitialSelect = false;
setting.endOfBookmarkLine = false;
setting.autoLineModeEnable = false;
setting.lineColumnView = 0;			// ( 0 論理行 / 1 表示行 )
setting.extraMenuEnable = true;
setting.menuPinEnable = true;
setting.menuScrollEnable = true;	// スペースキーでスクロール
setting.subMenuEnable = true;		// 分割サブメニュー
setting.subMenuHeight = 20;		// 初期値 = 20; (20 件* ずつに分割)
setting.menuWidth = 55;			// (30-100 程度 ※半角文字だけの行では×2)
setting.toFullWidth = false;	// [!"%'(),.:;@\[\]`a-z{|}]
setting.menuPosMouse = true;	// ( true マウス位置 / false カーソル位置 )

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


// menuWidth = ( menuWidth < 30 )  ? 25
//           : ( menuWidth > 100 ) ? 105
//                                 : menuWidth;

// ソースコードの「設定項目」内の初期値を保存する
var initialSettings = setting;
if ( initialSettings.settingEnable ) {
  // JSON ファイルから設定を読み込む
  jsonName = jsonName || ScriptName.replace( /\.js$/i, "" );
  setting  = IO.Deserialize( setting, jsonName );
  // 通常版の設定変数に再代入
  var settingEnable		 = setting.settingEnable;
  var blockSelectEnable	 = setting.blockSelectEnable;
  var keepInitialSelect	 = setting.keepInitialSelect;
  var endOfBookmarkLine	 = setting.endOfBookmarkLine;
  var extraMenuEnable	 = setting.extraMenuEnable;
  var menuPinEnable		 = setting.menuPinEnable;
  var menuScrollEnable	 = setting.menuScrollEnable;
  var autoLineModeEnable = setting.autoLineModeEnable;
  var lineColumnView	 = setting.lineColumnView;
  var subMenuEnable		 = setting.subMenuEnable;
  var subMenuHeight		 = setting.subMenuHeight;
  var menuWidth			 = setting.menuWidth;
  var toFullWidth		 = setting.toFullWidth;
  var menuPosMouse		 = setting.menuPosMouse;
}

do {
  if ( y ) { start = new Date(); }	// ループしたときはタイマーをリセット

  // Mery.ini から「行の表示方法」を取得する
  if ( autoLineModeEnable ) {
    lineColumnView = GetIniOptionNum( "LineColumnView" );
  }
  // 表示行/論理行 の定数
  var posMode  = lineColumnView ? mePosView : mePosLogical;
  var lineMode = lineColumnView ? meGetLineView : 0;
  // 全体の行数
  var d = editor.ActiveDocument;
  var lines = d.GetLines( lineMode );
  var linesWidth = lines.toString().length;
  // カーソルと選択範囲の位置を保存
  var s = d.selection;
  var act = s.GetActivePos(),  anc = s.GetAnchorPos();
  var aY = s.GetActivePointY( posMode );
  var sX = ScrollX, sY = ScrollY;
  // ブックマーク行の変数
  var bmArray = [],  flag = 0,  isBookmarked = 0;
  var bmY,  bmStr;

  // ブックマーク行を検索
  Redraw = false;
  // 先頭行がブックマークされているか
  s.SetActivePoint( mePosLogical, 1, 2 );
  if ( s.PreviousBookmark() ) {
    bmY = s.GetActivePointY( posMode );
    bmStr = d.GetLine( bmY, lineMode );
    if ( bmY == aY ) { flag = 1;  isBookmarked = 1; }
    bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
  }
  // 2行目以降のブックマークを探す
  s.SetActivePos( 0 );
  while ( s.NextBookmark() ) {
    bmY = s.GetActivePointY( posMode );
    bmStr = d.GetLine( bmY, lineMode );
    if ( bmY == aY ) { flag = 1;  isBookmarked = 1; }
    else { flag = 0; }
    bmArray.push( [ MenuKey( bmStr, bmY ), bmY, flag ] );
  }
  // ブックマークの件数
  var bmCount = bmArray.length;

  // カーソル位置と選択範囲を復帰
  s.SetActivePos( act );  s.SetAnchorPos( anc );
  ScrollX = sX;  ScrollY = sY;
  Redraw = true;

  // ステータスラベル
  var status1 = bmCount
              ? " ブックマーク: " + bmCount + " 件 /"
              : " ブックマークがありません。";
  var status2 = " 全体の行数: "
              + lines.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )
              + ( lineColumnView ? " 行 (表示行)" : " 行 (論理行)" );

  // ポップアップメニューの準備
  var bmMenu = CreatePopupMenu();

  // 「ブックマーク一覧」ポップアップメニュー
  // または「ブックマークがありません」ポップアップメニュー
  BookmarkMenu( bmMenu, bmArray, aY );

  // ステータスバーとポップアップメニューを表示
  Status = status1 + status2
         + TimerElapsed( new Date(), start );
  var y = bmMenu.Track( menuPosMouse );

  /* 【include版】の設定変更サブメニュー項目を選択した場合 */
  if ( y >= 10000000 && y <= 10000018 ) {
    SettingsChange( y );
  }
// 設定変更した場合はメニューを再表示する
} while ( ! menuPosMouse && y >= 10000000 && y <= 10000015 );	

// ブックマークを設定/解除
if ( y == 20000000 ) {
  editor.ExecuteCommandByID( MEID_EDIT_TOGGLE_BOOKMARK = 2126 );
}

// ジャンプ
else if ( y && y < 10000000 ) {
  Redraw = false;
  s.SetActivePoint( posMode, 1, y );

  // アクティブ行へのジャンプではスクロール位置をリセット
  if ( y == aY ) { ScrollY = aY; }
  // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択する
  if ( blockSelectEnable && act !== anc ) {
    var jumpDown = ( aY < y );
    // もとの選択範囲の文字列を含んだままにする
    if ( keepInitialSelect ) {
      var tp = Math.min( anc, act );
      var bp = Math.max( anc, act );
      s.SetAnchorPos( jumpDown ? tp : bp );
    }
    // または、もとの "キャレット位置" を範囲選択の基点にする
    else { s.SetAnchorPos( act ); }
    // 下の行へジャンプしたとき、ジャンプ先の行全体を選択範囲に含める
    if ( jumpDown && endOfBookmarkLine ) {
      s.EndOfLine( true, mePosLogical );
      ScrollX = 1;	// 水平スクロールを左端にリセット
    }
  }
  ScrollY = ( ScrollY == sY )
          ? sY : s.GetActivePointY( mePosView );
  Redraw = true;
}

// ---------- ▼ 関数 ▼ ---------- //

/**
 * 関数 BookmarkMenu( objMenu, objArray )
 * 「ブックマーク一覧」ポップアップメニュー
 * または「ブックマークがありません」ポップアップメニュー
 * 
 * ※各変数はグローバルスコープのものを利用する
 */
function BookmarkMenu( bmMenu, bmArray ) {
  // アクティブ行のチェックマークフラグ (meMenuChecked = 1)
  var Check = function( num ) { return ( num == aY ) *1; }
  // ピン止めアイテム
  var bmPin		= MenuIndent( aY, linesWidth )
           		+ ":  ブックマークを設定/解除 (&B)";
  var countPin	= MenuIndent()
              	+ " ▼ " + status1.slice( 0, -1 ) + " ▼";
  var cancelPin	= MenuIndent()
               	+ "  キャンセル\t& ";
  var topPin	= MenuIndent( 1, linesWidth )
            	+ ":  ** 先頭行へジャンプ (&T) **";
  var endPin	= MenuIndent( lines, linesWidth )
            	+ ":  ** 最終行へジャンプ (&E) **";
  var actPin	= MenuIndent( aY, linesWidth )
            	+ ":    行頭へジャンプ (&A)";

  // メニューの最上部に「設定/解除」をピン止め
  if ( bmCount || extraMenuEnable ) {
    bmMenu.Add( bmPin, 20000000, isBookmarked );
    bmMenu.Add( "----", 0, meMenuSeparator );

    /* 【include版】の設定変更サブメニュー */ 
    if ( initialSettings.settingEnable ) {
      SettingsMenu( bmMenu );
    }
  }

  // 「ブックマーク一覧」ポップアップメニュー
  if ( bmCount ) {
  // メニュー上部のピン止めアイテム
    bmMenu.Add( cancelPin, 0 );					// 「キャンセル」
    bmMenu.Add( "----", 0, meMenuSeparator );
    bmMenu.Add( "----", 0, meMenuSeparator );
    // メニュー上部のピン止めアイテム
    if ( menuPinEnable && ( bmArray[0][1] > 1
        || ! isBookmarked && aY > 1 && aY < lines ) ) {
      if ( bmArray[0][1] > 1 ) {
        bmMenu.Add( topPin, 1, Check( 1 ) );		// 「先頭行へジャンプ」
      }
      if ( ! isBookmarked && aY > 1 && aY < lines ) {
        bmMenu.Add( actPin, aY, meMenuChecked );	// 「行頭へジャンプ」
      }
      if ( bmCount > subMenuHeight && bmArray[ bmCount -1 ][1] !== lines ) {
        bmMenu.Add( endPin, lines, Check( lines ) );	// 「最終行へジャンプ」
      }
      bmMenu.Add( "----", 0, meMenuSeparator );
    }

    // 「分割サブメニュー」をピン止め
    if ( subMenuEnable && bmCount > subMenuHeight ) {
       var subMenu = DevidedSubMenuBM( bmMenu, bmArray );
    }

    // ブックマーク一覧
    bmMenu.Add( countPin, 0, 0 );				// ▼ ブックマーク: n 件 ▼
    for ( var i = 0; i < bmCount; i ++ ) {
      if ( i % 10 == 0 && i > 0 ) {
        bmMenu.Add( "----", 0, meMenuSeparator );	// 10行ごとにセパレータ
        if ( menuScrollEnable && i % subMenuHeight == 0 ) {
          bmMenu.Add( cancelPin, 0 );				// 20行* ごとに「キャンセル」
          bmMenu.Add( "----", 0, meMenuSeparator );
        }
      }
      // ブックマーク行をポップアップメニューに追加
      bmMenu.Add( bmArray[i][0], bmArray[i][1], Check( bmArray[i][1] ) );
    }

    // メニュー下部のピン止めアイテム
    if ( menuPinEnable && bmArray[ bmCount -1 ][1] !== lines ) {
      bmMenu.Add( "----", 0, meMenuSeparator );
      bmMenu.Add( endPin, lines, Check( lines ) );	// 「最終行へジャンプ」
    }
    if ( menuScrollEnable && bmCount > subMenuHeight ) {
      bmMenu.Add( "----", 0, meMenuSeparator );
      bmMenu.Add( cancelPin, 0 );					// 「キャンセル」
    }
  }

  // 「ブックマークがありません」ポップアップメニュー
  else if ( extraMenuEnable ) {
    bmMenu.Add( "----", 0, meMenuSeparator );
    // ステータスラベルをピン止め
    bmMenu.Add( "※ " + status1 + "  ※\t& ", 0 );	// 「ブックマークがありません」
    if ( ! menuPinEnable ) {
      bmMenu.Add( " " + status2, 0 );				// 全体の行数
    }
    // ピン止めアイテム
    if ( menuPinEnable ) {
      if ( lines > 1 ) {
        bmMenu.Add( "----", 0, meMenuSeparator );	
        bmMenu.Add( topPin, 1, Check( 1 ) );		// 「先頭行へジャンプ」
        if ( aY > 1 && aY < lines ) {
          bmMenu.Add( actPin, aY, meMenuChecked );	// 「行頭へジャンプ」
        }
        bmMenu.Add( endPin, lines, Check( lines ) );// 「最終行へジャンプ」
      } else {
        bmMenu.Add( actPin, aY, meMenuChecked );	// 「行頭へジャンプ」
      }
    }
  }
}

/**
 * 関数 MenuIndent( str )
 * メニューラベルの字下げ
 */
function MenuIndent( str ) {
  str = str || "";
  var appendix = /^&\d/.test( str ) ? 1 : 0;
  return ( "          " + str ).slice( - linesWidth - appendix );
}

/**
 * 関数 MenuKey( str, num )
 * ポップアップメニューに表示するラベルを生成する
 * 
 * ・行頭空白を除去、空白文字を圧縮
 * ・「&」 を補完
 * ・「¥」(U+005C) を 「∖」(U+2216) に置換:    または 「╲」(U+2572), 「﹨」(U+FE68)
 * ・ゼロ幅や特殊な空白文字を豆腐に置換:	→ 「▯」(U+25AF) または 「⊠」(U+22A0)
 * ・判別しづらい半角記号を全角に置換:		!"%'(),.:;@[]`{|}
 *     + 「a-z」 を全角に置換
 *   または半角記号の前後にスキマをつける:	HAIR SPACE 「 」(U+200A)
 * ・文字数を切り詰め
 * ・行番号を空白でケタ埋め:				EN SPACE 「 」(U+2002)
 */
function MenuKey( str, num ) {
  var keyWidth = /^[\s ]*[\s!-~∖▯⊠ ]+$/.test( str ) ? menuWidth * 2 : menuWidth;
  var regBlankLine = /^[\s \u00A0\u1680\u180e\u2000-\u200A\u2028\u2029\u202F\u205F\uFEFF]+$/;
  var regBlank = /[\u00A0\u1680\u180E\u2000-\u200E\u2028\u2029\u202F\u205F\u2062\u2063\uFEFF]/g
  var menuKey = ( str ) ? regBlankLine.test( str ) ? "( 空白行 )" : str
                        : "( 空行 )";
  menuKey = menuKey.replace( /^[\t  ]+/, "" )
                   .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " › " )
                   .slice( 0, keyWidth + 1 )
                   .replace( /[&]/g, "&&" )
                   .replace( /[\\]/g, "∖" )
                   .replace( regBlank, "▯" );
  if ( toFullWidth ) {
    menuKey = menuKey.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g,
      function( tmp ) {
        return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
      } );
  }
  else {
    // スラッシュ 以外の ascii 記号と fijl に HAIR SPACE (U+200A) を付加
    menuKey = menuKey.replace( /[!-%'-.:-@\[-`{-~fijl¥⊠▯]|&&/g, " $& " )
                     .replace( /\u200A+/g, " " )
                     .replace( /\u2002\u200A/g, " " );
    keyWidth = keyWidth
             + ( menuKey.match( /[\u200A›↲]|&&/g ) || "" ).length;
  }
  menuKey = ( menuKey.length > keyWidth )
          ? menuKey.slice( 0, keyWidth ) + " ..."
          : menuKey;
  num = MenuIndent( "&" + num ) + ": ";
  return num + menuKey;
}

/**
 * 関数 TimerElapsed( end, start )
 * start からの経過時間を [ s. sss 秒 ] で返す
 */
function TimerElapsed( end, start ) {
  var elapsedSec = ( ( end - start ) / 1000 ).toFixed( 3 );
  return "  [ " + elapsedSec.replace( /\./, ". " ) + " 秒 ]";
}

/**
 * 関数 GetIniOptionNum( key )
 * 引数で指定された設定項目の「値」を返す(※数値のみ)
 */
function GetIniOptionNum( key ) {
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  // Mery.ini を探す
  var meryPath = editor.FullName;
  var mery     = Fso.GetBaseName( meryPath );
  var iniPath  = meryPath.replace( /\.exe$/i, ".ini" );
  if ( ! Fso.FileExists( iniPath ) ) {
    var WshShell = new ActiveXObject( "WScript.Shell" );
    iniPath = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
            + "\\Mery\\" + mery + ".ini";
  }
  // Mery.ini を読み込む
  var iniFile = Fso.OpenTextFile( iniPath, 1 );
  var iniText = iniFile.ReadAll();
  iniFile.Close();
  // 項目の値を取得する
  return + RegExp( "^" + key + "=(\\d+)$", "m" ).exec( iniText )[1];
}

/**
 * 関数 DevidedSubMenuBM( objPopupMenu, items )
 * サブメニュー「※ 20* 件ずつ表示 ※  ▶」/「1 - 20* 件目  ▶」に
 * 配列の「アイテム」を分割表示する
 * ※「20*」の部分の数値は設定用変数 subMenuHeight で指定
 */
function DevidedSubMenuBM( objPopupMenu, items ) {
  var len      = items.length;
  var lenWidth = len.toString().length;
  var smRow    = Math.ceil( len / subMenuHeight );
  var smSq     = subMenuHeight * subMenuHeight;
  var smArray  = [];		// SubMenu Array
  var smLabelArray = [];	// SubMenu-Label Array
  var smId,  _from,  _to,  tick,  ticked;

  var subMenu   = CreatePopupMenu();
  var popupMenu = ( subMenuEnable == 1 )
                ? subMenu : objPopupMenu;
  if ( subMenuEnable == 1 ) {
    // 「※ 20* 件ずつ表示 ※  ▶」をピン止め
    objPopupMenu.AddPopup( MenuIndent() + " ※   "
                         + subMenuHeight + " 件ずつに分割して表示 (&D)  ※"
                         , subMenu );
    objPopupMenu.Add( "", 0, meMenuSeparator );
  }

  // サブメニューと配列のアイテムをポップアップメニューに追加していく
  for ( var i = 0; i < len; i ++ ) {
    // セパレータと「キャンセル」
    if ( i % subMenuHeight == 0 ) {
      if ( smRow > 10 && i != 0 && i % ( subMenuHeight * 10 ) == 0 ) {
        popupMenu.Add( "", 0, meMenuSeparator );
        if ( len > smSq && i % smSq == 0 ) {
          popupMenu.Add( MenuIndent() + "  キャンセル\t& ", 0 );
          popupMenu.Add( "", 0, meMenuSeparator );
        }
      }

      // 配列 smArray 内にメニュー項目「_from - _to 件目  ▶」を生成
      smArray.push( CreatePopupMenu() );
      // 「_from - _to 件目  ▶」メニューの index
      smId  = smArray.length - 1;	
      //  _from と _to の値
      _from = smId * subMenuHeight + 1;	
      _to   = Math.min( smArray.length * subMenuHeight, len );
      // 「_from - _to 件目」メニューラベルを別途の配列 smLabelArray に
      smLabelArray.push( ( "       &" + _from ).slice( - lenWidth - 1 )
                       + " - "
                       + ( "       " + _to ).slice( - lenWidth )
                       + " 件目" );
    }

    // 「アイテム」の配列から「_from - _to 件目」サブメニュー配下に
    // ※ smArray[smId] は CreatePopupMenu オブジェクト
    smArray[ smId ].Add( items[i][0], items[i][1], items[i][2] );
    // アクティブ行のフラグのついた行
    if ( items[i][2] ) { ticked = smId; }

    // 10 行ごとにセパレータ、さいごに「キャセル」行
    if ( ( i + 1 ) % 10 == 0  || i == len - 1) {
      smArray[ smId ].Add( "", 0, meMenuSeparator );
      if ( ( i + 1 ) % subMenuHeight == 0 || i == len - 1 ) {
        smArray[ smId ].Add( MenuIndent() + "  キャンセル\t& ", 0 );
      }
    }
  }	// end: for ()

  // 「_from - _to 件目」メニュー項目をメニューに追加
  for ( var i = 0; i < smArray.length; i ++ ) {
    // ※ AddPopup には meMenuChecked を付けられないので "*" 記号で代替
    tick = ( i == ticked ) ? "* " : "  ";
    popupMenu.AddPopup( tick + smLabelArray[i], smArray[i] );
  }
  // 「_from - _to 件目」のさいごに「キャセル」を追加
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "       ".slice( - lenWidth ) + "キャンセル\t& ", 0 );

  return subMenu;
}


// ---------- ▼ 【include版】 関数 ▼ ---------- //

/**
 * 関数 SettingsMenu( objPopupMenu )
 * ポップアップメニューに「設定変更」サブメニューを表示する
 * 引数: メインスコープのポップアップメニューオブジェクト
 */
function SettingsMenu( objMenu ) {
  var subMenu = CreatePopupMenu();
  // メニューのオプションフラグ
  var grayFlag  = ! settingEnable ? meMenuGrayed : 0;
  var grayFlag1 = ( ! blockSelectEnable || ! settingEnable )
                ?  meMenuGrayed : 0;
  var keepInitialSelectFlag = ( keepInitialSelect && blockSelectEnable )
                            ? meMenuChecked : 0;
  var endOfBookmarkLineFlag = ( endOfBookmarkLine && blockSelectEnable )
                            ? meMenuChecked : 0;
  var grayFlag2 = ( autoLineModeEnable || ! settingEnable )
                ?  meMenuGrayed : 0;
  var maxHeightFlag = ( ! settingEnable || ! subMenuEnable || subMenuHeight >= 100 )
                          ? meMenuGrayed : 0;
  var minHeightFlag = ( ! settingEnable || ! subMenuEnable || subMenuHeight < 20 )
                          ? meMenuGrayed : 0;
  var maxWidthFlag = menuWidth > 100 ? meMenuGrayed : 0;
  var minWidthFlag = menuWidth < 30 ? meMenuGrayed : 0;

  // 「設定変更」サブメニューのアイテム
  objMenu.AddPopup( MenuIndent() + "  設定を変更する (&S)", subMenu );
  objMenu.Add( "----", 0, meMenuSeparator );

  subMenu.Add( "  ジャンプ先の行まで 範囲選択する (&B)",
               10000001, blockSelectEnable + grayFlag );
  subMenu.Add( "  もとの選択範囲を ジャンプ後の範囲選択に含める (&K)",
               10000002, keepInitialSelectFlag + grayFlag1 );
  subMenu.Add( "  下の行にジャンプしたときに 行全体を選択 (&E)",
               10000003, endOfBookmarkLineFlag + grayFlag1 );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  ブックマークがなくても メニューを表示する (&N)",
               10000004, extraMenuEnable + grayFlag );
  subMenu.Add( "「先頭行/最終行/行頭 へジャンプ」 を表示する (&P)",
               10000005, menuPinEnable + grayFlag );
  subMenu.Add( "  " + subMenuHeight + " 件ごとに 「キャンセル」 を表示する (&C)",
               10000012, menuScrollEnable + grayFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "「行の表示方法」 を自動設定する (&A)",
               10000006, autoLineModeEnable + grayFlag );
  subMenu.Add( "  行数を 「論理行」 で表示する (&L)",
               10000007, !lineColumnView + grayFlag2 );
  subMenu.Add( "  行数を 「表示行」 で表示する (&V)",
               10000007, lineColumnView + grayFlag2 );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "「分割サブメニュー」 を表示する (&D)"
             , 10000013, subMenuEnable + grayFlag );
  subMenu.Add( "  サブメニューの分割単位を 10行 増やす (&X) \t"
             + subMenuHeight
             + ( subMenuHeight >= 100 ? "" : " → " + ( subMenuHeight + 10 ) )
             , 10000014, maxHeightFlag );
  subMenu.Add( "  サブメニューの分割単位を 10行 減らす (&Z) \t"
             + subMenuHeight
             + ( subMenuHeight < 20 ? "" : " → " + ( subMenuHeight - 10 ) )
             , 10000015, minHeightFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  メニューの表示幅を 10文字 増やす (&+)\t"
             + menuWidth
             + ( menuWidth > 100 ? "" : " → " + ( menuWidth + 10 ) ),
               10000008, maxWidthFlag + grayFlag );
  subMenu.Add( "  メニューの表示幅を 10文字 減らす (&-)\t"
             + menuWidth
             + ( menuWidth < 30 ? "" : " → " + ( menuWidth - 10 ) ),
               10000009, minWidthFlag + grayFlag );
  subMenu.Add( "  半角 a-z を全角で表示する (&F)",
               10000010, toFullWidth + grayFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  入力カーソル の位置 にメニューを表示する"
             + ( menuPosMouse ? " (&M)" : "" ),
               10000011, !menuPosMouse + grayFlag );
  subMenu.Add( "  マウスポインタの位置 にメニューを表示する"
             + ( menuPosMouse ? "" : " (&M)" ),
               10000011, menuPosMouse + grayFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  設定内容をロックする (&S)", 10000000, !settingEnable*1 );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "「ジャンプ」マクロの JS ファイルを開く (&O)", 10000016 );
  subMenu.Add( "「ジャンプ」マクロの JSON ファイルを開く (&J)", 10000017 );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  設定を初期化する (&I)", 10000018, grayFlag );
  subMenu.Add( "----", 0, meMenuSeparator );
  subMenu.Add( "  キャンセル\t& ", 0 );
}

/**
 * 関数 SettingsChange( num )
 * ポップアップメニューで選択した項目の設定状態を変更する
 * ※ 設定変更でのメニュー再表示 do - while ループのさいに
 *    JSON の再読みこみを省略するために
 *    ローカル変数 = JSON 項目 = 設定値  の形式で記述する
 */
function SettingsChange( num ) {
  // var jsonPath = JsonDir() + "\\" + jsonName + ".json";
  var sIsChanged = true;
  switch ( num ) {
    case 10000000:	// 設定内容をロックする
      settingEnable = setting.settingEnable
                    = ! settingEnable;
      break;
    case 10000001:	// ジャンプ先の行まで 範囲選択する
      blockSelectEnable = setting.blockSelectEnable
                        = ! blockSelectEnable;
      break;
    case 10000002:	// もとの選択範囲を ジャンプ後の範囲選択に含める
      keepInitialSelect = setting.keepInitialSelect
                        = ! keepInitialSelect;
      break;
    case 10000003:	// 下の行にジャンプしたときに 行全体を選択
      endOfBookmarkLine = setting.endOfBookmarkLine
                        = ! endOfBookmarkLine;
      break;
    case 10000004:	// ブックマークがなくても メニューを表示する
      extraMenuEnable = setting.extraMenuEnable
                      = ! extraMenuEnable;
      break;
    case 10000005:	// 「先頭行/最終行/行頭 へジャンプ」 を表示する
      menuPinEnable = setting.menuPinEnable
                    = ! menuPinEnable;
      break;
    case 10000006:	// 「行の表示方法」 を自動設定する
      autoLineModeEnable = setting.autoLineModeEnable
                         = ! autoLineModeEnable;
      break;
    case 10000007:	// 行数を 「論理行/表示行」 で表示する
      lineColumnView = setting.lineColumnView
                     = ! lineColumnView;
      break;
    case 10000008:	// メニューの表示幅を 10文字 増やす
      menuWidth = setting.menuWidth += 10;
      break;
    case 10000009:	// メニューの表示幅を 10文字 減らす
      menuWidth = setting.menuWidth -= 10;
      break;
    case 10000010:	// 半角英文字を全角で表示する
      toFullWidth = setting.toFullWidth
                  = ! toFullWidth;
      break;
    case 10000011:	// 入力カーソル/マウスポインタ の位置 にメニューを表示する
      menuPosMouse = setting.menuPosMouse
                   = ! menuPosMouse;
      break;
    case 10000012:	// 20 件* ごとに 「キャンセル」 を表示する
      menuScrollEnable = setting.menuScrollEnable
                       = ! menuScrollEnable;
      break;
    case 10000013:	// 「分割サブメニュー」 を表示する
      subMenuEnable = setting.subMenuEnable
                    = ! subMenuEnable;
      break;
    case 10000014:	// サブメニューの分割単位を 10行 増やす
      subMenuHeight = setting.subMenuHeight += 10;
      break;
    case 10000015:	// サブメニューの分割単位を 10行 減らす
      subMenuHeight = setting.subMenuHeight -= 10;
      break;
    case 10000016:	// 「ジャンプ」マクロの JS ファイルを開く
      sIsChanged = false;
      Status = " " + ScriptFullName;
      OpenJumpJS( "setting.settingEnable = " );
      break;
    case 10000017:	// 「ジャンプ」マクロの JSON ファイルを開く
      sIsChanged = false;
      OpenJumpJson( jsonName );
      break;
    case 10000018:	// 設定を初期化する
      sIsChanged = false;
      CheckJsonFile( jsonName );
      break;
    default:
      sIsChanged = false;
      break;
  }
  if ( sIsChanged ) {
    IO.Serialize( setting, jsonName );
    Status = " 設定の変更を保存しました。";
  }
}


/**
 * 関数 OpenJumpJS( str )
 * このマクロの JS ファイルを開いて設定項目の行にジャンプする
 * 引数: 検索文字列(設定項目の行の文字列)
 */
function OpenJumpJS( str ) {
  Redraw = false;
  var targetStr = str || "";
  var eCount = editors.Count;
  var dCount,  dItem,  ee,  dd;
  var isOpen = false;
  OuterLoop:
  for ( var j = 0; j < eCount; j ++ ) {
    dCount = editors.Item( j ).documents.Count;
    for ( var i = 0; i < dCount; i ++ ) {
      dItem = editors.Item( j ).documents.Item( i );
      if ( dItem.FullName == ScriptFullName ) {
        js = dItem;  isOpen = true;
        break OuterLoop;
      }
    }
  }
  if ( ! isOpen ) {
    editor.NewFile();
    ee = ( editor.EnableTab ) ? editor : editors.Item( eCount );
    ee.OpenFile( ScriptFullName, 0 );
    js = ee.ActiveDocument;
  }
  js.Activate();
  var settingPos = js.Text.indexOf( targetStr );
  js.selection.SetActivePos( settingPos + targetStr.length );
  // js.selection.WordRight( true );	// true を範囲選択
  ScrollY = js.selection.GetActivePointY( mePosView );
  Redraw = true;
}

/**
 * 関数 OpenJumpJson( jsonName )
 * JSON ファイルを開く
 */
function OpenJumpJson( jsonName ) {
  IO.Serialize( setting, jsonName );
  Sleep( 500 );
  var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
  if ( IO.Path.IsExist( jsonPath )
      && JsonContents( jsonPath ).length ) {
    var confirmStr = "設定ファイルは正常です \n\n"
                   + JsonContents( jsonPath )
                   + "設定ファイルを開きますか? ";
    if ( Confirm( confirmStr ) ) {
      var WshShell = new ActiveXObject( "WScript.Shell" );
      WshShell.Run( '"' + editor.FullName + '" "' + jsonPath + '"' );
    }
  } else {
    Alert( "設定ファイルがありません \n"
         + jsonDir + "  " );
  }
}

/** 
 * 関数 CheckJsonFile( jsonName )
 * JSON ファイルの初期化/実在確認
 */
function CheckJsonFile( jsonName ) {
  var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
  var confirmStr = jsonPath + "  \n\n"
                 + "設定ファイルを初期化しますか? "
  if ( Confirm( confirmStr ) ) {
    IO.Serialize( initialSettings, jsonName );
    Sleep( 500 );
    var alertStr = ( IO.Path.IsExist( jsonPath )
                     && JsonContents( jsonPath ).length )
                 ? "設定ファイルは正常です \n\n"
                   + JsonContents( jsonPath )
                 : "設定ファイルがありません \n"
                   + jsonDir + "  ";
    Alert( alertStr );
  }
}

/** 
 * 関数 JsonDir()
 * JSON ファイルの親フォルダのパス
 */
function JsonDir() {
  var jsonDir;
  if ( IO.Path.IsExist( editor.FullName.replace( /\.exe$/i, ".ini" ) ) ) {
    jsonDir = editor.FullName.replace( /[^\\]+$/i, "" )
            + "Macros\\MacroSettings";
  } else {
    var WshShell = new ActiveXObject( "WScript.Shell" );
    jsonDir = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
            + "\\Mery\\MacroSettings";
  }
  return jsonDir;
}

/** 
 * 関数 JsonContents( jsonPath [, bool] )
 * JSON ファイルの文字列をメッセージボックス用に整形する
 * 
 * 第1引数は JSON ファイルのパス
 * 第2引数は真偽値(省略可、true なら \uHHHH をデコードする)
 */
function JsonContents( jsonPath, bool ) {
  var contents = ( jsonPath + "  \n"
  + IO.LoadFromFile( jsonPath )
      .replace( /^\{/, "\n{\n  " )
      .replace( /\}$/g, "\n}\n\n" )
      .replace( /,"/g, " ,\n  \"" )
      .replace( /":/g, "\": " )
  );
  if ( bool ) {
    // 「テキスト整形」マクロより >> 符号化/復号化 >> \uHHHH デコード
    var Decode_uHHHH = function( str ) {
      return str.replace( /\\u([0-9A-Fa-f]{4})/g, function( s, n ) {
        return String.fromCharCode( Number( "0x" + n ) );
      } );
    };
    contents = Decode_uHHHH( contents );
  }
  if ( toFullWidth ) {
    // 半角アルファベットの小文字を全角にするなら
    contents = contents.replace( /[a-z]/g,
      function( tmp ) {
        return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
      } );
  }
  return contents;
}



/**
 * ---------- ▼ 更新履歴 ▼ ----------
 * 2019/03/29:
 * ・sukemaru版をマクロライブラリに投稿。
 * 
 * 2019/04/08:
 * ・Quit() の削除と、軽微な変更。
 * 
 * 2019/04/16:
 * ・Mery 2.7.0 でのブックマークの仕様変更に対応。
 * ・【include版】を追加。
 * 
 * 2019/12/05:
 * ・「ブックマークを設定/解除」 をメニューに追加
 * ・メニュー内にブックマーク件数を表示
 * ・「もとの選択範囲をジャンプ後の範囲選択に含める」 を設定項目に追加
 * 
 *  【include版】
 * ・ポップアップメニュー生成コードを関数セクションに移動
 * ・「分割サブメニュー」 を追加
 * ・20* 行ごとに 「キャンセル」 を追加(スペースキーでスクロール)
 *   「キャンセル」 行の追加単位は 「分割サブメニュー」 の分割設定にあわせた
 * ・「設定変更」 したときのメニュー表示のループ処理を追加
 * ・「半角 a-z を全角表示する」 を設定項目に追加
 * ・その他、設定項目の追加、動作コードの変更
 */
スポンサーリンク