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

提供: MeryWiki
ナビゲーションに移動 検索に移動
Sukemaru (トーク | 投稿記録)
sukemaru 版: 改訂版。ZIP 書庫を更新
MSY-07 (トーク | 投稿記録)
変数の宣言を追加
 
(2人の利用者による、間の12版が非表示)
1行目: 1行目:
 
== 概要 ==
= goat 版 =
 
ポップアップメニューでジャンプ先を選択。範囲指定中はジャンプ先まで選択範囲を拡大します。
ポップアップメニューでジャンプ先を選択。範囲指定中はジャンプ先まで選択範囲を拡大します。
Mery Ver.1.1.2.2840 および Ver.2.0.9.3936 での動作を確認しています。
(2012.09.24)
<source lang="javascript">
#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;
})()
</source>
== Mery ver 2.7.0 以降への対応 ==
<span style="color:#c00;"> goat 版を '''Mery ver 2.7.0 以降'''で使用する場合は、'''20''' 行目付近を以下のように書き換えてください。</span><br>
変更しない場合、ドキュメントの 1 行目がブックマークされていてもポップアップメニューに表示されなくなります。 (2019/04/16 sukemaru)
; 変更前
18.    var bookmarks = new Array();
19.    <span style="color:#c00;">StartOfDocument(false);</span>
20.    <i style="color:#408080;">// 先頭行がブックマークされてないか</i>
21.    var isTopMarked = false;
22.    <span style="color:#c00;">CharRight(false);</span>
23.    if(PreviousBookmark()){
24.      isTopMarked = true;
; 変更後
18.    var bookmarks = new Array();
19.    <span style="color:#c00;">'''//''' StartOfDocument(false);</span>
20.    <i style="color:#408080;">// 先頭行がブックマークされてないか</i>
21.    var isTopMarked = false;
22.    <b style="color:#c00;">SetActivePoint(mePosLogical, 1, 2);</b>
23.    if(PreviousBookmark()){
24.      isTopMarked = true;
= sukemaru 版 =
goat 版を元にして、ポップアップメニューを表示するまでの速度を改善し、動作設定をカスタマイズできるようにしました。
<br>
;▼ goat 版からの変更点 ▼
* ソースコード内の設定項目から "表示行" / "論理行" の選択可(初期値は "論理行")
* 設定項目で "ジャンプ先までの範囲選択" はしない状態を初期値にした
* ブックマーク行の検索 ~ ポップアップメニューの表示 までの速度を改善
* ポップアップメニューの体裁を変更(「[[ポップアップメニューで検索先にジャンプ#sukemaru_版|検索ジャンプ]]」マクロにあわせた)
* ステータスバーにブックマーク件数などの情報を表示
* 「ブックマークがありません」メニュー。
* ジャンプしたときのスクロール位置を調整。
* ジャンプ先まで範囲選択するときの先頭/末尾を調整。
* 「先頭行/最終行 へジャンプ」をメニューにピン止め表示。
* 「ブックマークを設定/解除」をメニューの先頭にピン止め表示。
* ポップアップメニューだと読み取りづらい半角英小文字・と ascii 記号の一部を全角で表示。
<br>
[[ファイル:Mery_ブックマークジャンプ(通常版)_SS.png|frameless|640px|thumb|ブックマークジャンプ(通常版)]]
<br clear=all>
* Mery でプログラミング用のフォントを使っている人だと、ポップアップメニュー内で半角 <q>'''¥'''</q> 記号 (U+005C) が'''バックスラッシュ''' (U+005C) にならないのは気落ち悪いかも? ということで、ソースコード内の関数 MenuKey() のコードのコメントアウトしてある行(300行目付近)をアンコメントすると "擬似的に" '''バックスラッシュ'''化できます <br> (※ <q>'''∖'''</q> (U+2216) に置換して表示します)。
* 行番号の桁埋め(右寄せ/空白埋め)用の[https://ja.wikipedia.org/wiki/スペース#コンピュータにおけるスペース 空白文字]を設定する項目 <syntaxhighlight lang="javascript" inline>var blankChr = " ";</syntaxhighlight> を追加し、初期値を半角空白 <code>" "</code>(U+0020)にしました(※ これまで MS UI Gothic に最適化してあった桁埋め方法をカスタマイズできるようにした)。
: MS UI Gothic では2分アキ(EN SPACE) <code>"\u2002"</code><br> Meiryo UI では和字間隔(全角空白) <code>"\u3000"</code><br> Segoe UI では図形間隔(FIGURE SPACE) <code>"\u2007"</code> にすると具合がよいようです。
== ダウンロード ==
'''ダウンロード:''' 「[[ファイル:ブックマークジャンプ.zip]]」(アイコン入り) 
<br> 最終更新: 2020/06/24
: 【共通】:行番号の桁埋め用の空白文字を指定する設定項目などを追加
: 【include版】:分割サブメニューの表示形式のカスタマイズ用設定項目を追加
: オマケとして [ 編集 ] メニューのブックマーク関連コマンドのアイコン化用マクロ×4


== ソースコード ==
== ソースコード ==
<syntaxhighlight lang="javascript" copy>
#title="ブックマーク一覧"
/*
* ブックマーク一覧をポップアップメニューにして選択ジャンプ
* Mery標準のブックマークが対象
* 範囲選択中の場合は選択範囲を拡大
*/
(function(){
// 描画停止
Redraw = false;
var sel = document.selection;
var flgBlock = !sel.IsEmpty;
//カーソル位置の保存
var activePos = sel.GetActivePos();
var anchorPos = sel.GetAnchorPos();
var sX = ScrollX;
var sY = ScrollY;


* このマクロではポップアップメニューの表示に unicode 文字を使用します。<br>
var bookmarks = [];
ファイルに保存するさいは、文字コードを shift_JIS にしないでください('''UTF-8''' や Unicode で保存する)。
var menuKey = "";
 
// 先頭行がブックマークされてないか
<source lang="javascript" style="height:80em; overflow:auto;" highlight="16-69,291">
var isTopMarked = false;
#title = "ブックマーク一覧ジャンプ"
sel.SetActivePoint(mePosLogical, 1, 2);
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
if(sel.PreviousBookmark()){
#icon = "bookmark_list.ico"
isTopMarked = true;
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243
sel.SelectLine();
 
menuKey = sel.Text.slice(0,24).replace(/\t/g, "\\t");
/**
sel.Collapse();
* ---------------------------------------------------------
bookmarks.push("Line " + sel.GetActivePointY(mePosLogical) + ":\t" + menuKey);
* 「ブックマーク一覧ジャンプ」
} else {
*  Orginal created by: Kuro, goat (2012/06/22 - 2012/09/24)
sel.StartOfDocument(false);
*  Modified by:        sukemaru  (2018/08/08 - 2020/06/07)
}
* ---------------------------------------------------------
while(sel.NextBookmark()){
*/
sel.SelectLine();
 
menuKey = sel.Text.slice(0, 24).replace(/\t/g, "\\t");
var start = new Date();   // 所要時間計測(開始)
sel.Collapse();
 
bookmarks.push("Line " + sel.GetActivePointY(mePosLogical) + ":\t" + menuKey);
// ---------- ▼ 設定項目 ▼ ---------- //
}
 
var bmcount = bookmarks.length;
// ■ ジャンプする前に選択範囲があったときはジャンプ先の行まで範囲選択する?
if(bmcount == 0){
var blockSelectEnable = false; // (true 範囲選択する / false 範囲選択しない)
//カーソル位置の復帰
 
sel.SetActivePos(activePos);
    // ■ もとの選択範囲をジャンプ後の範囲選択に含める?
sel.SetAnchorPos(anchorPos);
    var keepInitialSelect = false; // (true 含める / false 含めない)
ScrollX = sX;
 
ScrollY = sY;
    // ■ 下の行にジャンプしたとき、ジャンプ先の行全体(論理行)を選択範囲に含める?
} else {
    var endOfBookmarkLine = false; // (true 含める / false 含めない)
var BookmarkMenu = CreatePopupMenu();
 
for(var i = 0; i < bmcount; i++){
 
//メニューのアクセスキーも1から始まるほうが便利
// ■ 行番号の表示方法を Mery.ini から「自動」で読み込む?
BookmarkMenu.Add("&" + (i+1) + " " + bookmarks[i], i+1);
var autoLineModeEnable = false; // (true 自動読み込み / false 手動設定)
}
 
var ret = BookmarkMenu.Track(0);
    // ■ 行番号の表示方法(手動設定用)
if(ret == 0){
    var lineColumnView = 0;   // ( 0 論理行 / 1 表示行 )
//カーソル位置の復帰
 
sel.SetActivePos(activePos);
 
sel.SetAnchorPos(anchorPos);
// ■ 「ブックマークがありません」メニューを表示する?
ScrollX = sX;
var extraMenuEnable = true; // (true 表示する / false 表示しない)
ScrollY = sY;
 
} else {
// ■ ポップアップメニューに「先頭行/最終行 へジャンプ」をピン止め表示する?
sel.StartOfDocument(false);
var menuPinEnable = true; // (true 表示する / false 表示しない)
if(isTopMarked){
 
ret--;
// ■ ポップアップメニュー内の 30件* ごとに「キャンセル」を表示する?
}
var menuScrollEnable = false; // (true する / false しない)
for(i = 0; i < ret; i++){
 
sel.NextBookmark();
    // ■ 「キャンセル」の挿入ピッチ
}
    // ※ 10 - 100 のあいだの範囲の 10 の倍数で指定すること
}
    var subMenuHeight = 30;  // ◆初期値 = 30;
}
 
if(flgBlock){
 
//ジャンプ前の選択開始位置からジャンプ後のカーソル位置まで範囲選択
// ■ ポップアップメニューに表示する文字数の目安
sel.SetAnchorPos(anchorPos);
var menuWidth = 65; // (全角/半角の区別なし)
}
 
// 描画再開
// ■ 半角英文字を全角で表示する [!"%'(),.:;@\[\]`a-z{|}]
Redraw = true;
var toWideWidth = 0; // (0 しない / 1 文字間を広げる / 2 全角にする)
})();
 
</syntaxhighlight>
// ■ ポップアップメニューを表示する位置
var menuPosMouse = true; // ( true マウス位置 / false カーソル位置 )
 
 
// ■ 連番の桁埋め(右寄せ/空白埋め)用空白文字の定義
var blankChr = " "; // " " or "\u2002" or "\u3000" or "\u2007" or ...
 
  /* 半角数字の幅に等しい空白文字として
    MS UI Gothic では「2分アキ」 = EN SPACE: "\u2002"
    (MeiryoKe_UI Gothic は MS UI Gothic に同じ)
    Meiryo UI では「和字間隔」 = 全角空白: "\u3000"
    Segoe UI では「図形間隔」 = FIGURE SPACE: "\u2007"
    にすると具合がよさそうですが、ウエイトによって幅が変わるので... */
 
// ---------- ▲ 設定項目 ▲ ---------- //
 
// 連番の桁埋め(右寄せ/空白埋め)用
var b1 = ( ! blankChr || ( "" + blankChr ).length > 1 )
      ? " " : blankChr;
Object.prototype.PadSpace = function(len, chr) {
  var dgt = String(this);
  len = Number(len) || dgt.length;
  chr = chr ? chr.toString() : b1; // (b1 = blankChr || " ")
  if (len < 0 || len < dgt.length || chr.length !== 1)
    return dgt;
  for (var i = 0; i < len; i++)
    chr += chr;
  return (chr + dgt).slice( - len);
};
// 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 s = d.selection;
var act = s.GetActivePos(),  anc = s.GetAnchorPos();
var ay = s.GetActivePointY( posMode );
var sx = ScrollX,  sy = ScrollY;
// ブックマーク行の変数
var bmArray = [],  bmY = 0,  bmStr = "",  label = "";
// アクティブ行がブックマークされているか
var flag = 0,  isBookmarked = 0;
 
// ブックマーク行を検索
Redraw = false;
// 先頭行がブックマークされているか
s.SetActivePoint( mePosLogical, 1, 2 );
if ( s.PreviousBookmark() ) {
  bmY = s.GetActivePointY( posMode );
  bmStr = d.GetLine( bmY, lineMode );
  label = MenuKey( bmStr, menuWidth, toWideWidth, b1 );
  if ( bmY === ay ) { flag = 1;  isBookmarked = 1; }
  bmArray.push( { str: label,  id: bmY,  flag: flag } );
}
// 2行目以降のブックマークを探す
s.SetActivePos( 0 );
while ( s.NextBookmark() ) {
  bmY = s.GetActivePointY( posMode );
  bmStr = d.GetLine( bmY, lineMode );
  label = MenuKey( bmStr, menuWidth, toWideWidth, b1 );
  if ( bmY === ay ) { flag = 1;  isBookmarked = 1; }
  else { flag = 0; }
  bmArray.push( { str: label,  id: bmY,  flag: flag } );
}
// カーソル位置と選択範囲を復帰
s.SetActivePos( act );  s.SetAnchorPos( anc );
ScrollX = sx;  ScrollY = sy;
Redraw = true;
 
// ブックマークの件数
var bmCount = bmArray.length;
var numWidth = ( ( menuPinEnable || ! bmCount ) ? lines : bmY )
              .toString().length;
 
// ステータスラベル
var status1 = ( bmCount )
? " ブックマーク: "
  + bmCount.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )
  + " 件 "
: " ブックマークがありません。 ";
var status2 = ( bmCount ? "/ " : "" )
+ "全体の行数: "
+ lines.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )
+ ( lineColumnView ? " 行 (表示行)" : " 行 (論理行)" );
 
// アクティブ行のチェックマークフラグ (meMenuChecked = 1)
var Check = function( num ) { return ( num === ay ) *1; }
 
// ピン止めアイテム
var bmPin    = ay.PadSpace( numWidth ) + ": "
              + "ブックマークを設定/解除 (&B)";
var countPin  = "".PadSpace( numWidth )
              + " ▼ " + status1 + " ▼";
var cancelPin = "".PadSpace( numWidth )
              + " キャンセル\t& ";
var topPin    = ( 1 ).PadSpace( numWidth ) + ": "
              + "** 先頭行へジャンプ (&T) **";
var endPin    = lines.PadSpace( numWidth ) + ": "
              + "** 最終行へジャンプ (&E) **";
var actPin    = ay.PadSpace( numWidth ) + ": "
              + "** 行頭へジャンプ (&A) **";
 
// ポップアップメニューの準備
var bmMenu = CreatePopupMenu();
 
// メニューの最上部に「設定/解除」をピン止め
if ( bmCount || extraMenuEnable ) {
  bmMenu.Add( bmPin, 20000000, isBookmarked );
  bmMenu.Add( "----", 0, meMenuSeparator );
}
 
// 「ブックマーク一覧」ポップアップメニュー
if ( bmCount ) {
  bmMenu.Add( "----", 0, meMenuSeparator );
  // メニュー上部のピン止めアイテム
  if ( menuPinEnable
  && ( bmArray[0].id > 1 || ! isBookmarked && ay > 1 && ay < lines ) ) {
    if ( bmArray[0].id > 1 ) {
      bmMenu.Add( topPin, 1, Check( 1 ) );  // 「先頭行へジャンプ」
    }
    if ( ! isBookmarked && ay > 1 && ay < lines ) {
      bmMenu.Add( actPin, ay, meMenuChecked );  // 「行頭へジャンプ」
    }
    if ( bmCount > subMenuHeight && bmArray[ bmCount -1 ].id !== lines ) {
      bmMenu.Add( endPin, lines, Check( lines ) );  // 「最終行へジャンプ」
    }
    bmMenu.Add( "----", 0, meMenuSeparator );
  }
  // ブックマーク一覧
  bmMenu.Add( countPin, 0, 0 );  // ▼ ブックマーク: n 件 ▼
  for ( var i = 0, bm, num; i < bmCount; i ++ ) {
    if ( i % 10 === 0 && i > 0 ) {
      bmMenu.Add( "----", 0, meMenuSeparator );  // 10行ごとにセパレータ
      if ( menuScrollEnable && i % subMenuHeight === 0 ) {
        bmMenu.Add( cancelPin, 0 );  // 30行* ごとに「キャンセル」
        bmMenu.Add( "----", 0, meMenuSeparator );
      }
    }
    // ブックマーク行をポップアップメニューに追加
    bm  = bmArray[i];
    num = bm.id.PadSpace( numWidth ).replace( /\d$/, "&$&: " );
    bmMenu.Add( num + bm.str, bm.id, Check( bm.id ) );
  }
  // メニュー下部のピン止めアイテム
  if ( menuPinEnable && bmArray[ bmCount -1 ].id !== 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( "※ " + status1 + " ※", 0, meMenuGrayed );
  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 );  // 「行頭へジャンプ」
    }
  }
}
 
// ステータスとポップアップメニューを表示
Status = status1 + status2 + TimerElapsed( new Date(), start );
var y = bmMenu.Track( menuPosMouse );
 
// ブックマークを設定/解除
if ( y === 20000000 ) {
  editor.ExecuteCommandByID( MEID_EDIT_TOGGLE_BOOKMARK = 2126 );
}
// ジャンプ
else if ( y ) {
  s.SetActivePoint( posMode, 1, y );
  var yy = s.GetActivePointY( mePosView );
 
  // アクティブ行へのジャンプではスクロール位置をリセット
  if ( y === ay ) { ScrollY = yy }
 
  // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択する
  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 : yy;
}
 
 
// ---------- ▼ 関数 ▼ ---------- //
/**
* 関数 MenuKey( str, maxStrLength, toWideWidth, blankChr )
* ポップアップメニューに表示するラベルを生成する
*/
function MenuKey( str, menuWidth, toWideWidth, b1 ) {
  var keyWidth = ( /^[\s ]*[\s!-~\u200A\u2216\u22A0\u25AF]+$/.test( str ) )
              ? Math.floor( menuWidth *1.2 ) : menuWidth;
  var regBlankLine = /^[\s \u00A0\u1680\u180e\u2000-\u200A\u2028\u2029\u202F\u205F\uFEFF]+$/;
  var key = str ? ( regBlankLine.test( str ) ? "( 空白行 )" : str )
                : "( 空行 )";
  var regBlankChar = /[\u00A0\u1680\u180E\u2000-\u200E\u2028\u2029\u202F\u205F\u2062\u2063\uFEFF]/g;
  key = key.replace( /^[\s ]+/, "" )
          .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " \u203A " ) //「 › 」
          .slice( 0, keyWidth + 1 )
          .replace( /&/g, "&&" )
        // .replace( /\\/g, "\u2216" )          //「∖」
          .replace( regBlankChar, "\u25AF" );  //「▯」
  // 半角英小文字と ascii記号を全角化
  if ( toWideWidth === 2 ) {
    key = key.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g, function( tmp ) {
      return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
    } );
  }
  // スラッシュ 以外の ascii 記号と fijl に HAIR SPACE (\u200A) を付加
  else if ( toWideWidth === 1 ) {
    key = key.replace( /[!-%'-.:-@\[-`{-~fijl\u00A5\u22A0\u25AF]|&&/g,
              "\u200A$&\u200A" )
            .replace( /\u200A+/g, "\u200A" )
            .replace( RegExp( b1 + "\u200A", "g" ), b1 );
    keyWidth += ( key.match( /[\u200A\u203A]|&&/g ) || "" ).length;
  }
  key = ( key.length > keyWidth )
      ? key.slice( 0, keyWidth ) + " ..." : key;
  return key;
}
 
/**
* 関数 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 iniPath  = meryPath.replace( /\.exe$/i, ".ini" );
  if ( ! Fso.FileExists( iniPath ) ) {
    var mery = Fso.GetBaseName( meryPath );
    iniPath = new ActiveXObject( "WScript.Shell" )
              .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];
}
</source>
 
 
== include版 ==
 
* [[ダウンロード|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_ブックマークジャンプ_SS.png|設定変更サブメニュー]]
<br clear=all><br>
[[ファイル:Mery_ブックマークジャンプ_SS(2).png|frameless|254px|thumb|right|分割サブメニュー]]
* ブックマークの件数が多いときはサブメニューに分割表示します。
: 初期値では 30 件超で分割サブメニューを表示する設定にしてありますが、モニターの高さに合わせて分割単位を変更することができます(設定変更サブメニューで変更可)。
<br>
※ スクリーンショット画像は開発中のサンプル画像です。<br>
「メニューの再表示位置を調整する」コマンドは実装していません。
<br clear=all><br>
 
* 「[[includeライブラリ]]」を使用し、設定内容は外部ファイルに保存します(変数 <code>settingEnable</code> で設定。デフォルト: 有効)。<br>
<div id="注1" class="warningbox">
* あらかじめ [[includeライブラリ]] をインストールしてください。
* 設定内容の保存場所は '''Mery\Macros\MacroSettings\<ブックマーク一覧ジャンプ>.json''' <br> または '''%AppData%\Mery\MacroSettings\<ブックマーク一覧ジャンプ>.json''' です。
: ※ <code><ブックマーク一覧ジャンプ></code> の部分はこのマクロのファイル名
</div>
: 「▼ 通常版 設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。
: ※ ただし、settingEnable 変数を完全に無効にする場合は、ソースコード内で変更する必要があります。<br> ⇒ settingEnable を無効にした場合は「通常版」とほぼ同じ状態になり、「設定変更」サブメニューが表示されなくなります。
 
 
; ソースコード
 
<source lang="javascript" style="height:80em; overflow:auto;" highlight="23-41">
#title = "ブックマークジャンプ"
#tooltip = "ポップアップメニューでブックマーク行にジャンプ"
#icon = "bookmark_list.ico"
#include "include/IO.js"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",243
 
/**
* ---------------------------------------------------------
* 「ブックマーク一覧ジャンプ」
*  https://www.haijin-boys.com/wiki/ブックマーク一覧ジャンプ
*  Original created by: Kuro & goat (2012/06/22 - 2012/09/24)
* ---------------------------------------------------------
*  Modified        by: sukemaru    (2018/08/08 - 2020/06/24)
*  【include版】    by: sukemaru    (2019/04/15 - 2020/06/24)
* ---------------------------------------------------------
*/
 
var start = new Date(); // 所要時間計測(開始)
var setting = {};
 
// ---------- ▼ 【include版】 初期設定項目 ▼ ---------- //
 
// ◆ 「設定変更」サブメニューを表示するか?
setting.settingEnable = true; // (true する / false しない)
 
// ◆ JSON ファイルのベース名
var jsonName = ScriptName.replace( /\.js$/i, "" ); // 既定値
 
// 任意の名前に変更する場合はアンコメントして書き換える
// jsonName = "ブックマークジャンプ";
 
 
// ■ 連番の桁埋め(右寄せ/空白埋め)用空白文字の定義
var blankChr = " "; // " " or "\u2002" or "\u3000" or "\u2007" or ...
 
  /* 半角数字の幅に等しい空白文字として
    MS UI Gothic では「2分アキ」 = EN SPACE: "\u2002"
    (MeiryoKe_UI Gothic は MS UI Gothic に同じ)
    Meiryo UI では「和字間隔」 = 全角空白: "\u3000"
    Segoe UI では「図形間隔」 = FIGURE SPACE: "\u2007"
    にすると具合がよさそうですが、ウエイトによって幅が変わるので... */
 
 
// ---------- ▼ 通常設定項目 ▼ ---------- //
 
// 【include版】ではポップアップメニューから変更すること
setting.blockSelectEnable = false;
setting.keepInitialSelect = false;
setting.endOfBookmarkLine = false;
setting.autoLineModeEnable = false;
setting.lineColumnView = false;
setting.extraMenuEnable = true;
setting.menuPinEnable = true;
setting.menuScrollEnable = true;
setting.subMenuEnable = true;
setting.subMenuStyle = 0;
setting.subMenuHeight = 30;
setting.menuWidth = 55;
setting.toWideWidth = 0;
setting.menuPosMouse = true;
 
// ---------- ▲ 設定項目 ▲ ---------- //
 
 
/* ▼ 【include版】用の追加コード ▼ */
 
// ソースコードの「設定項目」内の初期値を保存する
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 subMenuStyle      = setting.subMenuStyle;
  var subMenuHeight      = setting.subMenuHeight;
  var menuWidth          = setting.menuWidth;
  var toWideWidth        = setting.toWideWidth;
  var menuPosMouse      = setting.menuPosMouse;
  var mouseDllEnable    = setting.mouseDllEnable;
}
/* ▲ 【include版】用のコード ▲ */
 
