「Curlにて実現したい動作について」カテゴリーアーカイブ

Curlに対する要求仕様について、過去にお問い合わせの多かった事例を紹介いたします。記載日時点での状況(最新or問い合わせ時指定バージョンにおける仕様)を元に回答したものです。
 

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

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}
    }
}

RecordGridのValueFinishedイベント時に直前に選択していた行を取得するには

◆ご質問◆
RecordGridにてValueFinished イベント処理時に
直前に選択されていた行を取得するにはどのようにしたらよいのでしょうか?

◆回答◆
ValueFinished イベント処理時に直前に選択されていた行のセルを取得する方法として、
直前のセルのインデックスを持たせる為のグローバル変数を作成し、
ValueFinishedイベントの中でその変数を使用して処理を記述する方法がございます。

まず、直前のセルのインデックスを持たせる為のグローバル変数を作成します。

次に、RecordGridクラス内でRecordGridに対する
“CurrentRecordChangeRequestイベント”と”CurrentRecordChangedイベント”を呼び出し、
両方でレコードグリッドのcurrent-indexを上記のグローバル変数に格納する処理を行わせます。

これにより、フォーカスを移動した後のValueFinishedイベントにおいて、
CurrentRecordChangedRequestイベントで取得した
「直前のセルのインデックス」を使用することができます。
(CurrentRecordChangedRequestはValueFinishedの前に発生します。
ValueFinishedのタイミングでcurrent-indexの値が変わる為、
CurrentRecordChangedRequestのタイミングでのcurrent-indexは
直前のレコードのインデックスとなります。)

RecordGridの1レコードにつき2行で表示させるには

【ご質問】
RecordGridの行を、1レコードにつき2行で表示させることは可能でしょうか。

【回答】
RecordGrid、RecordGridCell関連の標準APIでは
複数行にまたがって表示することはできません。

ただし、TextFieldの中にVBox等のコンテナを埋め込み、
その中に複数行に表示するようコントールを埋め込むという方法は考えられます。

しかし、この場合、どのようにRecordSetでデータを持たせるか、
tabキーでの遷移の制御など、非常に複雑なロジックが必要になると思われます。

曲線上でイベントを実施したい

【ご質問】
曲線を作成し、その線上にてイベントを実施させることは可能でしょうか?

【回答】
Pathを使用することで実現可能です。
詳細は以下のサンプルをご参照ください。

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

{import * from CURL.GUI.SHAPES}

{define-proc package {make-path}:Path
    let path:Path =
        {Path
            {Distance2d  0mm, 40mm},
            PathOperation.curve-to,
            {Distance2d  0mm, 20mm},
            {Distance2d 20mm, 20mm},
            {Distance2d 20mm, 40mm},
            PathOperation.move-to,
            {Distance2d 20mm, 40mm},
            PathOperation.curve-to,
            {Distance2d 40mm, 40mm},
            {Distance2d 40mm, 60mm},
            {Distance2d 20mm, 60mm},
            PathOperation.move-to,
            {Distance2d 20mm, 60mm},
            PathOperation.curve-to,
            {Distance2d 20mm, 80mm},
            {Distance2d  0mm, 80mm},
            {Distance2d  0mm, 60mm}
        }
    {return path}
}

{define-class package final CurvePathShape {inherits PathShape}
  field private curve-path:Path
  field private approx-path:Path

  {constructor package {default path:Path, …}
    set self.curve-path = path
    set self.approx-path = {path.to-line-segment-path max-angle = 10deg}
    {with-compiler-directives allow-slow-spliced-arguments? = true do
        {construct-super
            self.approx-path,
            …
        }
    }
  }

  {method public {draw renderer2d:Renderer2d}:void
    {unless self.path == self.approx-path do
        set self.curve-path = self.path
        set self.approx-path = {self.path.to-line-segment-path max-angle = 10deg}
        set self.path = self.approx-path
    }
   
    let constant p-s:Distance = {self.get-local-device-pixel-size}
    let (stroke-thickness:Distance, thin-line?:bool) =
        {self.quantize-line-thickness
            self.stroke-thickness,
            quantum-size = p-s
        }
    {if thin-line? or stroke-thickness <= p-s then
        set stroke-thickness = 0m
    }

    {renderer2d.render-path
        self.curve-path,
        fill-pattern = self.color,
        stroke-thickness = stroke-thickness,
        line-style = self.line-style
    }
  }
}

