/*
 * TableMVCBase
 * HTMLのテーブルに、モデル・ビュー・コントロールを
 * 実装するためのクラスの、最低限の実装を提供します。
 *
 * このクラスのインターフェイスは、JavaのSwingをベースにしています。
 *
 * このファイルの利用について
 * このファイルの内容は、個人利用・商用利用に関わらず、一部または全部を、
 * 自由に利用していただいてかまいません。
 * 但し、引用元の表示か、著作者が（有）ワイシステムクリエイトであることを
 * 示す、著作権表示を必ず行ってください。
 * また、このファイルの内容を利用した事により、何らかの不利益を被った場合
 * でも、（有）ワイシステムクリエイトは、一切保証できませんので、利用者ご
 * 自身の責任において、ご利用くださるようお願いします。
 *
 * Copyright (C) 2005 Y System Create Corporation. All Rights Reserved.
 *
 * $Date: 2005/05/28 09:10:16 $
 * $Revision: 1.2 $
 */

/**
 * クラス変数の定義
 */
/** 選択された行のクラス */
TableView.selectRowClass = "selectedRow";
/** 選択されていない行のクラス */
TableView.nonSelectRowClass = "nonSelectedRow";
/** 選択された列のクラス */
TableView.selectColumnClass = "selectedColumn";
/** 選択されていない列のクラス */
TableView.nonSelectColumnClass = "nonSelectedColumn";
/** セルの内容を格納する要素のクラス */
TableView.cellValueContainerClass = "cellValueContainer"
/** セルの内容を格納する要素の表示状態を決定する要素のクラス */
TableView.cellValueContainerViewClass = "cellValueContainerView"
/** セルの内容を更新する要素のクラス */
TableView.cellEditorClass = "cellEditor";
/** セルの内容を更新する要素の表示状態を決定する要素のクラス */
TableView.cellEditorViewClass = "cellEditorView";
/** 編集可能セルのクラス */
TableView.editableCellClass = "editableCell";
/** 編集不可セルのクラス */
TableView.nonEditableCellClass = "nonEditableCell";
/*
 * テーブル表示コントロールクラス
 * コンストラクタ
 */
function TableView() {}

TableView.instanceList = new Object();
TableView.instanceCount = 0;

TableView.getInstanceById = function (id) {

    var instance = TableView.instanceList[id];
    if (null == instance) {

        /*
         * 例外の送出
         */
        throw WebApplyUtil.createException(new Error(),
                                           "Instance not found.");
    }

    return instance;
}

TableView.focusToCellEditor = function (id) {

    var instance = TableView.getInstanceById(id);
    try {
        instance.getCurrentCellEditor().focus();
    }
    catch (e) {}
}

TableView.focusToController = function (id) {

    var instance = TableView.getInstanceById(id);
    try {
        instance.controller.focus();
    }
    catch (e) {}
}

/*
 * JavaScriptで、継承を前提としたクラスを作成する場合は、
 * コンストラクタとは別に、インスタンスの初期化を行う、
 * 初期化メソッドを用意する必要がある。
 * 継承先のクラスは、コンストラクタ内で、このメソッドをコールする。
 */