// 連番の桁埋め(右寄せ/空白埋め)用
var b1 = ( ! blankChr || ( "" + blankChr ).length > 1 )
      ? " " : blankChr;
/**
* Object.prototype.PadSpace( len, chr )
* Object オブジェクトに拡張メソッドを追加(グローバル)
* 数値/文字列の左側を chr で桁埋めした文字列をかえす
*/
Object.prototype.PadSpace = function(len, chr) {
  var dgt = String(this);
  len = Number(len) || dgt.length;
  chr = chr ? chr.toString() : b1;
  if (len < 0 || len < dgt.length || chr.length !== 1) return dgt;
  for (var i = 0; i < len; i++) chr += chr;
  return (chr + dgt).slice(- len);
};
 
// カーソルと選択範囲の位置を保存
var d = editor.ActiveDocument,  s = d.selection;
var act = s.GetActivePos(),  anc = s.GetAnchorPos();
var l_ay  = s.GetActivePointY( mePosLogical );
var v_ay  = s.GetActivePointY( mePosView );
var sx = ScrollX,  sy = ScrollY;
// ブックマーク行の変数
var bmArray = [];
var bmY, bmStr, vy, ly, y;
// アクティブ行がブックマークされているか
var flag = 0,  isBookmarked = 0;
 
// ブックマーク行を検索
Redraw = false;
// 先頭行がブックマークされているか
s.SetActivePoint( mePosLogical, 1, 2 );
if ( s.PreviousBookmark() ) {
  ly = s.GetActivePointY( mePosLogical );
  vy = s.GetActivePointY( mePosView );
  if ( ly === l_ay ) { flag = 1;  isBookmarked = 1; }
  bmArray.push( { LY: ly,  VY: vy,  flag: flag } );
}
// 2行目以降のブックマークを探す
s.SetActivePos( 0 );
while ( s.NextBookmark() ) {
  ly = s.GetActivePointY( mePosLogical );
  vy = s.GetActivePointY( mePosView );
  if ( ly === l_ay ) { flag = 1;  isBookmarked = 1; }
  else { flag = 0; }
  bmArray.push( { LY: ly,  VY: vy,  flag: flag } );
}
// ブックマークの件数
var bmCount = bmArray.length;
 