{let shape:PathShape =
    {CurvePathShape
        {make-path},
        {on PointerEnter at l:PathShape do
            {set l.color = “pink”}
        },
        {on PointerLeave at l:PathShape do
            {set l.color = “black”}
        },       
        dragee = {ShapeDragee},
        shape-selectable = null,
        cursor = cursor-hand,
        stroke-thickness = 3
    }
}

{let p1:Distance2d = {Distance2d 10mm, 10mm}}
{let p2:Distance2d = {Distance2d 10mm, 20mm}}
{let p3:Distance2d = {Distance2d 100mm, 20mm}}
{let p4:Distance2d = {Distance2d 100mm, 10mm}}
{let newPath:Path = {Path p1, PathOperation.curve-to, p2, p3, p4}}

{let c:Canvas =
    {Canvas background=“white”, width=200mm, height=200mm}
}

{value
    {set shape.path = newPath}
    {c.add shape}
    c
}

複数アプレットを同時に起動させたくない

◆ご質問◆
既にアプレットが起動していた場合、別のアプレットを起動させたくないのですが可能でしょうか。

◆回答◆
独立型アプレットの場合、
デフォルトでは同一アプレットの複数起動は行いません。
これを複数起動を許す場合、
Appletクラスにallow-multiple-instancesメソッドを呼び出すことで
複数のアプレットを起動することができます。

独立型アプレットで無い場合、
Curl自身でご質問にあるような機能は標準APIでは御座いません。

このような機能を実現するには何らかの作りこみが必要になってきます。
例えば、
ただ単純に1つのPCの中で同時に2つ以上のアプレットを起動したくない場合は、
1つ目のアプレットが起動した時に、ファイルもしくはパーシスタントデータ等に
1つ目のアプレットが起動していることを表すデータを書き込みます。
2つ目に起動しようとしたアプレットは、
1つ目のアプレットが書き込んだデータを読み、1つ目のアプレットが存在していることを知らせ、
起動させるのではなく、終了させるようにプログラムの中で分岐させます。
また、1つ目のアプレットが終了する際には書き込んだデータを削除しておきます。

ただ、正常に1つ目のアプレットが起動、終了を行う場合は問題ないのですが、
何らかの問題により1つ目のアプレットが異常終了した場合(OS自身が強制終了してしまった、電源が急に落ちた等)、
1つめのアプレットが起動していることを示すデータが残ったままになるため
再びアプレットを起動することができなくなる問題があります。
これを回避するようにある特定の時間が経過するとそのデータを無視するなどの
工夫も必要になってくるかと思います。

Record順にデータを取得するには

◆ご質問◆
RecordGridのRecord順に取得するにはどのようにすればよいのでしょうか?

Curl開発者ガイドの以下サンプルでは、表示順が不規則になっています。
 データの管理と表示
 └データレコードとグリッド
  └RecordGridでのデータ選択
   └行と列の選択

◆回答◆
該当の箇所はrg.selection.recordsで、
返されるオブジェクトは{Iterator-ofint}となります。
Iterator-ofはヘルプにもありますように要素の順列は保証されません。

また、RecordSet自身はただのデータの集合のみで
データ(レコード)の順序は厳密には持ちません。

ですのでレコード順に取得したい場合は、
何かレコード順であることを示すキーを用いてソートする必要があります。

RecordGridのカラム幅をPixcelで取得するには

【ご質問】
レコードグリッド上のカラム幅を任意に伸縮した後に当該カラムの幅をPixcelで取得するには
どのようにしたらよいのでしょうか。