TableView.prototype.initTableView = function (tableElement, controller, delay) {

    if (null == delay) {
        delay = 300;
    }
    this.tableElement = tableElement;
    this.id = TableView.instanceCount ++;
    this.jsTable = new JSTable(tableElement);
    this.controller = controller;
    this.selectedRow = -1;
    this.selectedColumn = 0;
    this.selectionListeners = new Array();
    this.selectionChangedTimer = new Timer(delay, this, false);
    this.model = null;

    TableView.instanceList[this.id] = this;

//    this.initializeView();
    EventController.addEventListener("keydown", controller, this);
    EventController.addEventListener("click", tableElement, this);

    /*
     * リスナの中から呼べるように、必要なメソッドを変数にセットする。
     */
//    var focusToController = this.focusToController;
    var getCurrentCellValueContainerView =
        this.getCurrentCellValueContainerView;
    var getCurrentCellEditorView = this.getCurrentCellEditorView;
    var thisInstance = this;
    var getModel = this.getModel;

    /*
     * 入力エリアを閉じて、表示エリアを表示するリスナ。
     */
    var inputListener = function (evnt) {

        evnt = EventUtil.createEvent(evnt);

        /*
         * EnterキーESCキー以外は無視する。
         */
        if (13 != evnt.keyIdentifier && 27 != evnt.keyIdentifier) {

            return;
        }

        /*
         * イベントの発生元が、inputかtextarea以外の場合は無視する。
         */
        var tagName = evnt.currentTarget.nodeName.toLowerCase();

        switch (tagName) {

            case "input" :

                break;

            case "textarea" :

                /*
                 * テキストエリアの場合は、シフト＋エンターのみ
                 */
                if (! evnt.shiftKey && 27 != evnt.keyIdentifier) {

                    return;
                }
                break;

            default :

                return;
        }

        thisInstance.invokeFocusToController();
        //focusToController.call(thisInstance);
        getCurrentCellEditorView.call(thisInstance).style.display = "none";
        getCurrentCellValueContainerView.call(thisInstance).style.display = "";

        if (27 == evnt.keyIdentifier) {

            thisInstance.setEditableValue(thisInstance.selectedRow,
                                          thisInstance.selectedColumn);
        }
        else {

            var model = getModel.call(thisInstance);
            model.setValueAt(evnt.currentTarget.value,
                             thisInstance.selectedRow,
                             thisInstance.selectedColumn);
        }
        thisInstance.isEditable = false;

        return;
    }   // inputListener End

    this.inputListener = inputListener;
    this.initializeView();

    /*
     * セルの更新要素を非表示とする。
     */
    this.noneDisplayEditor();
}

/*
 * イベント発生時に、イベントを処理するためのリスナの実装
 * @param event : Object : 発生したイベントを識別するイベントオブジェクト
 */
TableView.prototype.noneDisplayEditor = function () {

    /*
     * セルの更新要素を非表示とする。
     */
    var rowCount = this.jsTable.getBodiesRowCount();
    for (var i = 0; i < rowCount; i ++) {

        var row = this.jsTable.getRowFromBodiesAt(i);
        var cellCount = row.cells.length;
        for (var j = 0; j < cellCount; j ++) {

            var editor = this.getCellEditorAt(i, j);
            if (editor) {

                editor.onkeydown = this.inputListener;
            }

            var editorView = this.getCellEditorViewAt(i, j);
            if (editorView) {

                editorView.style.display = "none";
            }
        }
    }
}

/*
 * イベント発生時に、イベントを処理するためのリスナの実装
 * @param event : Object : 発生したイベントを識別するイベントオブジェクト
 */
TableView.prototype.actionPerformed = function (evnt) {
try {

    if ("keydown" == evnt.type) {

        switch (evnt.keyIdentifier) {
            case 37 : // 左
                this.goLeft(evnt);
                break;
            case 38 : // 上
                this.goUp(evnt);
                break;
            case 39 : // 右
                this.goRight(evnt);
                break;
            case 40 : // 下
                this.goDown(evnt);
                break;
            case 13 : // Return
                this.enterkeyDown(evnt);
                break;
            default :
        }
    }

    if ("click" == evnt.type) {

        this.mouseClicked(evnt);
    }
}
catch (e) {
    AppUtil.printExceptionLog(e);
}
}

/*
 * テーブルエレメントが、マウスクリックされた。
 */
TableView.prototype.mouseClicked = function (evnt) {

    /*
     * フォーカスが移ったときに、セルエディターが表示されていた場合は、
     * フォーカスを、カレントのセルエディタに移す。
     */
    if (this.invokeFocusToCellEditor()) {

        return;
    }

    /*
     * フォームコントロールがクリックされたときに、
     * フォーカスが移動してしまわないように。
     */
    if (evnt.target && "undefined" != typeof evnt.target.form) {
        return;
    }
    this.invokeFocusToController();
}

/*
 * 選択行を一行上げる。
 */
TableView.prototype.goUp = function (evnt) {

    this.selectRowAt(this.selectedRow - 1);
    this.fireSelectionChanged(this.selectedRow, this.selectedColumn);
}

/*
 * 選択行を一行下げる。
 */
TableView.prototype.goDown = function (evnt) {
try {
    this.selectRowAt(this.selectedRow + 1);
    this.fireSelectionChanged(this.selectedRow, this.selectedColumn);
}
catch (e) {
    AppUtil.printExceptionLog(e);
}
}

/*
 * 左矢印キーが押下された。
 */