// カーソル位置と選択範囲を復帰
s.SetActivePos( act );  s.SetAnchorPos( anc );
ScrollX = sx;  ScrollY = sy;
Redraw = true;
 
// ---------- ▼ ループ開始位置 ▼ ---------- //
 
do {
  // ループしたときはタイマーをリセット
  if ( y ) { start = new Date(); }
 
  // Mery.ini から「行の表示方法」を取得する
  if ( autoLineModeEnable ) {
    lineColumnView = GetIniOptionNum( "LineColumnView" );
  }
  var ay = lineColumnView ? v_ay : l_ay;
  // 全体の行数
  var lines = d.GetLines( lineColumnView ? meGetLineView : 0 );
  // ブックマーク行の文字列を取得する
  if ( bmCount ) {
    for ( var i = 0; i < bmCount; i ++ ) {
      bmY = lineColumnView ? bmArray[i].VY : bmArray[i].LY;
      bmStr = d.GetLine( bmY, lineColumnView ? meGetLineView : 0 );
      bmArray[i].id = bmY;
      bmArray[i].str = MenuKey( bmStr, menuWidth, toWideWidth, b1 );
    }
  }
  var numWidth = ( ( menuPinEnable || ! bmCount ) ? lines : bmY )
                .toString().length;
 
  // ポップアップメニューの準備
  var bmMenu = CreatePopupMenu();
 
  // ステータスラベル
  var status1 = ( bmCount )
  ? " ブックマーク: "
    + bmCount.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )
    + " 件 "
  : " ブックマークがありません。 ";
  var status2 = ( bmCount ? "/ " : "" )
  + "全体の行数: "
  + lines.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," )
  + ( lineColumnView ? " 行 (表示行)" : " 行 (論理行)" );
 
  // アクティブ行のチェックマークフラグ (meMenuChecked = 1)
  var Check = function( num ) { return ( num === ay ) *1; }
 
  // ピン止めアイテム
  var bmPin    = ay.PadSpace( numWidth ) + ": "
                + "ブックマークを設定/解除 (&B)";
  var countPin  = "".PadSpace( numWidth )
                + " ▼ " + status1 + " ▼";
  var cancelPin = "".PadSpace( numWidth )
                + " キャンセル\t& ";
  var topPin    = ( 1 ).PadSpace( numWidth ) + ": "
                + "** 先頭行へジャンプ (&T) **";
  var endPin    = lines.PadSpace( numWidth ) + ": "
                + "** 最終行へジャンプ (&E) **";
  var actPin    = ay.PadSpace( numWidth ) + ": "
                + "** 行頭へジャンプ (&A) **";
 
  // メニューの最上部に「設定/解除」をピン止め
  if ( bmCount || extraMenuEnable ) {
    bmMenu.Add( bmPin, 20000000, isBookmarked );
    bmMenu.Add( "----", 0, meMenuSeparator );
    bmMenu.Add( cancelPin, 0 ); // 「キャンセル」
    bmMenu.Add( "----", 0, meMenuSeparator );
 
    /* 【include版】の設定変更サブメニュー */
    if ( initialSettings.settingEnable ) {
      bmMenu.AddPopup( "".PadSpace( numWidth )
                      + " 設定を変更する (&S)"
                    , SettingsMenu( setting ) );
      bmMenu.Add( "----", 0, meMenuSeparator );
    }
  }
 
  // 「ブックマーク一覧」ポップアップメニュー
  if ( bmCount ) {
    bmMenu.Add( "----", 0, meMenuSeparator );
    // メニュー上部のピン止めアイテム
    if ( menuPinEnable
    && ( bmArray[0].id > 1 || ! isBookmarked && ay > 1 && ay < lines ) ) {
      if ( bmArray[0].id > 1 ) {
        bmMenu.Add( topPin, 1, Check( 1 ) ); // 「先頭行へジャンプ」
      }
      if ( ! isBookmarked && ay > 1 && ay < lines ) {
        bmMenu.Add( actPin, ay, meMenuChecked ); // 「行頭へジャンプ」
      }
      if ( bmCount > subMenuHeight && bmArray[ bmCount -1 ].id !== lines ) {
        bmMenu.Add( endPin, lines, Check( lines ) ); // 「最終行へジャンプ」
      }
      bmMenu.Add( "----", 0, meMenuSeparator );
    }
 
    // 「分割サブメニュー」をピン止め
    if ( subMenuEnable && bmCount > subMenuHeight ) {
      DevidedMenu( bmMenu, bmArray, bmCount, numWidth, subMenuStyle, subMenuHeight, b1 );
    }
 
    // ブックマーク一覧
    bmMenu.Add( countPin, 0, 0 ); // ▼ ブックマーク: n 件 ▼
    for ( var i = 0, bm, num; i < bmCount; i ++ ) {
      if ( i % 10 === 0 && i > 0 ) {
        bmMenu.Add( "----", 0, meMenuSeparator ); // 10行ごとにセパレータ
        if ( menuScrollEnable && i % subMenuHeight === 0 ) {
          bmMenu.Add( cancelPin, 0 ); // 30行* ごとに「キャンセル」
          bmMenu.Add( "----", 0, meMenuSeparator );
        }
      }
      // ブックマーク行をポップアップメニューに追加
      bm  = bmArray[i];
      num = bm.id.PadSpace( numWidth ).replace( /\d$/, "&$&: " );
      bmMenu.Add( num + bm.str, bm.id, Check( bm.id ) );
    }
 
    // メニュー下部のピン止めアイテム
    if ( menuPinEnable && bmArray[ bmCount -1 ].id !== 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( "※ " + status1 + " ※", 0, meMenuGrayed );
    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 ); // 「行頭へジャンプ」
      }
    }
  }
 
  // ステータスとポップアップメニューを表示
  Status = status1 + status2
        + TimerElapsed( new Date(), start );
  var y = bmMenu.Track( menuPosMouse );
 
  /* 【include版】の設定変更サブメニュー項目を選択した場合 */
  if ( y >= 10000000 && y < 20000000 ) {
    SettingsChange( y );
  }
 