【回答】
カラムの幅は何も処理を加えずに単純に取得するとSI-単位のm(メートル)で返されます。
これをpixelに変換するにはDisplayContext.pixel-sizeを使用して計算する必要があります。

詳細は以下のサンプルをご参照ください。
「First」列のカラム幅をドラッグで適当に変更して
「Savevalue」ボタンを押してください。
「First」列の幅の値がpxで表示されます。

{curl 6.0,7.0,8.0 applet}
{curl-file-attributes character-encoding = “shift-jis”}
{applet
    {compiler-directives
        allow-any-calls? = true,
        allow-implicit-any-casts? = true
    }
}
{import * from  CURL.GRAPHICS.DISPLAY}
{def display = {Display.get-primary}}

{import * from CURL.LANGUAGE.SOURCE}
{let display-context:DisplayContext =
    {get-default-display-context}}

{let people:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField “First”, domain = String},
            {RecordField “Last”, domain = String},
            {RecordField “Age”, domain = int}
        },
        {RecordData First = “John”, Last = “Smith”, Age = 25},
        {RecordData First = “Jane”, Last = “Smith”, Age = 29},
        {RecordData First = “Jane”, Last = “Jones”, Age = 28}
    }
}
{def rgc1 = {RecordGridColumn
                width = 100px,
                “First”}
}
{def rgc2 = {RecordGridColumn width = 2cm,“Last”}}
{def rgc3 = {RecordGridColumn width = 1.5cm,“Age”}}

{let save-width-value:String = {format “%s”,rgc1.width }}

{value
    {VBox
        {RecordGrid
            record-source = people,
            cells-take-focus? = false,
            select-current-record? = true,
            height = 10cm,
            width = 13cm,
            automatic-columns? = false,
            rgc1,
            rgc2,
            rgc3
        },
        {VBox
            {HBox
                {TextDisplay value = “Set-width with cm”},
                {TextField
                    {on ValueFinished at tf:TextField do
                        {if tf.value != “” then
                            set rgc1.width = {evaluate {format “%scm”,tf.value}}
                        }
                        {popup-message
                            tf.value & “cm = “ &
                            {format “%s”,{round rgc1.width / display-context.pixel-size}} &
                            ” px”
                        }
                    }  
                }
            },
            {HBox
                {TextDisplay value = “Set-width with px”},
                {TextField
                    {on ValueFinished at tf:TextField do
                        {if tf.value != “” then
                            ||String型で保存していたので、単位をつけてevaluate
                            set rgc1.width = {evaluate {format “%spx”,tf.value}}
                        }
                    }  
                }  
            }          
        },
        {HBox
            {CommandButton label = “Show width !”,
                {on Action do
                    {popup-message
                        {VBox
                            “rgc1.width = “ & {format “%s”,rgc1.width}
                        }
                    }
                }
            },
            {CommandButton label = “Save value”,
                {on Action do
                    set save-width-value =
                        {if rgc1.width isa PixelDistance then
                            {format “%s”,rgc1.width}
                         else
                            {format “%spx”,{round rgc1.width / display-context.pixel-size}}
                        }
                    {popup-message
                        “saved-value = “ & save-width-value    
                    }
                }
            },
            {CommandButton label = “Restore value”,
                {on Action do
                    set rgc1.width = {evaluate save-width-value}
                }
            }
        }
    }
}

OCCのモジュール単位のインストールについて

【ご質問】
OCC機能を使用するにあたりモジュール単位(パッケージ単位)でインストールを行うことは可能でしょうか。

【回答】
可能です。

パッケージ単位でOCCのコピーを行なうにはディレクトリ構成に工夫が必要になります。
 ・「メインモジュール」ディレクトリ
 ・「パッケージ1モジュール」ディレクトリ
 ・「パッケージ2モジュール」ディレクトリ
 …