TableView.prototype.goLeft = function (evnt) {

    this.selectColumnAt(this.selectedColumn - 1);
    this.fireSelectionChanged(this.selectedRow, this.selectedColumn);
}

/*
 * 右矢印キーが押下された。
 */
TableView.prototype.goRight = function (evnt) {

    this.selectColumnAt(this.selectedColumn + 1);
    this.fireSelectionChanged(this.selectedRow, this.selectedColumn);
}

/*
 * Enterキーが押下された。
 */
TableView.prototype.enterkeyDown = function (evnt) {

    /*
     * モデルが設定されていない場合、なにもしない。
     */
    if (! this.model) {

        return;
    }

    /*
     * 何らかの修飾キーが押下されていた場合は、
     * 全てアプリケーションに渡す。
     */
    if (evnt.ctrlKey || evnt.shiftKey || evnt.altKey || evnt.metaKey) {

        this.enter(evnt);
        return;
    }

    /*
     * 当該セルが編集を必要としない場合、
     * アプリケーションに渡す。
     */
    if (! this.model.isCellEditable(this.selectedRow, this.selectedColumn)) {

        this.enter(evnt);
        return;
    }

    /*
     * 表示セルを非表示とし、入力セルを表示する。
     */
    var cellView = this.getCurrentCellValueContainerView();
    var cellEditorView = this.getCurrentCellEditorView();
    var cellEditor = this.getCurrentCellEditor();
    cellEditor.defaultValue = cellEditor.value;
    cellView.style.display = "none";
    cellEditorView.style.display = "";
    this.isEditable = true;

    /*
     * keydownイベント中にフォーカスを移してしまうと、
     * フォーカス先でkeyupイベントが発生し、
     * テキストエリアに改行が追加されてしまう。
     * これを回避するために、一旦Webブラウザに処理を移行した後
     * (こういう表現で正しいかは不明だけど)、フォーカスを移動させる。
     */
//    cellEditor.focus();
    this.invokeFocusToCellEditor();
//    setTimeout("TableView.focusToCellEditor("+this.id+")", 0);

    return false;
}

/*
 * モデルをセットする。
 * モデルは、下記のメソッドを、必ず実装する必要がある。
 * addTableModelListener(view)
 * getColumnTitleAt(columnIndex)
 * getValueAt(rowIndex, columnIndex)
 * getEditableValueAt(rowIndex, columnIndex)
 * isCellEditable(rowIndex, columnIndex)
 */
TableView.prototype.setModel = function (model) {

    /*
     * 実装必須のメソッドが、実際に実装されているかのチェック。
     * 契約型のデザインパターンを、言語レベルでサポートしていないので、
     * アプリケーションレベルでチェックする必要がある。
     */
    if (! model.addTableModelListener) {

        throw WebApplyUtil.createException(new Error(),
                                           "addTableModelListener");
    }

    if (! model.isCellEditable) {

        throw WebApplyUtil.createException(new Error(),
                                           "isCellEditable");
    }

    if (! model.getValueAt) {

        throw WebApplyUtil.createException(new Error(),
                                           "getValueAt");
    }

    if (! model.getEditableValueAt) {

        throw WebApplyUtil.createException(new Error(),
                                           "getEditableValueAt");
    }

    this.model = model;
    this.model.addTableModelListener(this);
    if (this.selectedRow == -1) {

        this.selectedRow = 0;
    }
    this.selectRow();
    this.fireSelectionChanged(this.selectedRow, this.selectedColumn);
}

/**
 * モデルを返す。
 * @return テーブルモデル。
 *         セットされていない場合は、null。
 */
TableView.prototype.getModel = function () {

    return this.model;
}

/*
 * 指定行を選択する。
 */
TableView.prototype.selectRowAt = function (rowIndex) {

    var tbody = this.tableElement.tBodies.item(0);
    var rowCount = tbody.rows.length;

    if (0 > rowIndex || rowCount <= rowIndex) {

        return -1;
    }

    this.nonSelectRow();
    this.selectedRow = rowIndex;
    this.selectRow();

    return this.selectRow;
}

/*
 * 現在行を選択状態にする。
 */
TableView.prototype.selectRow = function () {

    var tbody = this.tableElement.tBodies.item(0);
    var element = tbody.rows.item(this.selectedRow);
    HtmlUtil.replaceClassValue(element,
                               TableView.nonSelectRowClass,
                               TableView.selectRowClass);
}