// 設定変更した場合はメニューを再表示する
} while ( y >= 10000000 && y < 10000100 && ! menuPosMouse  );
 
// ---------- ▲ ループ ここまで ▲ ---------- //
 
// ブックマークを設定/解除
if ( y === 20000000 ) {
  editor.ExecuteCommandByID( MEID_EDIT_TOGGLE_BOOKMARK = 2126 );
}
 
// ジャンプ
else if ( y && y < 10000000 ) {
  s.SetActivePoint( lineColumnView ? mePosView : mePosLogical, 1, y );
  var yy = s.GetActivePointY( mePosView );
 
  // アクティブ行へのジャンプではスクロール位置をリセット
  if ( y === ay ) { ScrollY = yy }
 
  // ジャンプ前の選択範囲からジャンプ先の行まで範囲選択する
  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 : yy;
}
 
 
// ---------- ▼ 関数 ▼ ---------- //
 
/**
* 関数 MenuKey( str, maxStrLength, toWideWidth, blankChr )
* ポップアップメニューに表示するラベルを生成する
*
* ・行頭空白を除去、空白文字を圧縮
* ・削られてしまう 「&」 を補完
* ・「¥」(\u005C) を 「∖」(\u2216) に置換:    または 「╲」(\u2572), 「﹨」(\uFE68)
* ・ゼロ幅や特殊な空白文字を豆腐に置換:    → 「⊠」(\u22A0) または 「▯」(\u25AF)
* ・判別しづらい半角記号を全角に置換:      !"%'(),.:;@[]`{|}
*    + 「a-z」 を全角に置換
*  または半角記号の前後にスキマをつける:  HAIR SPACE 「 」(\u200A)
* ・文字数を切り詰め
*/
function MenuKey( str, menuWidth, toWideWidth, b1 ) {
  // HAIR SPACE( ),「∖」「⊠」「▯」
  var keyWidth = ( /^[\s ]*[\s!-~\u200A\u2216\u22A0\u25AF]+$/.test( str ) )
              ? Math.floor( menuWidth *1.2 ) : menuWidth;
  var regBlankLine = /^[\s \u00A0\u1680\u180e\u2000-\u200A\u2028\u2029\u202F\u205F\uFEFF]+$/;
  var key = str ? ( regBlankLine.test( str ) ? "( 空白行 )" : str )
                : "( 空行 )";
  var regBlankChar = /[\u00A0\u1680\u180E\u2000-\u200E\u2028\u2029\u202F\u205F\u2062\u2063\uFEFF]/g;
  key = key.replace( /^[\s ]+/, "" )
          .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g, " \u203A " ) //「 › 」
          .slice( 0, keyWidth + 1 )
          .replace( /&/g, "&&" )
          .replace( /\\/g, "\u2216" )          //「∖」
          .replace( regBlankChar, "\u25AF" );  //「▯」
  // 半角英小文字と ascii記号を全角化
  if ( toWideWidth === 2 ) {
    key = key.replace( /[!"%'(),.:;@\[\]`a-z{|}]/g, function( tmp ) {
      return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
    } );
  }
  // スラッシュ 以外の ascii 記号と fijl に HAIR SPACE (\u200A) を付加
  else if ( toWideWidth === 1 ) {
    key = key.replace( /[!-%'-.:-@\[-`{-~fijl\u00A5\u22A0\u25AF]|&&/g,
              "\u200A$&\u200A" ) //「¥」「⊠」「▯」
            .replace( /\u200A+/g, "\u200A" ) // HAIR SPACE
            .replace( RegExp( b1 + "\u200A", "g" ), b1 );
    // HAIR SPACE,「›」
    keyWidth += ( key.match( /[\u200A\u203A]|&&/g ) || "" ).length;
  }
  key = ( key.length > keyWidth )
      ? key.slice( 0, keyWidth ) + " ..." : key;
  return key;
}
 
