「FAQ」カテゴリーアーカイブ

Curlに関するFAQのページです

ロケール変更を行っての帳票印刷について

【ご質問】

「ロケールを切り替えて印刷を行うと、印刷物の文字フォントが変わる。」
 という現象が発生しております。

【回答】

font-familyの指定がなされていない場合、font-familyに sans-serif が指定されたものとして振舞います。
sans-serifには日本語フォントを保持していないため、
この際の挙動がOSやプリンタの仕様に従います。

これを回避するには、
font-familyを明示的に指定することで回避することが可能です。

高さ幅の取得方法

【ご質問】

 Tableクラスにてwidth,heightへ値をセットせず幅高さを指定してないオブジェクト
 の幅と高さの値を取得するにはどうしたらよろしいでしょうか?

【回答】

get-bounds (メソッド)にて取得したGRectのwidthおよびheightを取得することで実現可能です。
詳細はサンプルをご参照ください。

http://developers.curlap.com/curl/faq/get_boundssample.curl

font-familyの設定に関して

【ご質問】
Curl開発者ガイドの
[テキスト書式]-[文字書式およびオプション]
の項内の「font-family」の説明内に

 「一致するフォント書体がリストにない場合は
 任意のフォントが選択される可能性があります。」

と記載されていますが、font-familyの指定を行っていない場合
フォントとしてどのフォントが使われているのでしょうか。

例えばfont-familyを特に指定していない場合、
CommandButtonのラベルではどのフォントが使われるのでしょうか。

【回答】
font-familyを設定していないテキストのフォントは
OSのバージョン・設定によって変わります。