/*
 * 現在行を非選択状態にする。
 */
TableView.prototype.nonSelectRow = function () {

    var tbody = this.tableElement.tBodies.item(0);
    var element = tbody.rows.item(this.selectedRow);
    HtmlUtil.replaceClassValue(element,
                               TableView.selectRowClass,
                               TableView.nonSelectRowClass);
}

/*
 * 指定列を選択する。
 */
TableView.prototype.selectColumnAt = function (columnIndex) {

    var row = this.jsTable.getRowFromBodiesAt(this.selectedRow);

    if (! row) {

        return -1;
    }

    var columnCount = row.cells.length;

    if (0 > columnIndex || columnCount <= columnIndex) {

        return -1;
    }

    this.nonSelectColumn();
    this.selectedColumn = columnIndex;
    this.selectColumn();

    return this.selectColumn;
}

/*
 * 現在列を選択状態にする。
 */
TableView.prototype.selectColumn = function () {

    var rowCount = this.jsTable.getBodiesRowCount();

    for (var i = 0; i < rowCount; i ++) {

        var element = this.getCellAt(i, this.selectedColumn);

        if (null == element) {

            continue;
        }
        HtmlUtil.replaceClassValue(element,
                                   TableView.nonSelectColumnClass,
                                   TableView.selectColumnClass);
    }
}

/*
 * 現在列を非選択状態にする。
 */
TableView.prototype.nonSelectColumn = function () {

    var rowCount = this.jsTable.getBodiesRowCount();

    for (var i = 0; i < rowCount; i ++) {

        var element = this.getCellAt(i, this.selectedColumn);

        if (null == element) {

            continue;
        }
        HtmlUtil.replaceClassValue(element,
                                   TableView.selectColumnClass,
                                   TableView.nonSelectColumnClass);
    }
}

/*
 * テーブルの内容が変更されたことを、通知します。
 * TableModelから呼び出される。
 */
TableView.prototype.tableChanged = function (tableModelEvent) {

    if (null == this.model) {

        return;
    }

    var startRow;
    var endRow;
    var startColumn;
    var endColumn;

    /*
     * イベントが指定されていないか、スタート行がマイナス指定の場合は、
     * テーブルを全面的に書き換える。
     */
    if (! tableModelEvent || 0 > tableModelEvent.startRow) {

        this.initializeView();
        startRow = 0;
        endRow = this.jsTable.getBodiesRowCount() - 1;
        startColumn = 0;
        endColumn = 99999;
    }
    else {

        startRow    = tableModelEvent.startRow;
        endRow      = tableModelEvent.endRow;
        startColumn = tableModelEvent.startColumn;
        endColumn   = tableModelEvent.endColumn;
    }

    for (var i = startRow; i <= endRow; i ++) {

        var row = this.jsTable.getRowFromBodiesAt(i);
        var cellCount = (endColumn < row.cells.length) ?
                            endColumn : (row.cells.length - 1);
        for (var j = startColumn; j <= cellCount; j ++) {

            this.setViewValue(i, j);

            if (this.model.isCellEditable(i, j)) {

                this.setEditableValue(i, j);
                var cell = this.getCellAt(i, j);
                HtmlUtil.removeClassValue(cell, TableView.nonEditableCellClass);
                HtmlUtil.addClassValue(cell, TableView.editableCellClass);
            }
        }
    }
}

/**
 * セルの表示内容を設定する。
 * @param rowIndex : 数値 : 行インデックス
 * @param columnIndex : 数値 : 列インデックス
 */
TableView.prototype.setViewValue = function (rowIndex, columnIndex) {

    var viewValue = this.model.getValueAt(rowIndex, columnIndex);
    var valueContainer = this.getCellValueContainerAt(rowIndex, columnIndex);
    if (null == valueContainer) {
        return;
    }
    if (viewValue.replace) {
        viewValue = viewValue.replace(/\n/g, "<br />");
        viewValue = "<doc>" + viewValue + "</doc>";
        var newDoc = XmlUtil.perseFromString(viewValue);
        DomUtil.removeAllChild(valueContainer);
        DomUtil.deepClone(valueContainer, newDoc.documentElement);
    }
    else {
        DomUtil.setTextValue(valueContainer, viewValue);
    }
}

/**
 * セルの編集内容を設定する。
 * @param rowIndex : 数値 : 行インデックス
 * @param columnIndex : 数値 : 列インデックス
 */