/**
* 関数 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 iniPath  = meryPath.replace( /\.exe$/i, ".ini" );
  if ( ! Fso.FileExists( iniPath ) ) {
    var mery = Fso.GetBaseName( meryPath );
    iniPath = new ActiveXObject( "WScript.Shell" )
              .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];
}
 
// ---------- ▼ 【include版】 関数 ▼ ---------- //
 
/**
* 関数 DevidedMenu( objPopupMenu, itemsArray, itemsCount, numWidth, subMenuStyle, subMenuHeight, blankChr )
* サブメニュー「※ 20* 件ずつ表示 ※  ▶」/「 1 - 20* 件目  ▶」に
* 配列の「アイテム」を分割表示する
* ※「 1 - 20 件目  ▶」の先頭にチェックマーク「*」をつけるために
*  for() ループを2回まわす
*/
function DevidedMenu( objPopupMenu, bmArray, bmCount, numWidth, subMenuStyle, subMenuHeight, b1 ) {
  b1 = ( b1.length === 1 ) ? b1 : " ";
  var devidedSubMenu = CreatePopupMenu();
  var indent = "".PadSpace( numWidth, b1 );
  var smArray = [];
  var tickedId;
 
  if ( subMenuStyle ) {
    // 「※ 20* 件ずつ表示 ※  ▶」をピン止め
    objPopupMenu.AddPopup( indent
    + " ※   " + subMenuHeight + " 件ずつに分割して表示 (&D)  ※"
    , devidedSubMenu );
    objPopupMenu.Add( "", 0, meMenuSeparator );
  }
 
  // 配列 bmArray のアイテムをサブメニューに追加していく
  bmArrayLoop:
  for ( var i = 0, j = 1, k = subMenuHeight, subMenu, line, num;
  i < bmCount; i ++, j ++, k ++ ) {
    if ( i % subMenuHeight === 0 ) {
      // smArray に「_from - _to 件目  ▶」用のサブメニューを生成する
      subMenu = CreatePopupMenu();
      smArray.push( {
        menu: subMenu,
        from: j,
        to: ( k < bmCount ) ? k : bmCount
      } );
    }
 
    // bmArray のアイテムをサブメニュー配下に追加する
    // ※ なぜか smArray[smArray.length -1].menu ではなく subMenu への Add() でよい
    line  = bmArray[i];
    num = line.id.PadSpace( numWidth, b1 ).replace( /\d$/, "&$&: " );
    subMenu.Add( num + line.str, line.id, line.flag );
    // アクティブ行のチェックマークフラグのついた行
    if ( line.flag === 1 ) { tickedId = smArray.length - 1; }
 
    // 10 件ごとにセパレータ、height ごとに「キャセル」行
    if ( j % 10 === 0 || j === bmCount) {
      subMenu.Add( "", 0, meMenuSeparator );
      if ( j % subMenuHeight === 0 || j === bmCount ) {
        subMenu.Add( indent + " キャンセル\t& ", 0 );
      }
    }
  }
 
  // 「_from - _to 件目  ▶」メニュー項目を親メニュー内に追加
  var popupMenu = subMenuStyle ? devidedSubMenu : objPopupMenu;
  var bmWidth  = bmCount.toString().length;
  var smIndent  = subMenuStyle ?  "" : indent;
 
  smArrayLoop:
  for ( var i = 0, smCount = smArray.length, tick, smLabel;
  i < smCount; i ++ ) {
    // ※ AddPopup に meMenuChecked は付けられないので文字「*」で代替
    tick = ( i === tickedId ) ? "* " : b1 + " ";
    smLabel = smArray[i].from.PadSpace( bmWidth, b1 ).replace( /\d/, "&$&" )
            + " - "
            + smArray[i].to.PadSpace( bmWidth, b1 ) + " 件目:";
    // ※ smArray[i].menu は PopupMenu オブジェクト
    popupMenu.AddPopup( smIndent + tick + smLabel, smArray[i].menu );
 
    // セパレータと「キャセル」行
    if ( ( i % subMenuHeight === 0 && i !== 0 ) || i === smCount - 1 ) {
      popupMenu.Add( "", 0, meMenuSeparator );
      if ( subMenuStyle ) {
        popupMenu.Add( " キャンセル\t& ", 0 );
      }
    }
    if ( i % 10 === 0 && i !== 0 ) {
      popupMenu.Add( "", 0, meMenuSeparator );
    }
  }
}
 