CommandButtonのラベルに関しては
OSの以下の設定によって使用されるフォントが変わります。
 ・WindowsXPの場合
  [[デスクトップ上で右クリックしてプロパティ選択]-[デザイン]-[詳細設定]
 ・WindowsVista・7・8の場合
  [デスクトップ上で右クリックして個人設定選択]-[ウインドウの色]-[デザインの詳細設定]

以下のサンプルコードと実行例を参考にしてください。

{curl 6.0,7.0,8.0 applet}
{curl-file-attributes character-encoding = “shift-jis”}

{VBox
    {CommandButton
        font-size = 20pt,
        label = “abc ABC ああいいううええおお”},
    {text
        font-family = “メイリオ”,
        font-size = 20pt,
        abc ABC ああいいううええおお(メイリオのサンプル行)}
}

上記コードをWindows7上で実行した際の様子

OSの設定が以下の設定の場合(初期状態です。フォントはメイリオになっています)
766-font-standard-1.png

上記サンプルコードを実行すると以下のように表示されます。
CommandButtonのラベルがメイリオのサンプル行と同じ書体であることからメイリオであることが分かります。

766-font-standard-2.png

OSの設定を以下の様に変更した場合(フォントをメイリオからMS 明朝に変更)
766-font-standard-3.png

上記サンプルコードの実行画面は以下のようになります。
メイリオのサンプル行と見比べると、CommandButtonのラベルのフォントが変わっていることが分かります。

766-font-standard-4.png

 

バイナリデータの受信について

【ご質問】

Curl側でバイナリーデータを受け取るための機能は提供されていますでしょうか?
バイナリデータをAcrobatReaderでPDFとして表示させたいです。

バイナリーデータを受け取り、AcrobatReaderを起動して表示するというやり方を教えていただけないでしょうか?

【回答】

バイナリデータの受信は可能です。
httpレスポンスの内容は
byte配列に取得することができます。

http://developers.curlap.com/curl/faq/pdf_bin_sample.curl

ダウンロードしたpdfファイルをローカルに保存して
関連付け実行するサンプルを添付いたしました。

詳細は、開発者ガイドの
[Curl プロセス間のコミュニケーション]
-[ファイルやその他のリソースへのアクセス]
-[ストリーム]-[バイト ストリーム]
をご参照ください。

拡張子に紐付くプログラムの自動実行について(WINDOWS)

【ご質問】
ファイルをダウンロードし、その後自動でダウンロードファイルを実行したいと考えております。

Windowsではアイコンをダブルクリックすると拡張子に関連付けされたアプリケーションが実行されますが、
Curlでも同様の処理が出来ますでしょうか?

【回答】
STARTコマンドをrun-host-shellプロシージャから実行することで
関連付け実行が可能です。
フルパスをSTARTコマンドの引数としてお渡しください。

詳細は以下のサンプルをご参照ください。

{curl 6.0,7.0,8.0 applet}
{curl-file-attributes character-encoding = “shift-jis”}

{import * from CURL.RUNTIME.HOST-PROCESS}

{let tf:TextField =
    {TextField
        value = “フルパスを入力してください”,
        width = 10cm
    }
}

{let cb:CommandButton =
    {CommandButton
        label=“関連付け実行”,
        {on Action do
            let url-link:Url = {url file:/// & tf.value}
            let file:any = {url-link.resolve error-if-missing? = false}
            {type-switch file
             case file:File do
                ||ファイルとして存在する
                let hostprocessrtn:int =
                    {run-host-shell
                        “START \”\” cmd /c \”” & url-link.local-filename asa String & “\””
                    }
             else
                {popup-message “ファイルが存在しません”}
            }
        }                           
    }
}

{View
    {VBox
        tf,
        cb
    },
    visibility = “normal”,
    {on WindowClose do
        {exit}
    }
}

また、APIリファレンスの
[CURL.RUNTIME.HOST-PROCESS]-[run-host-shell]
の項をご参照ください。

ScrollBoxの「expanded-vscroll-bounds?」について

【ご質問】
ScrollBoxの「expanded-vscroll-bounds?」プロパティ
「スクロール可能な領域を垂直に拡張し、内容がちょうど見えなくなる位置までスク
ロールできるようにします。」
を用いると、スクロールボックスの上と下両方が拡張されますが、
下のみ見えなくなるまで拡張する方法はありますでしょうか。

【回答】
この点についての制御を行うプロパティ等はございませんが、
オープンコントロールを使用すれば可能です。

※オープンコントロールは標準コントロール部品全体を複製しているため、
  ソースのコンパイル等に時間を要し、
  全体の処理速度が遅くなる可能性がありますので、
  使用する際はお気をつけください。

無効または認識されない応答をサーバーが返しました (12152)

【ご質問】

以下のエラーが発生しております。

Error: HttpException: https://servername.com/start.dcurl の送信に失敗しました
:無効または認識されない応答をサーバーが返しました (12152)。

 

【回答】

このエラーはCurlが発生させているものではなく、
Windowsの通信モジュールが検知・発生させそれを
Curlが表示させたものになります。

このエラーは
 ERROR_HTTP_INVALID_SERVER_RESPONSE
 The server response could not be parsed.
と定義されているものです。
http://support.microsoft.com/kb/193625/ja

MS社のホームページにて発生原因について記載がございますので
ご確認頂きますようお願い致します。
http://support.microsoft.com/kb/187650/ja

 

ロードバランサ利用時の留意点

【ご質問】
ロードバランサ利用時の留意点等ありましたら教えてください。

【回答】
Curlはサーバへの依存度は低いため、ロードバランサ利用時における特殊な設定等は必要ありません。

ライセンスファイルにはCurlクライアントから参照する際のURL(ホスト名)を指定する必要があります。
(ロードバランサ配下の各サーバの実IP、ホスト名などと関係有りません)

尚、ロードバランサ導入に伴い、想定外の挙動となっている場合は、パケットキャプチャにて
通信を監視することでトラブルシューティングを行うことが有効です。

こちらのFAQもご参照ください。
http://developers.curlap.com/faq/49-faq-operation/578-loadbalancer.html

クエリ文字列のあるURLで呼び出すとパーシスタントデータが読み出せない

【ご質問】
アプレット呼び出し時にパラメータを設定するため
http://www.sample.com/sample.curl?query=hoge
のようにクエリ文字列を付加したURLで
アプレットを呼び出すことにしました。

この呼び出される側のアプレットは
プライベートパーシスタントデータを使用しているのですが、
クエリ文字列(?query=hogeなど)のあるURLで呼び出されると、
クエリ文字列のないURLで呼び出した時に保存したデータが
読み出せなくなることがわかりました。
異なるURLとみなして、データファイルを別途に作成するようです。

クエリ文字列のあるURLで呼び出されたときにも
同じデータファイルを読み書きできるようにする方法はないでしょうか?

【回答】
このような場合は、プライベートパーシスタントデータの代わりに
共有パーシスタントデータをご使用ください。
クエリ文字列のあるURLで呼び出された場合でも
共通のパーシスタントデータファイルを使用できます。

詳細は、Curl開発者ガイドの
[外部リソースとの対話]-[クライアント側パーシスタントデータ]-[共有パーシスタントデータ]
の項をご参照ください。

エンターキーでタブ移動

【ご質問】
エンターキーでタブ移動することは可能でしょうか?

【回答】
可能です。

詳細は以下のサンプルをご参照ください。

{curl 6.0,7.0,8.0 applet}
{curl-file-attributes character-encoding = “shift-jis”}

{define-proc {prepare-traverse-with-enter
                      atc:ActiveTraversalContainer,
                      target-controls:{Set-of Type},
                      ignore-exception? = true
                  }:void
    {let gui-m:GuiManager = {get-gui-manager}}
    {let fm:#FocusManager = {atc.get-focus-manager}}
    {gui-m.add-event-handler
        {on ke:RawKeyRelease at gui-m do
            {if ke.keycode == Keycode.enter and fm.keyboard-focus-target isa ControlUI then
                def target = fm.keyboard-focus-target asa ControlUI
                def ctl = target.control
                {if {target-controls.member? {type-of ctl}}then
                    {if ke.shift? then
                        {d.traverse forward?=false}
                     else
                        {d.traverse}
                    }
                }
            }
        }
    }
    {return}
}

{def enter-to-move-targets:{Set-of Type} =
    {{Set-of Type}
        TextField
        ,PasswordField
        ||,TextArea || In TextArea, enter key will be kept as a carriage return key.
        ,{type-of {SpinControl}.focus-object} || for DateField and SpinControl
        ,CommandButton
        ,CalendarControl
    }
}

{let d:Dialog
    ={Dialog
         {VBox
             {TextField prompt=“enter to move next”},
             {PasswordField prompt=“enter to move next”},
             {TextDisplay value=“test”},
             {DateField},
             {SpinControl},
             {CalendarControl},
             {CommandButton},
             {TextArea}
         }
     }
}

{value d}

{prepare-traverse-with-enter
    d,
    enter-to-move-targets
}

 

pcurlをget-backtraceしたときにソースコードの行番号を知りたい

◆ご質問◆
pcurl化されたものをget-backtraceしたときに
ソースコードの行数を知りたい。(何もしないと行数が??になります。)
例)
#0   get-backtrace (0x100467f5-58) at curl://source-9/Core/ConsoleDebugger/stack.curl:??
#1   Xxxxx.xxx-xxxx-xx (0x06159883-79) at file:///c:/Documents and
Settings/xxxx/デスクトップ/xxxx/load.scurl:??
#2   Xxxxx.xxx-xxxx-xx (0x0615b82f-1999) at file:///c:/Documents and
Settings/xxxx/デスクトップ/xxxx/load.scurl:??
#3   Xxxxx.handle-message (0x0615bb53-183) at file:///c:/Documents and
Settings/xxxx/デスクトップ/xxxx/load.scurl:??
…….

◆回答◆
まず、IDEで「コンポーネントターゲット設定」で「デバッグインフォメーションを含める」の
チェックボックスを選択し、デプロイします。

Curlコントロールパネルの「デバッガ」タグでそのURLを登録します。(例:http://localhost/)

そして実行すると以下のようにソースコードの行数が表示されます。
例)
http://localhost/start.curl

#0   get-backtrace (0x100467f5-58) at curl://source-9/Core/ConsoleDebugger/stack.curl:??
#1   Xxxxx.xxx-xxxx-xx (0x06159883-79) at file:///c:/Documents and
Settings/xxxx/デスクトップ/xxxx/load.scurl:1301[4]
#2   Xxxxx.xxx-xxxx-xx (0x0615b82f-1999) at file:///c:/Documents and
Settings/xxxx/デスクトップ/xxxx/load.scurl:900[8]
#3   Xxxxx.xxx-xxxx-xx (0x0615bb53-183) at file:///c:/Documents and
Settings/xxxx/デスクトップ/xxxx/load.scurl:847[8]
…….

マルチランゲージについて

【ご質問】
複数言語の文字が混在する文字列の表示のために  font-familyに複数のフォントを指定し、
文字によって使用されるフォントを切り替えることは可能でしょうか?

例えば、font-familyに
 MS ゴシック,
 NSimSun(中国語フォント),
 GungsuhChe(韓国語フォント)
とした場合はどのような挙動をしますでしょうか?

【回答】
font-familyに複数のフォントを指定することはできますが、これはシステム(OS)に
該当フォントがインストールされていない場合における振る舞いを想定したものであり、
マルチランゲージを表示するためではございません。

font-familyの指定に対して、Curl実行環境は、システムで使用可能なフォント書体が
見つかるまでフォントリストを検索します。
最初に見つかったシステムで使用できるフォントが複数言語が混在する文字列に対しても使用されます。

複数言語が混在する文字列を正しく表示できないことがありえますが
文字種によってはCurl実行環境が代替のフォントで表示する場合があります。
この代替フォントはfont-familyの記述によらないもので、
韓国語以外のフォントを第一番目に指定した場合に
ハングルがいつも同じフォントで表示されるのはこのためです。

上記例ですと、
 ・MS ゴシックがシステムで使用可能(基本的にWindows環境であればこのフォントは
  通常インストールされています)ですので、このフォントが使用されます。
 ・中国語:MS ゴシックは中国語を完全に表示できるフォントではありませんので
  読める形で表示されるとは限りません。
 ・韓国語:MS ゴシックは韓国語が表示できませんが、  font-familyによらない
  代替フォントが使用されて表示されます。

Curlに限った話ではありませんが、これらを解決するために、マルチランゲージを表示するために、
世間では、ユニバーサルフォント「Arial Unicode MS」のようなフォントが用意されているようです。

 

BadEnumExceptionについて

【ご質問】

https://xxx.xxx.xxx.xxx/xxxxxx/をブラウザ上から開こうとした際に、
ログイン画面が表示されず、「予期していないアプレット例外」が発生します。

[エラー詳細]
BadEnumException: xxxx の要素に値 xxx がありません。

[エラーポップアップ]
次で説明されている予期されていない例外が、アプレットからスローされます。

その例外がスローされる(または例外が無視される)のを許可するには、
「継続」をクリックしてください。または、「アプレットの終了」をクリックし、アプレットを終了してください。
BadEnumException: xxxx の要素に値 xxx がありません。

【回答】
BadEnumExceptionの詳細に関しては、APIリファレンスの
[CURL.RUNTIME.COMPILER-SUPPORT]-[BadEnumException]
の項をご参照ください。
「列挙値の構築での実行時エラーを示します。」
と記述しております。

原因の1つとして、
列挙型として定義した要素以外の値を参照した場合に、
BadEnumExceptionが発生します。

例を示します。

{define-enum Bear
    polar = “endangered”,
    grizzly = “threatened”,
    panda,
    pooh = “abundant”
}

{value {Bear value=“aaa”}.name}

“aaa”は列挙型Bearの要素として定義されていないため
このコードを実行するとBadEnumExceptionが発生します。
 
  

列挙型として定義した要素を使用することで、
本現象を回避することが可能です。

2重起動制御

【ご質問】
独立型アプレットが起動している状態で、同じアプレットを起動しようとした場合に、
そのアプレットが起動済みであるかの判定を行うことは出来ますか。

【回答】
独立型アプレットに以下のようなコードを追加することで、実現可能です。

{let invoke-count:int = 0}

{do
  set {get-the-applet}.launch-handler =
          {proc {url:Url}:void
              {inc invoke-count}
              {if invoke-count > 1 then
                  {popup-message “すでに起動しています。”}
              }
          }
}

プロセスのインスタンスは1つであるため、グローバル変数の初期化は1回だけ行われます。
しかし、アプレットのlaunch-handlerは起動ごとに呼ばれるため、
この中でグローバル変数のチェックを行うことで、起動済みかどうかの判断をしています。

文字列連結のパフォーマンス

文字列連結(編集)において、以下のうち、どれがレスポンスが良いか?

  1. &による文字列連結
  2. StringBuf.concatによる文字列連結
  3. formatによる文字列連結

結果としては1.の”&”がダントツ早いです。

“&”はコンパイラが既に知っているオペレータなので早いです。
 パフォーマンスを気にするところなら”&”を用いたほうがよいですが、コードがみずらくなります。
 もし、パフォーマンスを気にしなくていい部分ならformatマクロを使うことをお勧めします。

コンテキストメニューのカスタマイズについて

【ご質問】
カスタマイズされたコンテキストメニューに
標準で右クリック時に表示されるコンテキストメニューの「印刷」と同等処理を行う
メニューを作成することは可能でしょうか?

また、特定コントロールのみを印刷するといった制御は可能でしょうか?

【回答】
print-graphicプロシージャを使用することで実現可能です。

★同等処理を行うには
print-graphicプロシージャに印刷対象として、
画面に表示されている全てのコンテンツを含むコンテナ(FrameやVBoxなど) を指定し、
「hstretch? = true」及び「ne-title = {get-base-url}」の2つのプロパティの設定を行います。
これにより、デフォルトの「印刷…」とほぼ同等の印刷を行うことができます。

{MenuAction
   label = “印刷…”,
    {on Action do
        {print-graphic
            contents,  ||画面に表示されている全コンテンツを含むFrameなど

          ||デフォルトの「印刷…」ボタンと同じスタイルで印刷する為に
          ||以下のプロパティを変更します。

             hstretch? = true,
             ne-title = {get-base-url}
         }
    }
}

★印刷対象の制御をするには
print-graphicプロシージャの印刷対象として、印刷したいコントロールを指定することによって、
特定のコントロールのみ印刷することが可能となります。

また、context-popupプロシージャの設定をする際に、
menu-pane-procのキーワード引数に対して、下記のように記述することにより、
コンテキストメニューを呼び出したコントロールの印刷を行うという設定もできます。 

menu-pane-proc =
{proc {vi:Visual}:MenuPane
    {return
        {MenuPane
            {MenuAction
                label = vi.name & “印刷”,
                {on Action do
                    {print-graphic
                    vi,
                    ne-title = {get-base-url}
                    }
                            }
                     }
              }
    }
}

この設定は、例えば、画面に表示されている全てのコントロールに対して、
そのコントロールのみの印刷を行うというコンテキストメニューを追加する場合などに使用できます。
(例えば、全てのコントロールを含むVBoxのchild-arrayを呼び、for文を使い、
その1つ1つのchildに対して、add-event-handlerで上記設定を行った
context-popupプロシージャを追加するという方法が考えられます。)

ComboBoxの入力文字を小文字不可としたい

【ご質問】
ComboBoxで入力する文字を”小文字入力不可”としたいのですが、どのようにしたらよいのでしょうか?

【回答】
SkinnableComboBoxUI.replace-selection-with-string(メソッド)を
改変することで実現可能です。

詳細はサンプルをご参照ください。

{curl 7.0,8.0 applet}
{curl-file-attributes character-encoding = “shift-jis”}

{define-class public open MyComboBox {inherits ComboBox}
  {constructor public {default …}
    {construct-super …}
  }

  {method public open {replace-selection-with-string
                          text:StringInterface
                      }:void

    set text = {text.to-upper-clone}
    {super.replace-selection-with-string text}
  }
}

{let cb:MyComboBox = {MyComboBox width = 3cm}}
{let cb2:ComboBox = {ComboBox width = 3cm}}

{HBox
    {VBox
        “↓小文字入力不可”,
        cb
    },
    {Fill width = 0.5cm},
    {VBox
        “↓小文字入力可”,
        cb2
    }
}

また以下のFAQをご参照ください。
http://developers.curlap.com/faq/48-faq-specification/386-2010-10-18-07-59-02.html

デバッグができない

◆ご質問◆
IDEにてデバッグを行うことができません。
どのような場合にデバッグが行えないのでしょうか?

◆回答◆
以下の場合、デバッグを行うことが出来ませんので、ご注意ください。

 ・デバッグ対象ソースコードのバージョンと同じバージョンのIDEがインストールされていない場合
 ・Curlコントロールパネルのデバッガタブにロケーションが追加されていない場合
 ・マップするURLのパスとソースコードのディレクトリが一致していない場合
 ・サーバに配置されているソースコードがpcurl化されている場合

choose-fileプロシージャとCurlのDialogの挙動の違いについて

【ご質問】
別々のViewからchoose-fileプロシージャとDialogにてダイアログ表示を行っています。

choose-fileプロシージャで選択を行った場合、
片方のDialogが閉じられるまでモーダル状態になります。

しかし、別々のViewからモーダルのDialogを表示させた場合は、
上記動作とは違い、片方のウィンドウが閉じられてもそのウインドウを表示した
Viewは操作出来ます(モーダル状態になっていません)。

なぜこのような違いが生じるのでしょうか。

【回答】
choose-fileプロシージャとCurlのDialogの挙動の違いについてですが、
choose-fileプロシージャは”WindowsAPI”の機能を使い、
“Winodws”のファイル選択ダイアログを表示して値を取得しています。

つまり、
choose-fileプロシージャで表示されているファイル選択ダイアログは
Curlが生成して表示しているのではなくWindowsが表示しております。

このためCurlのモーダルの考え方(実装の仕方)とWindowsのファイル選択ダイアログ
の考え方の違いが今回の現象を発生させています。

RecordGridのソートについて

【ご質問】
RecordGridにて、「特定カラムの昇順・降順のソートを第1条件とし、
その他カラムの昇順を第2条件とする」にはどのようにしたらよいのでしょうか。

【回答】
第1条件対象の”特定カラム”のsort-specに昇順か降順かの設定を行い、
第2条件対象の”その他カラム”のsort-specにnullを設定することで実現可能です。

詳細は以下のサンプルをご参照ください。

{curl 6.0,7.0,8.0 applet}
{curl-file-attributes character-encoding = “shift-jis”}

{let records:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField
                “id”, caption = “row no”, domain = int,
                index-type = RecordFieldIndexType.unique
            },
            {RecordField “level”, domain = String},
            {RecordField “Lv1”, domain = String},
            {RecordField “Lv2”, domain = String},
            {RecordField “Lv3”, domain = String},
            {RecordField “Lv4”, caption = “金額”, domain = int}
        },
        {RecordData id = 1,  level = “1”,     Lv1 = “Fred”,  Lv2 = “”,          Lv3 = “”,  Lv4 = 700},
        {RecordData id = 2,  level = “1-1”,   Lv1 = “Fred”,  Lv2 = “Cambridge”, Lv3 = “”,  Lv4 = 450},
        {RecordData id = 3,  level = “1-1-1”, Lv1 = “Fred”,  Lv2 = “Cambridge”, Lv3 = “E”, Lv4 = 150},
        {RecordData id = 4,  level = “1-1-2”, Lv1 = “Fred”,  Lv2 = “Cambridge”, Lv3 = “F”, Lv4 = 300},
        {RecordData id = 5,  level = “1-2”,   Lv1 = “Fred”,  Lv2 = “Boston”,    Lv3 = “”,  Lv4 = 100},
        {RecordData id = 6,  level = “1-2-1”, Lv1 = “Fred”,  Lv2 = “Boston”,    Lv3 = “Z”, Lv4 = 100},
        {RecordData id = 7,  level = “1-3”,   Lv1 = “Fred”,  Lv2 = “Hartford”,  Lv3 = “”,  Lv4 = 150},
        {RecordData id = 8,  level = “1-3-1”, Lv1 = “Fred”,  Lv2 = “Hartford”,  Lv3 = “A”, Lv4 = 150},
        {RecordData id = 9,  level = “2”,     Lv1 = “Pat”,   Lv2 = “”,          Lv3 = “”,  Lv4 = 950},
        {RecordData id = 10, level = “2-1”,   Lv1 = “Pat”,   Lv2 = “Keene”,     Lv3 = “”,  Lv4 = 700},
        {RecordData id = 11, level = “2-2”,   Lv1 = “Pat”,   Lv2 = “New Haven”, Lv3 = “”,  Lv4 = 100},
        {RecordData id = 12, level = “2-3”,   Lv1 = “Pat”,   Lv2 = “Concord”,   Lv3 = “”,  Lv4 = 150},
        {RecordData id = 13, level = “2-3-1”, Lv1 = “Pat”,   Lv2 = “Concord”,   Lv3 = “E”, Lv4 = 150},
        {RecordData id = 14, level = “3”,     Lv1 = “Glenn”, Lv2 = “”,          Lv3 = “”,  Lv4 = 611},
        {RecordData id = 15, level = “3-1”,   Lv1 = “Glenn”, Lv2 = “Storrs”,    Lv3 = “”,  Lv4 = 306},
        {RecordData id = 16, level = “3-1-1”, Lv1 = “Glenn”, Lv2 = “Storrs”,    Lv3 = “O”, Lv4 = 103},
        {RecordData id = 17, level = “3-1-2”, Lv1 = “Glenn”, Lv2 = “Storrs”,    Lv3 = “Q”, Lv4 = 102},
        {RecordData id = 18, level = “3-1-3”, Lv1 = “Glenn”, Lv2 = “Storrs”,    Lv3 = “S”, Lv4 = 101},
        {RecordData id = 19, level = “3-2”,   Lv1 = “Glenn”, Lv2 = “Storrs2”,   Lv3 = “”,  Lv4 = 305},
        {RecordData id = 20, level = “3-2-1”, Lv1 = “Glenn”, Lv2 = “Storrs2”,   Lv3 = “I”, Lv4 = 155},
        {RecordData id = 21, level = “3-2-2”, Lv1 = “Glenn”, Lv2 = “Storrs2”,   Lv3 = “M”, Lv4 = 150}
    }
}