上記の様にパッケージを呼び出すメインモジュール(start.curl、manifest.mcurlファイルなどが含まれる)と
各パッケージモジュールは別のディレクトリに分ける必要があります。

詳細はサンプルをご参照ください。
(バージョン6・7・8用のサンプルです。)
このサンプルでは、Curlルートディレクトリの中に
 「MAIN」ディレクトリ
 「PACKAGE1_1」ディレクトリ
 「PACKAGE1_2」ディレクトリ
 「PACKAGE1_3」ディレクトリ
があります。
「MAIN」ディレクトリ内に manifest.mcurl、start.curl  が格納されており
start.curlがら各モジュール(パッケージ)を呼び出しています。
このような構成で適切に curl-timestamp.txt、curl-archive.car(curl-contens.txt) を配置、
記述すればパッケージ単位で更新することが可能になります。

更新されるモジュールの判断基準は、
Curlルートのタイムスタンプが新しく、各モジュールのディレクトリにあるタイムスタンプの新しいもの
が更新されます。

なお、OCCをモジュール単位で配備して使用する場合は
Curl IDEのOCCデプロイ機能は使用できません。
APIを使用して必要なファイルを作成してください。

・curl-archive.car(curl-contents.txt)
 create-curl-archive-file(create-curl-contents-file)プロシージャを使用して各パッケージごとに作成します。

・curl-timestamp.txt
 create-curl-timestamp-fileプロシージャを使用して各パッケージごとに作成します。

・curl-modules.txt
 create-curl-modules-fileプロシージャを使用してルートディレクトリに作成します。

詳細は、Curl開発者ガイドの
[外部リソースとの対話]-[随時接続コンピューティング (OCC)]
の項と、APIリファレンスの
[CURL.IO.PACK]-[create-curl-archive-file]・[create-curl-contents-file]
                       [create-curl-modules-file]・[create-curl-timestamp-file]
の項をご参照ください。

DLLファイルがエクスポートするクラスの利用について

◆ご質問◆
DLLファイルがエクスポートするクラスの利用(※)は可能でしょうか。
※__declspec(dllexport)でエクスポートしたクラスのメンバ関数呼び出しなど。

◆回答◆
DLLファイルがエクスポートするクラスを利用する方法については、
通常のDLLファイルの利用方法と同様になります。

Curl開発者ガイドの
[基本概念 – ライブラリ]→[外部ライブラリ]の項を
ご参照ください。

RecordGridにて拡張したCellのフォーカスアウトイベントが複数回発生する

【ご質問】
レコードグリッドセルにて拡張したセルをフォーカスアウトすると
イベントが複数回発生してしまいます。
解決するにはどうしたらよいでしょう。

【回答】
RecordGridはさまざまなコントロールが複雑に絡み合って構成されております。
直接セル内のTextFieldに対してFocusIn、FocusOutのイベントを
設定しても意図した通りのイベントの発生となるとは限りません。

RecordGrid内でスクロールされた場合や、クリックされた場合、
再描画する必要がある場合などにrefresh-dataメソッドが呼ばれ、
その中でRecordGridCellが使い回しされ、RecordGridのセルに当てはめられていきます。
その際にそのセルの中で使われているTextFieldに
FocusIn、FocusOutのイベントが付いている場合、複数回のイベントが発生します。

このような状況でセル間のフォーカスが入った、出て行った事を通知する
RecordGridCell.note-grid-focus-in/RecordGridCell.note-grid-focus-out
メソッドが用意されています。
上記メソッドを使用することで実現することが可能かと思われます。

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

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

{define-enum public Rank
    First,
    Second,
    Third
}