/**
* 関数 SettingsMenu( objSettinds )
* ポップアップメニューに「設定変更」サブメニューを表示する
*/
function SettingsMenu( setting ) {
  var ss = setting;
  var setMenu = CreatePopupMenu();
  // メニューのオプションフラグ
  // ※ meMenuChecked の数値は 1 なので hogeEnable *1 で代用できる
  // ※ meMenuGrayed の数値は 2 なので hogeEnable *2 で代用できる
  var grayFlag = ! ss.settingEnable ? meMenuGrayed : 0;
  var blockSelectGrayFlag = ! ss.blockSelectEnable *2 || grayFlag;
  var keepInitialSelectFlag = blockSelectGrayFlag
  + ( ss.keepInitialSelect && ss.blockSelectEnable ) *1;
  var endOfBookmarkLineFlag = blockSelectGrayFlag
  + ( ss.endOfBookmarkLine && ss.blockSelectEnable ) *1;
  var autoLineModeGrayFlag = ss.autoLineModeEnable *2 || grayFlag;
  var subMenuFlag = ! ss.subMenuEnable *2 || grayFlag;
  var maxHeightFlag = ( ss.subMenuHeight >= 100 ) *2 || subMenuFlag;
  var minHeightFlag = ( ss.subMenuHeight < 20 ) *2 || subMenuFlag;
  var maxWidthFlag = ( ss.menuWidth > 100  ) *2 || grayFlag;
  var minWidthFlag = ( ss.menuWidth < 30 ) *2 || grayFlag;
 
  // 「設定変更」サブメニューのアイテム
  setMenu.Add( "  ジャンプ先の行まで 範囲選択する (&B)"
            , 10000001, ss.blockSelectEnable + grayFlag );
  setMenu.Add( "  もとの選択範囲を ジャンプ後の範囲選択に含める (&K)"
            , 10000002, keepInitialSelectFlag );
  setMenu.Add( "  下の行にジャンプしたときに 行全体を選択 (&E)"
            , 10000003, endOfBookmarkLineFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "  ブックマークがなくても メニューを表示する (&N)"
            , 10000004, ss.extraMenuEnable + grayFlag );
  setMenu.Add( "「先頭行/最終行/行頭 へジャンプ」 を表示する (&P)"
            , 10000005, ss.menuPinEnable + grayFlag );
  setMenu.Add( "  " + subMenuHeight
            + " 件ごとに 「キャンセル」 を表示する (&C)"
            , 10000006, ss.menuScrollEnable + grayFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "「行の表示方法」 を自動設定する (&A)"
            , 10000007, ss.autoLineModeEnable + grayFlag );
  setMenu.Add( "・ 行数を 「論理行」 で表示する"
            + ( ss.lineColumnView ? " (&L)" : "" )
            , 10000008, ! lineColumnView + autoLineModeGrayFlag );
  setMenu.Add( "・ 行数を 「表示行」 で表示する"
            + ( ss.lineColumnView ? "" : " (&V)" )
            , 10000008, lineColumnView + autoLineModeGrayFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "「分割サブメニュー」 を表示する (&D)"
            , 10000009, ss.subMenuEnable + grayFlag );
  setMenu.Add( "・ メインメニューの中に「 1 - " + ss.subMenuHeight
            + " 件目: \u25B6」 を表示する"
            + ( ss.subMenuStyle ? " (&T)" : "" )
            , 10000010, ! ss.subMenuStyle + subMenuFlag );
  setMenu.Add( "・ 「" + ss.subMenuHeight
            + " 件ずつに分割して表示 \u25B6」"
            + " の中に表示する" + ( ss.subMenuStyle ? "" : " (&T)" )
            , 10000010, ss.subMenuStyle + subMenuFlag );
  setMenu.Add( "  サブメニューの分割単位を 10行 増やす (&X) \t"
            + ss.subMenuHeight + ( ss.subMenuHeight >= 100 ? ""
            : " → " + ( ss.subMenuHeight + 10 ) )
            , 10000011, maxHeightFlag );
  setMenu.Add( "  サブメニューの分割単位を 10行 減らす (&Z) \t"
            + ss.subMenuHeight + ( ss.subMenuHeight < 20 ? ""
            : " → " + ( ss.subMenuHeight - 10 ) )
            , 10000012, minHeightFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "  メニューの表示幅を 10文字 増やす (&+)\t"
            + ss.menuWidth + ( ss.menuWidth > 100 ? ""
            : " → " + ( ss.menuWidth + 10 ) )
            , 10000013, maxWidthFlag );
  setMenu.Add( "  メニューの表示幅を 10文字 減らす (&-)\t"
            + ss.menuWidth + ( ss.menuWidth < 30 ? ""
            : " → " + ( ss.menuWidth - 10 ) )
            , 10000014, minWidthFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "・ 半角 a-z と記号を全角で表示する (&F)\t"
            , 10000015, ( ss.toWideWidth === 2 ) + grayFlag );
  setMenu.Add( "・ 半角英・記号の文字間隔を調整する (&W)\t"
            , 10000016, ( ss.toWideWidth === 1 ) + grayFlag );
  setMenu.Add( "・ 半角英数記号を半角のまま表示する (&H)\t"
            , 10000017, ! ss.toWideWidth + grayFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "・ 入力カーソル の位置 にメニューを表示する"
            + ( ss.menuPosMouse ? " (&M)" : "" )
            , 10000018, ! ss.menuPosMouse + grayFlag );
  setMenu.Add( "・ マウスポインタの位置 にメニューを表示する"
            + ( ss.menuPosMouse ? "" : " (&M)" )
            , 10000018, ss.menuPosMouse + grayFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "  設定内容をロックする (&S)"
            , 10000000, ! ss.settingEnable *1 );
  setMenu.Add( "", 0, meMenuSeparator );
  setMenu.Add( "「ジャンプ」マクロの JS ファイルを開く (&O)"
            , 10000100 );
  setMenu.Add( "「ジャンプ」マクロの JSON ファイルを開く (&J)"
            , 10000101 );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "  設定を初期化する (&I)", 10000102, grayFlag );
  setMenu.Add( "----", 0, meMenuSeparator );
  setMenu.Add( "  キャンセル\t& ", 0 );
  return setMenu;
}
 