TableView.prototype.setEditableValue = function (rowIndex, columnIndex) {

    var editableValue = this.model.getEditableValueAt(rowIndex, columnIndex);
    var cellEditor = this.getCellEditorAt(rowIndex, columnIndex);
    if (cellEditor) {

        cellEditor.value = editableValue;
    }
}

/**
 * テーブル表示の初期化を行う。
 * 全体の初期化と、テーブルボディー部以外の内容の表示などを行う。
 * Modelからも呼び出される。
 */
TableView.prototype.initializeView = function () {
}

/*
 * テーブルの選択状態が変更されたことを、通知します。
 */
TableView.prototype.addSelectionListener = function (listener) {

    var index = this.selectionListeners.length;
    this.selectionListeners[index] = listener;
//    this.fireSelectionChanged(this.selectedRow, this.selectedColumn);
}

/*
 * テーブルの選択状態が変更されたことを、通知します。
 */
TableView.prototype.fireSelectionChanged = function (rowIndex, columnIndex) {

    this.selectionChangedTimer.stop();
    this.selectionChangedTimer.start();

//    for (var i = 0; i < this.selectionListeners.length; i ++) {

//        this.selectionListeners[i].selectionChanged(rowIndex, columnIndex, this);
//    }
}

TableView.prototype.action = function () {

    for (var i = 0; i < this.selectionListeners.length; i ++) {

        this.selectionListeners[i].selectionChanged(this.selectedRow,
                this.selectedColumn, this);
    }
}

/**
 * 選択セルの表示要素を返す。
 * @return 要素 : 選択のセルの表示要素。
 *         該当するセルが存在しない場合、null。
 *         デフォルトの表示要素が存在しない場合、セルを返す。
 */
TableView.prototype.getCurrentCellValueContainer = function () {

    return this.getCellValueContainerAt(this.selectedRow,
                                        this.selectedColumn);
}

/**
 * 選択セルの表示要素の表示状態を決定する要素を返す。
 * @return 要素 : 選択セルの表示要素の表示状態を決定する要素。
 *         該当するセルが存在しない場合、null。
 *         デフォルトの表示要素が存在しない場合、セルを返す。
 */
TableView.prototype.getCurrentCellValueContainerView = function () {

    return this.getCellValueContainerViewAt(this.selectedRow,
                                            this.selectedColumn);
}

/**
 * 選択セルの入力要素を返す。
 * @return 要素 : 選択のセルの入力要素。
 *         該当するセルや入力要素が存在しない場合、null。
 */
TableView.prototype.getCurrentCellEditor = function () {

    return this.getCellEditorAt(this.selectedRow, this.selectedColumn);
}

/**
 * 選択セルの入力要素の表示状態を決定する要素を返す。
 * @return 要素 : 選択のセルの入力要素の表示状態を決定する要素。
 *         該当するセルや入力要素が存在しない場合、null。
 */
TableView.prototype.getCurrentCellEditorView = function () {

    return this.getCellEditorViewAt(this.selectedRow, this.selectedColumn);
}

/**
 * 指定セルの表示要素を返す。
 * アプリケーション固有の、セルビュー取得メソッドが定義されている場合は、
 * そちらを有効とする。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 要素 : 指定のセルの表示要素。
 *         該当するセルが存在しない場合、null。
 *         デフォルトの表示要素が存在しない場合、セルを返す。
 */
TableView.prototype.getCellValueContainerAt = function (rowIndex, columnIndex) {

    var cellValueContainer = null;

    /*
     * アプリケーション固有のセルビュー取得メソッドが定義されていた場合は、
     * 最初にそちらを呼び出す。
     */
    if (this.getAppCellValueContainerAt) {

        cellValueContainer =
            this.getAppCellValueContainerAt(rowIndex, columnIndex);

        if (cellValueContainer) {

            return cellValueContainer;
        }
    }

    var cell = this.getCellAt(rowIndex, columnIndex);
    if (! cell) {

        return null;
    }

    cellValueContainer =
        DomUtil.getElementByAttributeValue(cell,
                                           "className",
                                           TableView.cellValueContainerClass);

    if (! cellValueContainer) {

        return cell;
    }

    return cellValueContainer;
}