{let records:RecordSet =
    {RecordSet
        {RecordFields
            {RecordField
                “id”, caption = “User ID”, domain = int,
                index-type = RecordFieldIndexType.unique
            },
            {RecordField “Last”, domain = String},
            {RecordField “First”, domain = String},
            {RecordField “City”, domain = String}
        },
        {RecordData id = 1, Last = “Smith”, First = “Gene”, City = “Boston”},
        {RecordData id = 2, Last = “Smith”, First = “Fred”, City = “Cambridge”},
        {RecordData id = 3, Last = “Smith”, First = “Mike”, City = “Keene”},
        {RecordData id = 4, Last = “Smith”, First = “Ben”, City = “New Haven”},
        {RecordData id = 5, Last = “Abrams”, First = “Ben”, City = “Boston”},
        {RecordData id = 6, Last = “Jones”, First = “Sam”, City = “Storrs”},
        {RecordData id = 7, Last = “Stevens”, First = “Nigel”, City = “Hartford”},
        {RecordData id = 8, Last = “Stevens”, First = “Bert”, City = “Cambridge”},
        {RecordData id = 9, Last = “Linden”, First = “Pat”, City = “Hartford”},
        {RecordData id = 10, Last = “Abrams”, First = “Mat”, City = “Boston”}
    }
}
{define-class public TestCell {inherits StandardStringCell}
  {constructor public {default}
    {construct-super}
  }

  {method public {note-grid-focus-in}:void
    {super.note-grid-focus-in}
    {output “FocusIn “ & “self::[[“ & self & “]]”}
  }
  {method public {note-grid-focus-out}:void
    {super.note-grid-focus-out}
    {output “FocusOut “ & “self::[[“ & self & “]]”}
  } 
}

{let record-grid:RecordGrid =
    {RecordGrid
        record-source = records,
        height = 10cm,
        width = 13cm,
        {RecordGridColumn “id”, cell-spec = TestCell,edit-on-focus? = false},
        {RecordGridColumn “Last”},
        {RecordGridColumn “First”},
        {RecordGridColumn “City”},

        {on FocusIn do
            {output “FocusIn”}
        },
        {on event:FocusOut do
            {output “FocusOut”}
        }
    }
}

{View
    {Frame width = 13cm, height = 10cm,
        {VBox
            record-grid
        }
    },
    visibility = “normal”,
    {on WindowClose do
        {exit}
    }
}

 

画面上に表示されている内容と印刷結果の内容が異なる

◆ご質問◆
画面上では枠内に表示されている文字が、印刷をすると枠外にはみ出して表示される。
解決方法あるのでしょうか。

◆回答◆
画面上に表示されているものと印刷されたものを
【完全に】一致させることは不可能です。

使用されるディスプレスの解像度、DPI、フォントのバージョンや
プリンタドライバ、プリンタフォントなどの様々な要因によって
ディスプレイ上に表示されているものとプリンタで印刷された結果に
微小な差が出ることがあります。
これは、Curlに限ったことではなく多くのアプリケーションでも発生する問題です。

「ずれ」に対して対応できるように余裕を持たせて
レイアウトの作成を行うようお願いします。

RecordGridにて表示される値に誤差が生じる

◆ご質問◆
グリッドのドメインが「double」の場合に、
小数値を入力すると誤差が生じるケースが存在します。

どのようにすれば誤差を生じさせず、表示することができるのでしょうか。

◆回答◆
小数以下の数値で誤差を生じさせない方法として、
2点挙げられます。

■1点目
 RecordFieldのドメインにStringを設定し、
 入力した値をそのまま表示させるようにし、
 計算を行う際には値の小数を整数に変換した上で
 計算するという方法です。

 まず、プログラムで扱う小数点以下の最大桁数を決めます。
 (下記のサンプルであれば、小数点以下3桁が最大桁数)
 その桁数までが整数となるように
 10のN乗を掛け、整数として計算を行い、
 その結果を10のN乗で割ります。
 これにより、浮動小数点数を介さずに計算を行い、
 誤差の発生を抑止できます。

 ==============================================
 {let number:double = 0}

 {for i:int = 1 to 100000 do
     ||小数0.003を整数にするために1000倍する
     set number = number + 0.003*1000
 }
 ||上記で1000倍した分を元に戻す
 {set number = number/1000}
 {value (number asa int)}

 ==============================================