/**
* 関数 SettingsChange( num )
* ポップアップメニューで選択した項目の設定状態を変更する
*/
function SettingsChange( num ) {
  var isChanged = true;
  switch ( num ) {
  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: // 20 件* ごとに 「キャンセル」 を表示する
    menuScrollEnable = setting.menuScrollEnable
                    = ! menuScrollEnable;
    break;
  case 10000007: // 「行の表示方法」 を自動設定する
    autoLineModeEnable = setting.autoLineModeEnable
                      = ! autoLineModeEnable;
    break;
  case 10000008: // 行数を 「論理行/表示行」 で表示する
    lineColumnView = setting.lineColumnView
                  = ! lineColumnView;
    break;
  case 10000009: // 「分割サブメニュー」 を表示する
    subMenuEnable = setting.subMenuEnable
                  = ! subMenuEnable;
    break;
  case 10000010: // 「20* 件ずつに分割して表示 ▶」のなかに表示する
    subMenuStyle = setting.subMenuStyle
                = Number( ! subMenuStyle );
    break;
  case 10000011: // サブメニューの分割単位を 10行 増やす
    subMenuHeight = setting.subMenuHeight += 10;
    break;
  case 10000012: // サブメニューの分割単位を 10行 減らす
    subMenuHeight = setting.subMenuHeight -= 10;
    break;
  case 10000013: // メニューの表示幅を 10文字 増やす
    menuWidth = setting.menuWidth += 10;
    break;
  case 10000014: // メニューの表示幅を 10文字 減らす
    menuWidth = setting.menuWidth -= 10;
    break;
  case 10000015: // 半角 a-z と記号を全角で表示する
    toWideWidth = setting.toWideWidth = 2;
    break;
  case 10000016: // 半角英・記号の文字間隔を調整する
    toWideWidth = setting.toWideWidth = 1;
    break;
  case 10000017: // 半角英数記号を半角のまま表示する
    toWideWidth = setting.toWideWidth = 0;
    break;
  case 10000018: // 入力カーソル/マウスポインタ の位置 にメニューを表示する
    menuPosMouse = setting.menuPosMouse
                = ! menuPosMouse;
    break;
  case 10000000: // 設定内容をロックする
    settingEnable = setting.settingEnable
                  = ! settingEnable;
    break;
  case 10000100: // 「ジャンプ」マクロの JS ファイルを開く
    isChanged = false;
    Status = " " + ScriptFullName;
    new ActiveXObject( "WScript.Shell" )
    .Run( '"' + editor.FullName + '" "' + ScriptFullName + '"' );
    break;
  case 10000101: // 「ジャンプ」マクロの JSON ファイルを開く
    isChanged = false;
    OpenJumpJson( jsonName, decode = false, toWideWidth );
    break;
  case 10000102: // 設定を初期化する
    isChanged = false;
    CheckJsonFile( jsonName, decode = false, toWideWidth );
    break;
  default:
    sIsChanged = false;
    break;
  }
  if ( isChanged ) {
    IO.Serialize( setting, jsonName );
    Status = "  設定の変更を保存しました。";
  }
}
 