/**
 * 指定セルの表示要素の表示状態を決定する要素を返す。
 * この実装では、getCellValueContainerAtと同じ結果を返す。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 要素 : 指定のセルの表示要素の表示状態を決定する要素。
 *         該当するセルが存在しない場合、null。
 *         デフォルトの表示要素が存在しない場合、セルを返す。
 */
TableView.prototype.getCellValueContainerViewAt = function (rowIndex, columnIndex) {

    return this.getCellValueContainerAt(rowIndex, columnIndex);
}

/**
 * 指定セルの入力要素を返す。
 * アプリケーション固有の、セルエディター取得メソッドが定義されている場合は、
 * そちらを有効とする。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 要素 : 指定のセルの入力要素。
 *         該当するセルや入力要素が存在しない場合、null。
 */
TableView.prototype.getCellEditorAt = function (rowIndex, columnIndex) {

    var cellEditor = null;

    /*
     * アプリケーション固有の、セルエディター取得メソッドが
     * 定義されている場合は、最初にそちらを呼び出す。
     */
    if (this.getAppCellEditorAt) {

        cellEditor = this.getAppCellEditorAt(rowIndex, columnIndex);
        if (cellEditor) {

            return cellEditor;
        }
    }

    var cell = this.getCellAt(rowIndex, columnIndex);
    if (! cell) {

        return null;
    }

    cellEditor = DomUtil.getElementByAttributeValue(cell,
                                                    "className",
                                                    TableView.cellEditorClass);

    if (! cellEditor) {

        return null;
    }

    return cellEditor;
}

/**
 * 指定セルの入力要素の表示状態を決定する要素を返す。
 * この実装では、getCellEditorAtと同じ結果を返す。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 要素 : 指定のセルの入力要素の表示状態を決定する要素。
 *         該当するセルや入力要素が存在しない場合、null。
 */
TableView.prototype.getCellEditorViewAt = function (rowIndex, columnIndex) {

    return this.getCellEditorAt(rowIndex, columnIndex);
}

/**
 * 現在選択中のセルを返す。
 * @return 要素 : 指定のセル。該当するセルが存在しない場合、null。
 */
TableView.prototype.getSelectedCell = function () {

    return this.getCellAt(this.selectedRow, this.selectedColumn);
}

/**
 * 指定のセルを返す。
 * @param rowIndex : 数値 : 行インデックス。
 * @param columnIndex : 数値 : 列インデックス。
 * @return 要素 : 指定のセル。該当するセルが存在しない場合、null。
 */
TableView.prototype.getCellAt = function (rowIndex, columnIndex) {

    return this.jsTable.getCellFromBodiesAt(rowIndex, columnIndex);
}

/**
 * セルエディタを表示中の場合、そのセルエディタにフォーカスを当てます。
 * セルエディタが表示中でない場合、falseを返し処理はなにもしません。
 * @return : boolean : セルエディタにフォーカスを当てた場合true、
 *                     セルエディタが表示中でない場合falseを返す。
 */
TableView.prototype.invokeFocusToCellEditor = function () {

    if (! this.isEditable) {

        return false;
    }

    setTimeout("TableView.focusToCellEditor("+this.id+")", 0);
    return true;
}

/**
 * コントロールにフォーカスを当てます。
 */
TableView.prototype.invokeFocusToController = function () {

    setTimeout("TableView.focusToController("+this.id+")", 0);
}


/**
 * テーブルモデルから、テーブルビューへ、
 * モデルの内容が変更されたことを通知するイベント。
 * コンストラクタ。
 * @param model : テーブルモデル
 * @param startRow : 数値 : 変更された最初の行(この行も含む)。
 *        この値がマイナスの場合、モデル全体が変更されたとして、
 *        テーブルの内容を、全て書き換える。
 * @param endRow : 数値 : 変更された最期の行(この行も含む)。
 * @param startColumn : 数値 : 変更された最初の列(この列も含む)。
 * @param endColumn : 数値 : 変更された最期の列(この列も含む)。
 */
function TableModelEvent(model, startRow, endRow, startColumn, endColumn, type) {

    this.model       = model;
    this.startRow    = startRow;
    this.endRow      = endRow;
    this.startColumn = startColumn;
    this.endColumn   = endColumn;
    this.type        = type;
}

TableModelEvent.CELL_UPDATE = 1;
TableModelEvent.ROW_INSERT  = 2;
TableModelEvent.ROW_DELETE  = 3;