■2点目
 RecordFieldのドメインにStringを設定し、
 計算の時には、BigDecimalを使用するという方法です。

 CurlではjavaでいうところのBigDecimalと類似した機能を扱うことができます。
 BigDecimalでは任意の長さの10進数の表現と操作を行え、
 浮動小数点数のように10進数から2進数に変換するという動作をしないため、
 丸め誤差を生じさせずに計算を行う事ができます。

 BigDecimalクラスは既存のCurlAPIではないため、
 以下URLよりダウンロードする必要がございます。
 http://www.curlap.com/curl/bigDecimalSample.zip

 ダウンロードしたパッケージ(big-pkg.curl)をBigDecimalを
 使用したいアプレットにインポートすることにより、
 BigDecimalクラスを使用することができます。
  (但し、big-pkg.curlの内容に関してサポートの範囲外になるため、
 このパッケージに関するサポートは行いません。
 あらかじめご了承下さい。)

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

TabContainer内のテキストが範囲選択できない

【ご質問】
HBoxやVBoxに配置したテキストは範囲選択が可能ですが、
TabContainer内に配置したテキストは範囲選択することができません。

TabContainer内のテキストを範囲選択可能にするためには、
どのような設定をすればよいでしょうか。

【回答】
TabContainer内に配置したテキストが範囲選択できないのは仕様です。
TabPane内のテキストをGuiRangeSelectionFrameで囲めば選択できるようになります。

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

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

{TabContainer
    {TabPane
        {GuiRangeSelectionFrame
            {text font-size = 28pt, TabContainerの作成}
        }
    },
    {TabPane
        {text font-size = 28pt, 2}
    },
    {TabPane
        {text font-size = 28pt, 3}
    }
}

ListBoxにて右クリックでValueFinishedを行いたい

【ご質問】
ListBoxにて値を左クリックで変更するときには”ValueFinished”イベントが
実行されますが、右クリックで変更するときには実行されません。

右クリック時に”ValueFinished”イベントを実行させることは可能でしょうか。

【回答】
ListBox内において、右クリックでイベントを発生させる方法として、
ValueFinishedの代わりにValueChangedを使用する方法が考えられます。

{on ValueChanged at box:ListBox do
    {popup-message “ValueChanged”}
}

また、ValueFinishedを使用する必要がある場合は、
ValueChangedイベントの中でValueFinishedイベントを呼び出すという方法もあります。
動作としてはValueFinishedの代わりにValueChangedを使用した場合とほとんど変わりません。

ListBoxクラスの既定では、ValueFinishedはマウスの左ボタンを
離した際に、イベントが発生するようになっています。
その為、既定のListBoxにおいて、マウスの右ボタンを使用して
ValueFinishedのイベントを発生させることはできません。

PointerReleaseがマウスの左ボタンによって行われた場合は、
ListBoxクラスのfire-value-finishedメソッドが呼び出されるように設定されています。
マウスの右ボタンでPointerReleaseが行われても、
fire-value-finishedメソッドが呼び出されないためValueFinishedは発生しません。

マウス左ボタンのPointerReleaseイベントでのみ、
ValueFinishedイベントが発生するように設定されている理由は、
マウス右ボタンのPointerReleaseイベントを
コンテキストメニューの呼び出しに利用するためです。

子画面起動時のKeyPressイベントについて

【ご質問】
画面からボタン押下で子画面(ダイアログ(modal?=true))を生成して、子画面の起動を行い、
子画面でESCキーを押下すると子画面のWindowCloseイベントが発生し、子画面が閉じます。

「×」をマウスでクリックした場合は、WindowsCloseイベントを発生させ、
ESCキーを押下した際に、WindowCloseイベントを発生させないようにすることは可能でしょうか。

【回答】
DialogにKeyPressイベントをつけるのではなく、
GuiManager(アプレット全体)にKeyPressイベントを
つけてはどうでしょうか。