/**
* 関数 OpenJumpJson( setting, jsonName )
* JSON ファイルを開く
*/
function OpenJumpJson( jsonName, decode, toWideWidth ) {
  IO.Serialize( setting, jsonName );
  Sleep( 500 );
  var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
  if ( IO.Path.IsExist( jsonPath )
  && ( json = JsonContents( jsonPath, decode, toWideWidth ) ).length ) {
    var confirmStr = "設定ファイルは正常です \n\n"
                  + json + "設定ファイルを開きますか? ";
    if ( Confirm( confirmStr ) ) {
      new ActiveXObject( "WScript.Shell" )
      .Run( '"' + editor.FullName + '" "' + jsonPath + '"' );
    }
  }
  else {
    Alert( "設定ファイルがありません \n" + jsonDir + "  " );
  }
}
 
/**
* 関数 CheckJsonFile( jsonName )
* JSON ファイルの初期化/実在確認
*/
function CheckJsonFile( jsonName, decode, toWideWidth ) {
  var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
  var confirmStr = jsonPath + "  \n\n"
                + "設定ファイルを初期化しますか? "
  if ( Confirm( confirmStr ) ) {
    IO.Serialize( initialSettings, jsonName );
    Sleep( 500 );
    var msg = ( IO.Path.IsExist( jsonPath )
    && ( json = JsonContents( jsonPath, decode, toWideWidth ) ).length )
    ? "設定ファイルは正常です \n\n" + json
    : "設定ファイルがありません \n" + jsonDir + "  ";
    Alert( msg );
  }
}
 
/**
* 関数 JsonDir()
* JSON ファイルの親フォルダのパス
*/
function JsonDir() {
  var jsonDir;
  if ( IO.Path.IsExist(
  editor.FullName.replace( /\.exe$/i, ".ini" ) ) ) {
    jsonDir = editor.FullName.replace( /[^\\]+$/i, "" )
            + "Macros\\MacroSettings";
  }
  else {
    jsonDir = new ActiveXObject( "WScript.Shell" )
              .ExpandEnvironmentStrings( "%APPDATA%" )
            + "\\Mery\\MacroSettings";
  }
  return jsonDir;
}
 
/**
* 関数 JsonContents( jsonPath, decode, toWideWidth )
* JSON ファイルの文字列をメッセージボックス用に整形する
*/
function JsonContents( jsonPath, decode, toWideWidth ) {
  var json = IO.LoadFromFile( jsonPath )
            .replace( /^\{/, "\n{\n  " )
            .replace( /\}$/g, "\n}\n\n" )
            .replace( /,"/g, " ,\n  \"" )
            .replace( /":/g, "\": " );
  if ( decode ) {
    // 「テキスト整形」マクロより >> 符号化/復号化 >> \uHHHH デコード
    json = json.replace( /\\u([0-9A-Fa-f]{4})/g, function( s, n ) {
      return String.fromCharCode( Number( "0x" + n ) );
    } );
  }
  if ( toWideWidth ) {
    // 半角アルファベットの小文字を全角にするなら
    json = json.replace( /[a-z]/g, function( tmp ) {
      return String.fromCharCode( tmp.charCodeAt( 0 ) + 0xFEE0 )
    } );
  }
  return jsonPath + "  \n" + json;
}
</source>

2025年11月25日 (火) 16:53時点における最新版

概要[編集]

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

ソースコード[編集]

#title="ブックマーク一覧"
/*
* ブックマーク一覧をポップアップメニューにして選択ジャンプ
* Mery標準のブックマークが対象
* 範囲選択中の場合は選択範囲を拡大
*/
(function(){
	// 描画停止
	Redraw = false;
	var sel = document.selection;
	var flgBlock = !sel.IsEmpty;
	//カーソル位置の保存
	var activePos = sel.GetActivePos();
	var anchorPos = sel.GetAnchorPos();
	var sX = ScrollX;
	var sY = ScrollY;

	var bookmarks = [];
	var menuKey = "";
	// 先頭行がブックマークされてないか
	var isTopMarked = false;
	sel.SetActivePoint(mePosLogical, 1, 2);
	if(sel.PreviousBookmark()){
		isTopMarked = true;
		sel.SelectLine();
		menuKey = sel.Text.slice(0,24).replace(/\t/g, "\\t");
		sel.Collapse();
		bookmarks.push("Line " + sel.GetActivePointY(mePosLogical) + ":\t" + menuKey);
	} else {
		sel.StartOfDocument(false);
	}
	while(sel.NextBookmark()){
		sel.SelectLine();
		menuKey = sel.Text.slice(0, 24).replace(/\t/g, "\\t");
		sel.Collapse();
		bookmarks.push("Line " + sel.GetActivePointY(mePosLogical) + ":\t" + menuKey);
	}
	var bmcount = bookmarks.length;
	if(bmcount == 0){
		//カーソル位置の復帰
		sel.SetActivePos(activePos);
		sel.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){
			//カーソル位置の復帰
			sel.SetActivePos(activePos);
			sel.SetAnchorPos(anchorPos);
			ScrollX = sX;
			ScrollY = sY;
		} else {
			sel.StartOfDocument(false);
			if(isTopMarked){
				ret--;
			}
			for(i = 0; i < ret; i++){
				sel.NextBookmark();
			}
		}
	}
	if(flgBlock){
		//ジャンプ前の選択開始位置からジャンプ後のカーソル位置まで範囲選択
		sel.SetAnchorPos(anchorPos);
	}
	// 描画再開
	Redraw = true;
})();
スポンサーリンク