<< 前 ホーム 次 >>

bakaid: 20100316

javax.swing.undo.UndoManagerがよくわかんねーなーと
思っていろいろやってたんですけど。ようやくなんとなく
わかってきました。

UndoManagerの何がよくわかんないかといえば、連続した
Editの束ね方。たとえば、UndoManagerとJTextComponentを
組み合わせて使うとき、何の工夫もしなければ、1文字
ずつundoされちゃいます。

123

とタイプしたとして、1回undoすると:

12

となって、もう1回undoすると:

1

になる。このときredoすると:

12

になる。

でも、フツーは:

123

とタイプしたときに、1回のundoですべての文字が消えて
ほしい、さらにredoしたらまた123が復活してほしいもの。

で、こういうときはCompoundEditを使うんだっていうのは
よく知られてるんですけど。でも、その使い方がよく
わかんねーなーっていう。javadoc見ても大したこと
書かれてないし、巷に流通してるコード見ても、なんか
違うなーと。

で、そもそもUndoManagerの動きがよくわかってねんじゃね?
ってことになって。わざわざNetBeansもらってきて、
デバッガでUndoManagerの動きを追って。それでようやく
なんとなくはわかってきて。

最初のうちは、CompoundEdit使うにはUndoManagerの
サブクラス作るしかないのかと思ったんですよね。巷の
コード見るとそんな感じのが多くって。でも、そりゃ
おかしいだろと、思ったわけですよ。オレは継承嫌いだし。

そもそも、UndoManagerがCompoundEditのサブクラスに
なってるのは設計間違ってるでしょ? is_a?が崩れてん
じゃん? イマドキ、Javaのライブラリでこんなアンチ
パターン見るとは思わなかったわ。

閑話休題。で、やっぱりCompoundEdit使うのに継承は
いらなそうだとわかってきて。書いたコードが下のヤツ。
ほんとにサンプル目的なんで実用的じゃないんですけど。
Editを2個ずつ束ねてるっていう、ただそれだけ。

また脱線すると、JTextComponentで起こるUndoableEditEvent
から取れるEditって、AbstractDocument.DefaultDocumentEvent
ってヤツなんですよ。これがもうバリバリの内部クラスで。
外からだとサブクラス作れない。名前もおかしいでしょ?
実際のコードだと:

public void undoableEditHappened(UndoableEditEvent event) {
    AbstractDocument.DefaultDocumentEvent edit =
        (AbstractDocument.DefaultDocumentEvent)event.getEdit();
    ...

ってなるんだぜ? わけわからん。歴史的な理由でもある
んですかね。

include Java

java_import java.awt.BorderLayout
java_import javax.swing.JButton
java_import javax.swing.JFrame
java_import javax.swing.JPanel

class UndoManagerTest
  include java.lang.Runnable
  include javax.swing.event.UndoableEditListener

  def initialize
    @undoManager = javax.swing.undo.UndoManager.new
  end

  def run
    createAndShowGUI
  end

  def createAndShowGUI
    frame = JFrame.new("UndoTest")
    frame.setContentPane(createContentPane)
    frame.setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE)
    frame.pack
    frame.setVisible(true)
  end

  def createContentPane
    pane = JPanel.new(BorderLayout.new)
    editor = javax.swing.JEditorPane.new
    editor.setPreferredSize(java.awt.Dimension.new(320, 240))
    editor.document.addUndoableEditListener(self)
    pane.add(editor, BorderLayout::CENTER)
    pane.add(createButtonPane, BorderLayout::SOUTH)
    return pane
  end

  def createButtonPane
    pane = JPanel.new
    undoButton = JButton.new("Undo")
    undoButton.addActionListener do |actionEvent|
      @undoManager.canUndo and @undoManager.undo
    end
    pane.add(undoButton)

    redoButton = JButton.new("Redo")
    redoButton.addActionListener do |actionEvent|
      @undoManager.canRedo and @undoManager.redo
    end
    pane.add(redoButton)
    return pane
  end

  def undoableEditHappened(event)
    seq = SequentialEdit.new
    seq.addEdit(event.edit)
    seq.evenId? and @undoManager.lastEdit and @undoManager.lastEdit.end
    @undoManager.addEdit(seq)
  end
end

class SequentialEdit < javax.swing.undo.CompoundEdit
  @@count = 0

  def initialize
    @id = @@count
    @@count += 1
  end

  def isInProgress
    return false
  end

  def evenId?
    return @id % 2 == 0
  end
end

if $0 == __FILE__
  javax.swing.SwingUtilities.invokeLater(UndoManagerTest.new)
end

本家Permlink

<< 前 ホーム 次 >>


Copyright © 1905 tko at jitu.org

バカが征く on Rails