{{get-gui-manager}.add-event-handler
    {on key:KeyPress do
        {if key.value == KeyPressValue.esc then
            {key.consume}
        }
    }
}

しかし、アプレット全体につけることになるので
他の場所(Dialog以外)でもKeyPressイベントが動きますので
他の場所においてエスケープキーで何か処理させているような
箇所がある場合は注意が必要になります。

「各行の先頭の文字番号」を取得するには

【ご質問】
テキストエリアに表示している文字の
「各行の先頭の文字番号」を取得するにはどうしたらよいのでしょうか。

【回答】
CurlRTE4.0ではオープンコントロールを利用すれば
“テキストエリアの各行の先頭の文字番号”を取得することが出来ます。

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

※オープンコントロールを使用しているため、
  パッケージをロードする際にパフォーマンスに影響が生じる可能性があります。
  使用する際にはご注意ください。

Curl起動時間の短縮について

◆ご質問◆
初回Curl起動時には20-40秒程度かかり、次回からは5秒程度とかなり差があります。
初回Curl起動にも5秒程度となるのを目指しています。

Curl起動時間を短縮する上で、推奨される構成や手法はあるのでしょうか。

◆回答◆
パッケージキャッシュが有効ということから、
初回起動が20-40秒、次回から5秒という差は
一般的にコンパイル処理の有無の差ということになります。

一度起動(コンパイル)したアプレットは二回目以降、再度コンパイルしなくても良い様に、
コンパイル済みのパッケージを保存し、それを利用することによって起動時間を短くしています。

この初回起動の時にコンパイル時間を0にする、つまり、初回起動時間を5秒にすることは
出来ませんが減らすことは可能な場合があります。


起動時に必要なクラス、プロシージャ等を最小限に留め、
パッケージの構成を見直すことです。起動時に不必要なクラスなどが
多く含まれている場合、その分コンパイルに時間がかかります。


起動ファイル(start.dcurl)の内容を極力短くし、
行う処理をパッケージに含めてしまいます。
パッケージに含めることでその処理がパッケージキャッシュされ、
その分のコンパイル時間が減少します。


起動ファイル自身は一般的に毎回サーバから取得されます。
(ブラウザのキャッシュから取得される場合もあります。)
この起動ファイルを短くすることは、ファイルサイズが小さくなるということですの
でサーバからの応答時間も短くなります。

RecordGridのソート(昇順・降順)の判別

【ご質問】
一定の固定値に対して「昇順,降順」と切り替えた場合でも、固定値に関しては
常に同じ位置に表示したいと思っています。

RecordSortの「昇順、降順」の状態を判別するAPIは存在するのでしょうか。

【回答】
RecordSortの「昇順、降順」の状態を判別する標準APIは用意されておりません。
ただし、昇順、降順を判別するフラグを定義し、独自で実装することは可能だと思います。

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

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