{let sort1:RecordSort = {RecordSort {proc {x:Record, y:Record}:int
                                        {let x-val:String = x[“level”] asa String}
                                        {let y-val:String = y[“level”] asa String}
                                        {set x-val = {x-val.substr 0, 1}}
                                        {set y-val = {y-val.substr 0, 1}}
                                        {if {x.compare-field “Lv1”, y[“Lv1”]} != 0 then
                                            {return {x.compare-field “Lv1”, y[“Lv1”]}}
                                         else
                                            {return {(x[“level”] asa String).compare (y[“level”] asa String)}}
                                        }
                                    }
                        }
}
{let sort2:RecordSort = {RecordSort.from-string “Lv2, level”}}

{let record-grid:RecordGrid =
    {RecordGrid
        record-source = records,
        height = 15cm,
        width = 20cm,
        {RecordGridColumn “id”, edit-on-focus? = false, sort-spec = null},
        {RecordGridColumn “level” ,sort-spec = null},
        {RecordGridColumn “Lv1”, sort-spec = {RecordSort.from-string “Lv1 ASC”}},
        {RecordGridColumn “Lv2” ,sort-spec = null},
        {RecordGridColumn “Lv3” ,sort-spec = null},
        {RecordGridColumn “Lv4” ,sort-spec = null}
    }
}

{View
    title = “Grid sample”,
    {Frame width = 21cm, height = 16cm,
        {VBox
            record-grid
        }
    },
    visibility = “normal”,
    {on WindowClose do
        {exit}
    }
}