@Override
  public void modelPropertyChange(PropertyChangeEvent evt) {
    // Object oldValue = evt.getOldValue();
    Object newValue = evt.getNewValue();
    String propName = evt.getPropertyName();

    if (BookController.StrandProps.UPDATE.check(propName)) {
      EntityUtil.refresh(mainFrame, scene.getStrand());
      setEndBgColor(scene.getStrand().getJColor());
      repaint();
      return;
    }

    if (BookController.SceneProps.UPDATE.check(propName)) {
      Scene newScene = (Scene) newValue;
      if (!newScene.getId().equals(scene.getId())) {
        // not this scene
        return;
      }
      scene = newScene;
      lbSceneNo.setText(scene.getChapterSceneNo(false));
      lbSceneNo.setToolTipText(scene.getChapterSceneToolTip());
      lbStatus.setIcon(scene.getStatusIcon());
      taTitle.setText(scene.getTitle());
      taTitle.setCaretPosition(0);
      tcText.setText(scene.getSummary());
      tcText.setCaretPosition(0);
      if (scene.hasSceneTs()) {
        if (!DateUtil.isZeroTimeDate(scene.getSceneTs())) {
          DateFormat formatter = I18N.getDateTimeFormatter();
          lbTime.setText(formatter.format(scene.getSceneTs()));
        } else {
          lbTime.setText("");
        }
      }
      return;
    }

    if (BookController.ChronoViewProps.ZOOM.check(propName)) {
      setZoomedSize((Integer) newValue);
      refresh();
    }
  }
  @Override
  public void refresh() {
    MigLayout layout = new MigLayout("fill,flowy,insets 4", "[]", "[][grow]");
    setLayout(layout);
    setPreferredSize(new Dimension(size, size));
    setComponentPopupMenu(EntityUtil.createPopupMenu(mainFrame, scene));

    removeAll();

    // set dotted border for scenes of other parts
    setBorder(SwingUtil.getBorderDefault());
    if (scene.hasChapter()) {
      if (!scene.getChapter().getPart().getId().equals(mainFrame.getCurrentPart().getId())) {
        setBorder(SwingUtil.getBorderDot());
      }
    }

    // strand links
    StrandLinksPanel strandLinksPanel = new StrandLinksPanel(mainFrame, scene, true);

    // person links
    PersonLinksPanel personLinksPanel = new PersonLinksPanel(mainFrame, scene);

    // location links
    LocationLinksPanel locationLinksPanel = new LocationLinksPanel(mainFrame, scene);

    // location links
    ItemLinksPanel itemLinksPanel = new ItemLinksPanel(mainFrame, scene);

    // button new
    btNew = getNewButton();
    btNew.setSize20x20();
    // btNew.setName(COMP_NAME_BT_NEW);

    // button remove
    btDelete = getDeleteButton();
    btDelete.setSize20x20();
    // btDelete.setName(COMP_NAME_BT_REMOVE);

    // button edit
    btEdit = getEditButton();
    btEdit.setSize20x20();
    // btEdit.setName(COMP_NAME_BT_EDIT);

    // chapter and scene number
    lbSceneNo = new JLabel("", SwingConstants.CENTER);
    lbSceneNo.setText(scene.getChapterSceneNo(false));
    lbSceneNo.setToolTipText(scene.getChapterSceneToolTip());
    lbSceneNo.setOpaque(true);
    lbSceneNo.setBackground(Color.white);

    // status
    lbStatus = new SceneStateLabel(scene.getSceneState(), true);

    // informational
    lbInformational = new JLabel("");
    if (scene.getInformative()) {
      lbInformational.setIcon(I18N.getIcon("icon.small.info"));
      lbInformational.setToolTipText(I18N.getMsg("msg.common.informative"));
    }

    // scene time
    lbTime = new JLabel();
    if (scene.hasSceneTs()) {
      if (!DateUtil.isZeroTimeDate(scene.getSceneTs())) {
        lbTime.setText(DateUtil.simpleDateTimeToString(this.scene.getSceneTs()));
      }
    }

    // title
    taTitle = new UndoableTextArea();
    taTitle.setName(CN_TITLE);
    taTitle.setText(scene.getTitle());
    taTitle.setLineWrap(true);
    taTitle.setWrapStyleWord(true);
    taTitle.setDragEnabled(true);
    taTitle.setCaretPosition(0);
    taTitle.getUndoManager().discardAllEdits();
    taTitle.addFocusListener(this);
    SwingUtil.addCtrlEnterAction(taTitle, new EditEntityAction(mainFrame, scene, true));
    JScrollPane spTitle = new JScrollPane(taTitle);
    spTitle.setPreferredSize(new Dimension(50, 35));

    // text
    tcText = SwingUtil.createTextComponent(mainFrame);
    tcText.setName(CN_TEXT);
    tcText.setText(scene.getText());
    tcText.setDragEnabled(true);
    tcText.addFocusListener(this);
    SwingUtil.addCtrlEnterAction(tcText, new EditEntityAction(mainFrame, scene, true));
    JScrollPane spText = new JScrollPane(tcText);
    spText.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    spText.setPreferredSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));

    // layout

    // button panel
    JPanel buttonPanel = new JPanel(new MigLayout("flowy,insets 0"));
    buttonPanel.setName("buttonpanel");
    buttonPanel.setOpaque(false);
    buttonPanel.add(btEdit);
    buttonPanel.add(btDelete);
    buttonPanel.add(btNew);

    upperPanel = new JPanel(new MigLayout("ins 0", "[][grow][]", "[top][top][top]"));
    upperPanel.setName(CN_UPPER_PANEL);
    upperPanel.setOpaque(false);
    upperPanel.add(lbSceneNo, "grow,width pref+10px,split 3");
    upperPanel.add(lbStatus);
    upperPanel.add(lbInformational);
    upperPanel.add(strandLinksPanel, "grow");
    upperPanel.add(buttonPanel, "spany 4,wrap");
    JScrollPane scroller =
        new JScrollPane(
            personLinksPanel,
            JScrollPane.VERTICAL_SCROLLBAR_NEVER,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    scroller.setMinimumSize(new Dimension(20, 16));
    scroller.setOpaque(false);
    scroller.getViewport().setOpaque(false);
    scroller.setBorder(null);
    upperPanel.add(scroller, "spanx 2,growx,wrap");
    upperPanel.add(locationLinksPanel, "spanx 2,grow,wrap");
    upperPanel.add(lbTime);

    // main panel
    add(upperPanel, "growx");
    add(spTitle, "growx, h 35!");
    add(spText, "grow");

    revalidate();
    repaint();

    tcText.setCaretPosition(0);
    taTitle.setCaretPosition(0);
  }
  @Override
  public void refresh() {
    MigLayout layout = new MigLayout("fill,flowy,insets 4", "[]", "[][grow]");
    setLayout(layout);
    setPreferredSize(new Dimension(size, size));
    setComponentPopupMenu(EntityUtil.createPopupMenu(mainFrame, scene));

    removeAll();

    // set dotted border for scenes of other parts
    setBorder(SwingUtil.getBorderDefault());
    if (scene.hasChapter()) {
      if (!scene.getChapter().getPart().getId().equals(mainFrame.getCurrentPart().getId())) {
        setBorder(SwingUtil.getBorderDot());
      }
    }

    // button new
    btNew = getNewButton();
    btNew.setSize20x20();
    // btNew.setName(COMP_NAME_BT_NEW);

    // button remove
    btDelete = getDeleteButton();
    btDelete.setSize20x20();
    // btDelete.setName(COMP_NAME_BT_REMOVE);

    // button edit
    btEdit = getEditButton();
    btEdit.setSize20x20();
    // btEdit.setName(COMP_NAME_BT_EDIT);

    // chapter and scene number
    lbSceneNo = new JLabel("", SwingConstants.CENTER);
    lbSceneNo.setText(scene.getChapterSceneNo(false));
    lbSceneNo.setToolTipText(scene.getChapterSceneToolTip());
    lbSceneNo.setOpaque(true);
    lbSceneNo.setBackground(Color.white);

    // status
    lbStatus = new SceneStateLabel(scene.getSceneState(), true);

    // informational
    lbInformational = new JLabel("");
    if (scene.getInformative()) {
      lbInformational.setIcon(I18N.getIcon("icon.small.info"));
      lbInformational.setToolTipText(I18N.getMsg("msg.common.informative"));
    }

    // scene time
    lbTime = addSceneTimeTextToPanel();

    // title
    JScrollPane spTitle = generateTitlePane();
    spTitle.setPreferredSize(new Dimension(50, 35));

    // text
    JScrollPane spText = generateTextPane();
    spText.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    spText.setPreferredSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));

    // layout
    // button panel
    upperPanel = setupUpperPanel();

    // main panel
    add(upperPanel, "growx");
    add(spTitle, "growx, h 35!");
    add(spText, "grow");

    revalidate();
    repaint();

    tcText.setCaretPosition(0);
    taTitle.setCaretPosition(0);
  }