{let ascending?:bool = true}
{define-proc public {custom-sort rgc:RecordGridColumn}:RecordSort

    set rgc.grid.sort = {RecordSort
                                  {proc {r1:Record, r2:Record}:int
                                      let val:String = r1[“id”] asa String
                                      let val2:String = r2[“id”] asa String
                                      {if ascending? then
                                          {if (val == “”)  and (val2 == “”) then
                                              {return -1}
                                          else
                                              {return {r1.compare-field “id”, r2[“id”]}}
                                          }
                                          set ascending? = false
                                          {dump ascending?}
                                      else
                                          {if (val == “”)  or  (val2 == “”then
                                              {return 1}
                                          else
                                              {return {r1.compare-field “id”, r2[“id”]}}
                                          }
                                          set ascending? = true
                                          {dump ascending?}
                                      }
                                  }
                              }
    {if ascending? then
        set ascending? = false
        {dump ascending?}
     else
        set ascending? = true
        {dump ascending?}
    }
    {return
        rgc.grid.sort asa RecordSort
    }
}

{let rs:RecordSet = {RecordSet
                                {RecordFields
                                    {RecordField “id”, domain = String}
                                },
                                {RecordData id = “a”},
                                {RecordData id = “b”},
                                {RecordData id = “c”},
                                {RecordData id = “d”},
                                {RecordData id = “”},
                                {RecordData id = “”},
                                {RecordData id = “”},
                                {RecordData id = “e”},
                                {RecordData id = “f”},
                                {RecordData id = “g”},
                                {RecordData id = “h”},
                                {RecordData id = “i”},
                                {RecordData id = “3”},
                                {RecordData id = “2000”},
                                {RecordData id = “2000”},
                                {RecordData id = “j”}                       
                            }
}

{let rg:RecordGrid = {RecordGrid
                                 record-source = rs,
                                 automatic-columns? = false,
                                 {RecordGridColumn “id”, halign = “right”,
                                     sort-spec = custom-sort
                                 }
                             }
}

{let v:View = {View
                       width = 5in,
                       height = 5in,
                       rg
                   }
}
       
{v.show}

改行コードを除いてコピーをしたい

【ご質問】
テキストの文字列を選択し、「Crtl+C」を押下した際に
選択した文字列内の改行コードを除いてコピーすることは可能でしょうか?

【回答】
CopyCommandクラスを継承したクラスのexecuteメソッドに
おいて処理したい内容を記述すれば宜しいかと思います。

詳細は以下のサンプルをご参照ください。
(今回は改行コードを除去してクリップボードに設定しています。)

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

{import * from CURL.DESKTOP.CLIPBOARD}

{define-class public CustomCopyCommand {inherits CopyCommand}
  {constructor public {default context:SelectionContext}   
    {construct-super context}
  }
 
  {getter public open {context}:SelectionContext
    {return super.context asa SelectionContext}
  }
 
  {getter public open {enabled?}:bool
    {return true}
  }
   
  {method protected open {execute}:void 
    {if not self.enabled? then {return}}
    let constant context:SelectionContext = self.context
    let ret:StringBuf = {StringBuf}
    let str:String = {(self.context.selection asa TextSelection).get-text}
    let strs:StringArray = {str.split}
    {for str in strs do
        {ret.concat str}
    }
    {{Clipboard.get-system-clipboard}.set-string ret}
  }
}

{define-class CustomCommandTA {inherits TextArea}
  {constructor {default …}
    {construct-super
        value = null,
        prompt = null,
        max-chars = -1,
        data-model = null,
        ui-object = null,
        width = 200pt,
        height = 100pt,
        …}
  }
  {method public open {create-command name:String}:#Command
    {return
        {switch name
         case “copy” do {CustomCopyCommand {non-null self.selection-context}}
         else
            {super.create-command name}
        }
    }
  }
}

{define-class CustomCommandRTA {inherits RichTextArea}
  {constructor {default …}
    {construct-super
        value = null,
        prompt = null,
        max-chars = -1,
        data-model = null,
        ui-object = null,
        width = 200pt,
        height = 100pt,
        …}
  }
  {method public open {create-command name:String}:#Command
    {return
        {switch name
         case “copy” do {CustomCopyCommand {non-null self.selection-context}}
         else
            {super.create-command name}
        }
    }
  }
}

{let rta1:RichTextArea =  {RichTextArea width = 10cm, height = 4cm}}
{let rta2:RichTextArea =  {CustomCommandRTA width = 10cm, height = 4cm}}
{let ta1:TextArea =  {TextArea width = 10cm, height = 4cm}}
{let ta2:TextArea =  {CustomCommandTA width = 10cm, height = 4cm}}
{let ta3:TextArea = {TextArea width = 10cm, height = 5cm}}

{value
    {VBox
        “通常のRichTextArea”,
        rta1,
        “改行をコピーしないRichTextArea”,
        rta2,
        “通常のRichTextArea”,
        ta1,
        “改行をコピーしないRichTextArea”,
        ta2,
        “下のTextAreaに貼り付けて確認して下さい”,
        ta3
    }
}