Eclipse

[번역완료] Adding Drag and Drop to an SWT Application

_침묵_ 2007. 5. 1. 22:20

원문 :http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html

 

 

Copyright © 2003 International Business Machines Corp.
 Eclipse Corner Article

사용자 삽입 이미지

드래그 & 드롭

SWT 어플리케이션에 드래그 & 드롭 추가하기

요약
드래그 & 드롭은 사용자가 화면을 재배치하거나 어플리케이션 내에서의 데이터 전달 및 어플리케이션 간의 데이터 전달을 빠르고 쉽게할 수 있도록 한다. 이 문서는 SWT 어플리케이션에서 드래그 & 드롭을 어떻게 구현하는지와 클립보드에 데이터를 전달하는 방법을 설명한다.

 

By Veronika Irvine, IBM OTI Labs
August 25, 2003 (revised November 2, 2005)

 

번역: 이상훈(calm1979@gmail.com)

2007년 5월 1일

 


 

드래그 & 드롭 개요

그림 1에 보여지는 것 처럼 하나의 테이블에서 다른 테이블로 항목을 드래깅(dragging) 하는 간단한 예제를 생각해보자:

사용자 삽입 이미지

그림 1: 쇼핑 카트 예

 

오른쪽에는 내가 구입할 수 있는 항목들의 리스트가 있고 왼쪽에는 나의 쇼핑 카트 내용이 있다. 무언가를 사기위해, 오른쪽에 있는 리스트에서 선택을 하고(그림 1에서는 "Truggle Assortment"를 선택했다.), 그 항목을 왼쪽의 쇼핑 카트로 드래그 & 드롭을 한다. 마우스를 쇼핑 카트 위로 옮기면, 그 항목이 내가 구입하고 싶은 목록에 추가될 것이다. 지금의 데이터 전달을 보면, 오른쪽의 분류된 초콜렛 목록이 드래그 소스(drag source)가 되고 내 쇼핑 카트가 드롭 타겟(drop target)이 된다. 드래그 소스는 전달하고자 하는 데이터의 원본(source)이고, 드롭 타겟은 수신자(receiver)이다. 드롭 타겟 위로 마우스를 드래그 하면 여러 형태로의 피드백(feedback)을 받는다. 첫째로, 유효한 드롭 타겟임을 알 수 있도록 "do not enter" 표시에서 화살표("drag over effect"라고 부른다.)로 커서 모양이 바뀐다. 또한 커서 모양을 보고, 전달되는 데이터가 어떤 형태의 작업 - 데이터가 복사, 이동, 바로가기(link) 되는 것 - 을 할 것인지 알 수 있다. 두번째로, 트리나 테이블 처럼 서브 아이템(sub-items)을 가지고 있는 위젯(widget) 위로 드래깅을 하면, 절달되는 데이터가 특정 서브 아이템으로 드롭될 것을 알리기 위해 서브 아이템이 하이라이팅될 것("drag under effect"라고 부른다.)이다. 이것은 폴더로 데이터를 전달하려고 할 때나 항목들을 재배치하려고 할 때 유용하다. 이 문서에서는, 위에서 언급한 용어들을 좀 더 자세하게 설명하고 SWT에서 이런 요소들을 정의하고 사용하는 방법을 설명할 것이다.

드래그 소스(Drag Source)

드래그 소스는 드래그 & 드롭 데이터 전달에서 데이터의 제공자이며 또한 드래그 & 드롭 동작의 시작점(originator)이기도 하다. 드래그 소스에 의해 제공되는 데이터는 같은 위젯상의 다른 위치로 전달될 수도 있고, 같은 어플리케이션의 다른 위젯으로나 완전히 다른 어플리케이션 사이에서도 전달될 수 있다. 예를 들어, 당신은 어플리케이션에서 텍스트를 드래그해서 이메일 어플리케이션에 드롭할 수 있다. 또한 트리에 있는 항목을 드래그 해서 같은 트리의 다른 노드상에 드롭할 수도 있다.

 

드래그 소스를 정의하는 방법을 보여주는 간단한 예제를 보자. 목록 1의 예제에서는 라벨(label) 위젯으로 부터 텍스트를 드래그 하는 방법을 보여준다.

 

1import org.eclipse.swt.dnd.*;
2 
3// 라벨(label)이 드래그 소스 역할을 하도록 한다.
4final Label dragLabel = new Label(shell, SWT.BORDER);
5dragLabel.setText("text to be transferred");
6 
7// 드래그 소스로부터 데이터를 복사하거나 이동할 수 있도록 한다.
8int operations = DND.DROP_MOVE | DND.DROP_COPY;
9DragSource source = new DragSource(dragLabel, operations);
10 
11// 텍스트 포맷으로 데이터를 제공한다.
12Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
13source.setTransfer(types);
14 
15source.addDragListener(new DragSourceListener() {
16   public void dragStart(DragSourceEvent event) {
17      // 라벨(label)에 실제로 텍스트가 있을 때만 드래그를 시작한다.
18      // 이 텍스트가 타겟에 드롭될 것이다.
19      if (dragLabel.getText().length() == 0) {
20          event.doit = false;
21      }
22   }
23   public void dragSetData(DragSourceEvent event) {
24     // 요청된 타입의 데이터를 제공한다.
25     if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
26          event.data = dragLabel.getText();
27     }
28   }
29   public void dragFinished(DragSourceEvent event) {
30     // 이동 동작이 수행된 것이라면,
31     // 소스의 데이터를 지워준다.
32     if (event.detail == DND.DROP_MOVE)
33         dragLabel.setText("");
34     }
35   }
36});

목록 1: 라벨(Label) 위젯으로 부터 텍스트 드래깅하기.

 

Line 1:
모든 SWT 드래그 & 드롭 클래스들은 org.eclipse.swt.dnd 패키지 안에 정의되어 있다.

 

Lines 3 to 5:
위젯을 만든다. 드래그를 시작하기 위해, 사용자는 위젯에서 마우스를 누른 상태로 드래그한다. 예제에서는, 사용자가 라벨(label)에서 드래그를 시작한다. 하나의 위젯에서는 여러개의 드래그 소스를 둘 수 없음을 알아야한다. 만약 또 다른 드래그 소스를 만들려고 하면, SWTError 가 발생할 것이다.

 

Lines 7 to 9:
드래그 소스에 위젯을 붙이기 위해 org.eclipse.swt.dnd.DragSource 객체를 만들어야한다. DragSource 생성자는 두 개의 인자를 가지는데, 드래그 소스에 붙일 위젯과 허용할 동작을 지정한다. 허용할 동작은 전송될 데이터가 드롭 타겟에서 취할 수 있는 형태를 말한다. 이 값은 DND.DROP_COPY, DND.DROP_MOVE 그리고 DND.DROP_LINK 의 OR 연산 조합으로 표현 가능하다. 예제에서는 복사와 이동을 가능하게 하였다.

 

Lines 11 to 13:
드래그 소스의 정의를 마무리하기 위해, 전송될 수 있는 데이터 타입을 지정해야한다. 데이터 타입은 TextTranser 나 FileTransfer 같은 org.eclipse.swt.dnd.Transfer 의 서브 클래스로 정의한다. 전송 타입의 자세한 설명을 보려면전송(Transfer) 를 보면 된다. 드래그 소스는 하나의 이상의 데이터 형태를 제공할 수 있으나, 요청하는 형태에 맞게 일일이 데이터를 제공해주어야 한다. 예제에서는 라벨(label)에서 텍스트를 드래그 할 수 있게 하였다.

 

Line 15:
드래그 소스가 정의되고 나면, 드래그 & 드롭 동작이 서로 상호작용을 할 수 있는 메커니즘이 필요하다. 이 역할을 해주는 DragSourceListener 를 추가한다.

 

아래와 같은 순서로 이벤트가 발생한다:

  1. dragStart
  2. dragStart, one or more dragSetData, dragFinished
  3. dragStart, dragFinished

 

Lines 16 to 22:
dragStart 이벤트는 드래그 & 드롭의 시작을 알리는 동작이 취해졌을 때 발생한다. 사용자가 취하는 동작은 플랫폼마다 다르다. - 윈도우에서는 사용자가 마우스 왼쪽 버튼을 누르고 일정 픽셀(pixels, 제어판의 마우스 설정에서 이 값을 설정할 수 있다.) 이상 움직이게 되는 경우에 발생한다. Motif 에서는 사용자가 마우스 중간 버튼을 눌렀을 경우에 발생한다. 이런 플랫폼별의 차이는 SWT에서 처리해주기 때문에 어플리케이션에서 이런 조건들에 대한 고려를 할 필요는 없다. 단지 dragStart 이벤트를 받았을 경우에, 실제로 드래그 & 드롭 동작을 시작할 것인지 아닌지를 정하면 된다. 예를 들어, 드래그 소스 위젯에서 선택된 항목이 유효하지 않은 경우에는 어플리케이션에서 동작을 취소해야한다. 이렇게 동작을 취소하는 것은 event.doit 필드를 false로 설정하면 된다.

 

알림: 어플리케이션은 dragStart 이벤트를 받기 전까지 전송될 데이터의 유형을 알 필요가 없다. 즉, 어플리케이션에서 dragStart 이벤트가 발생하기 전까지는 DragSource.setTransfer 를 호출하지 않아도 된다. 하지만 dragStart 이벤트를 처리하는 과정에서 드래그 소스에 대한 전송 유형이 정의되어 있지 않으면, 드래그 & 드롭 동작은 취소될 것이다.

 

드래그 & 드롭 동작을 처리하기 위해 어플리케이션은 특정 요청에 대한 정의된 데이터를 제공할 것을 약속해두어야 한다. 그렇지 않을 경우에는 DND 작업에 오류가 발생하고 원치 않는 결과를 발생시킬 수 있다.

 

Lines 23 to 28:
dragSetData 이벤트는 dragStart 이벤트에서 약속했던 데이터를 요청한다. 이 이벤트는 동일한 데이터 혹은 약속된 데이터 유형에 대해 반복적으로 호출되기도 한다. 몇몇 플랫폼에서는, 마우스를 드롭 타켓 위로 이동할 때 내부적으로 드롭 타겟이 데이터를 요청할 수 있다.(예. 윈도우) 그리고 몇몇 플랫폼에서는 드롭이 되었을 때(마우스가 유효한 드롭 범위에서 놓여졌을 때)에만 데이터를 요청한다.(예. Motif) 따라서, 이 이벤트가 호출되었다고 해서 드래그 & 드롭 동작이 완료되었다고 보면 안된다. 데이터가 드롭되었음을 알 수 있는 방법이 없다 - 동일한 어플리케이션 상에서 드롭될 수도 있고, 같은 위젯에서 혹은 다른 어플리케이션에서 드롭될 수도 있다.

 

데이터 유형은 event.dataType 필드에 의해서 요청된다. event.dataType 필드는 TransferData 객체를 가지고 있다. TransferData 객체의 공개(public) 필드는 플랫폼에 종속적이다. 따라서, 이 필드에 대해 직접적으로 접근하면 안되고, Transfer 객체에 TransferData 를 넘겨주어 데이터 유형을 판단해야 한다. 이 단계를 예제에서는 TextTransfer.getInstance().isSupportedType 를 호출해서 처리한다. (25번째 줄)

 

드래그 소스는 event.data 필드를 채워주어야 한다. 데이터의 정확한 형태는 Transfer 유형에 따른다. 예를들어, TextTransfer 는 전체 텍스트를 포함하는 String 객체를 전달하고, FileTransfer 는 파일의 절대 경로를 포함하는 String 배열을 전달한다. 각각의 전송 유형을 위한 javadoc 에는 전송될 자바 객체에 대한 값을 기술해둔다. Transfer 와 TransferData 에 대한 추가적인 정보는 이 문서의전송(Transfer) 섹션에서 볼 수 있다.

 

Lines 29 to 34:
dragFinished 이벤트는 드래그 & 드롭 동작이 완료되었음을 알려준다. 사용자가 유효한 위치에 데이터를 드롭했거나, 유효하지 않은 위치에 데이터를 드롭했거나, ESC(Escape) 키를 눌렀을 때 발생한다. 만약 사용자가 유효하지 않은 위치에 데이터를 드롭하거나 ESC 키를 눌렀을 경우라면, event.doit 필드 값은 false 가 될 것이고 event.detail 필드는 DND.DROP_NONE 가 될 것이다. 만약 사용가가 유효한 위치에 데이터를 드롭했다면, event.doit 필드는 true 가 되고 event.detail 필드는 드롭 타겟에서 수행될 동작의 유형이 할당될 것이다. 이 값은 표 1에 표시된 값들중의 하나일 것이다.

 

dragFinished event.detail 값설명
DND.DROP_COPY드롭 타겟에 데이터를 복사한다.
DND.DROP_LINK드롭 타겟에 데이터 링크를 만든다. - 대게 파일에서 바로가기 할 때 사용한다.
DND.DROP_MOVE드롭 타겟에 데이터를 복사하고, 드래그 소스의 원본은 삭제한 후 다시 표시한다.
DND.DROP_TARGET_MOVE드롭 타겟이 원래 위치의 데이터를 새로운 위치로 이동시킨다. 대게 파일에서 사용하며, 이 경우에, 드래그 소스는 원본을 삭제할 필요는 없다; 다만 원본의 정보를 갱신해서 표시해준다.

표 1: 유효한 dragFinished event.detail 값

 

드롭 타겟(Drop Target)

드롭 타겟은 드래그 & 드롭 동작에서 데이터를 받는 쪽이다. 드롭 타겟에 수신되는 데이터는 같은 위젯에서 오는 것일 수도, 같은 어플리케이션의 다른 위젯에서 오는 것일 수도 있고 완전히 다른 어플리케이션에서 오는 것일 수도 있다. 예를 들어, 당신은 이메일 어플리케이션에서 당신의 어플리케이션으로 텍스트를 드래그 & 드롭 할 수도 있고, 같은 트리 상에 있는 다른 노드에서 임의의 노드로 항목을 드래그 & 드롭 할 수도 있다.

 

드롭 타겟을 정의하는 간단한 예제를 보자. 목록 2에서는 테이블 위젯에 파일이나 텍스트를 드롭하는 방법을 보여준다.

 

1import org.eclipse.swt.dnd.*;
2 
3// 테이블(table)을 드롭 타겟으로 사용한다.
4final Table dropTable = new Table(shell, SWT.BORDER);
5for (int i = 0; i < 10; i++) {
6    TableItem item = new TableItem(dropTable, SWT.NONE);
7    item.setText("item" + I);
8}
9 
10// 드롭 타겟에 데이터를 복사하거나 이동할 수 있도록 한다.
11operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
12DropTarget target = new DropTarget(dropTable, operations);
13 
14// 텍스트나 파일 형태의 데이터를 수락한다.
15final TextTransfer textTransfer = TextTransfer.getInstance();
16final FileTransfer fileTransfer = FileTransfer.getInstance();
17types = new Transfer[] {fileTransfer, textTransfer};
18target.setTransfer(types);
19 
20target.addDropListener(new DropTargetListener() {
21  public void dragEnter(DropTargetEvent event) {
22     if (event.detail == DND.DROP_DEFAULT) {
23         if ((event.operations & DND.DROP_COPY) != 0) {
24             event.detail = DND.DROP_COPY;
25         } else {
26             event.detail = DND.DROP_NONE;
27         }
28     }
29     // 텍스트나 파일을 수락한다.
30     for (int i = 0; i < event.dataTypes.length; i++) {
31         if (fileTransfer.isSupportedType(event.dataTypes[i])){
32             event.currentDataType = event.dataTypes[i];
33             // 파일은 복사만 한다.
34             if (event.detail != DND.DROP_COPY) {
35                 event.detail = DND.DROP_NONE;
36             }
37             break;
38         }
39     }
40   }
41   public void dragOver(DropTargetEvent event) {
42        event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
43        if (textTransfer.isSupportedType(event.currentDataType)) {
44            // 알림: 지원하지 않는 플랫폼에서는 null이 리턴된다.
45            Object o = textTransfer.nativeToJava(event.currentDataType);
46            String t = (String)o;
47            if (t != null) System.out.println(t);
48        }
50    }
51    public void dragOperationChanged(DropTargetEvent event) {
52        if (event.detail == DND.DROP_DEFAULT) {
53            if ((event.operations & DND.DROP_COPY) != 0) {
54                event.detail = DND.DROP_COPY;
55            } else {
56                event.detail = DND.DROP_NONE;
57            }
58        }
59        // 텍스트는 이동하고, 파일은 복사한다.
60        if (fileTransfer.isSupportedType(event.currentDataType)){
61            if (event.detail != DND.DROP_COPY) {
62                event.detail = DND.DROP_NONE;
63            }
64        }
65    }
66    public void dragLeave(DropTargetEvent event) {
67    }
68    public void dropAccept(DropTargetEvent event) {
69    }
70    public void drop(DropTargetEvent event) {
71        if (textTransfer.isSupportedType(event.currentDataType)) {
72            String text = (String)event.data;
73            TableItem item = new TableItem(dropTable, SWT.NONE);
74            item.setText(text);
75        }
76        if (fileTransfer.isSupportedType(event.currentDataType)){
77            String[] files = (String[])event.data;
78            for (int i = 0; i < files.length; i++) {
79                TableItem item = new TableItem(dropTable, SWT.NONE);
80                item.setText(files[i]);
81            }
82        }
83    }
84});

목록 2: 테이블 위젯에 텍스트나 파일 드롭하기

 

Line 1:
드래그 & 드롭과 관련된 클래스들은 org.eclipse.swt.dnd 패키지에 정의되어 있다.

 

Lines 3 to 8:
위젯을 만들자. 사용자는 데이터를 드래그해서 UI 상에 보여지는 위젯 즉, 타겟에 끌어다 놓을 것이다. 예제에서는, 사용자가 테이블에 데이터를 드롭할 수 있다. 하나의 위젯에 여러개의 드롭 타겟을 둘 수 없음을 알아야 한다. 만약 두번째 드롭 타겟을 지정하려고 하면, SWTError 가 발생할 것이다.

 

Lines 10 to 12:
드롭 타겟에 드롭된 데이터에 대해 수행할 수 있는 동작 유형을 정의한다. 이 유형은 DND.DROP_COPY, DND.DROP_MOVE, 혹은 DND.DROP_LINK 속성의 OR 연산 조합으로 정의할 수 있다. 기본적으로, 동작 유형이 정의되지 않으면 DND.DROP_MOVE 동작을 수행한다.

 

정의하는 동작 유형은  DND.DROP_DEFAULT 를 포함할 수 있다. (알림: DND.DROP_DEFAULT 스타일은 다른 DND.DROP_* 스타일이 하나 이상은 있어야 정의할 수 있다.) DND.DROP_DEFAULT 스타일은 드롭 타겟이 기본 동작 - 기본 동작은 어떠한 변경자 키(modifier keys)도 누르지 않았을 때 행해지는 것을 말한다. - 을 정의하도록 해준다. 만약 DND.DROP_DEFAULT 스타일을 정의하지 않으면, 기본 동작은 DND.DROP_MOVE 가 될 것이다. 아래에 있는 dragEnter 와 dragOperaionChanged 를 함께 보라.

 

Lines 14 to 18:
드롭 타겟이 수락할 데이터 유형을 정의한다. 데이터 유형은 TextTransfer 나 FileTransfer 처럼 org.eclipse.swt.dnd.Transfer 의 서브클래스로 정의된다. 전송 유형에 대한 자세한 설명은 전송(Transfer)을 참조하길 바란다. 드롭 타겟은 하나 이상의 데이터 유형을 받을 수 있다. 그러나 현재 2.1의 구현에서는 최종 드롭에 대해서 하나의 형태만 받는다. 즉, 드롭 타겟이 텍스트와 RTF 텍스트 모두 받을 수는 있지만, 드롭 이벤트에서는 텍스트와 RTF 텍스트 중의 하나만 제공된다는 것이다. 두가지 형태를 등록해두는 것은 어떤 어플리케이션에서는 텍스트만을 제공하고 또 다른 어플리케이션에서는 RTF 텍스트만을 제공하는 경우에 유용하다. 만약 두가지 모두 제공하는 어플리케이션이 있다면, 어떤 형태의 데이터를 받을지 선택할 수 있다. 아래 dragEnter 를 보라.

 

Line 20:
드롭 타겟을 정의하고 나면, 드래그 & 드롭 동작 간의 통신을 위한 메커니즘이 필요하게 된다. 이를 위해 DropTargetListener 을 추가한다.

 

아래와 같은 순서로 이벤트가 발생한다:

  • dragEnter, dragLeave
  • dragEnter, one or more dragOver *, dragLeave
  • dragEnter, one or more dragOver *, dragLeave, dropAccept
  • dragEnter, one or more dragOver *, dragLeave, dropAccept, drop

 

*dragOver 와 dragOperationChanged 가 혼합될 수도 있다.

 

Line 21 to 40:

dragEnter 이벤트는 드래그 & 드롭 동작 중에 커서가 드롭 타겟 위젯의 범위 안으로 들어갔을 때 발생한다. 커서가 위젯의 범위를 벗어났다가 다시 들어가게 되면, 다른 dragEnter 이벤트가 발생한다.

 

Lines 22 to 28:

드래그 오버 효과(drag over effect)는 드롭을 수행할 때 처리되는 동작을 사용자에게 알리기 위한 시각적인 신호이다. 시각적인 신호의 실질적인 모양은 플랫폼 종속적이다 - 표 2에 몇가지 예제가 있다. 드롭 타겟에서는 event.detail 필드를 DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK 나 DND.DROP_NONE 중의 하나로 설정함으로써 드래그 오버 효과를 갱신 해줄 수 있다. event.detail 필드에 들어갈 값은 드래그 소스에서 OR 연산을 통해 정의한 event.operations 필드 값들 중의 하나이어야 한다. 만약 드래그 소스에서 정의되지 않은 값을 세팅하면, 그 동작은 DND.DROP_NONE 이 될 것이다. event.detail 필드는 dragOver, dragOperationChanged, dropAccept 그리고 drop 이벤트 발생시에 갱신해줄 수도 있다.

 

dragEnter 에서는 어플리케이션에서의 기본 동작을 정의한다. 위에서 언급한대로, 드롭 타겟이 DND.DROP_DEFAULT 스타일로 만들어졌다면 사용자가 다른 변경자 키(modifier keys)를 누르지 않을 경우에 발생할 것이다. 이 경우에는, dragEnter 이벤트에서 event.detail 필드를 DND.DROP_DEFAULT으로 설정한다. 어플리케이션에서는 event.detail 필드를 바꾸어서 원하는 동작을 기본 동작으로 설정할 수 있다. 만약 어플리케이션에서 event.detail 필드를 DND.DROP_DEFAULT에서 다른 동작으로 바꾸지 않으면, 기본 동작은 DND.DROP_MOVE로 바뀔 것이다. DND.DROP_DEFAULT 값은 dragOperationChanged 이벤트에서도 설정된다.

 

위 예제에서는, 드래그 소스에서 정의되어 있는 경우에 복사(Copy)를 기본 동작으로 설정한다.

 

동작

Win32 cursor
이동(Move)
사용자 삽입 이미지
복사(Copy)
사용자 삽입 이미지
연결(Link)
사용자 삽입 이미지
아무 것도 안함(None)
사용자 삽입 이미지

표 2: 전송 동작을 나타내기 위한 커서(cursor)

 

Lines 29 to 39:

드롭 타겟은 받을 수 있는 데이터의 타입을 선택할 수 있다. dragEnter 이벤트는 이를 위해 두 개의 필드를 가진다. event.currentType 은 어플리케이션에서 설정된 데이터 타입(TransferData 객체로 표현된)을 나타내고, event.dataTypes 는 드래그 소스에서 제공되는 타입의 리스트(TransferData 객체의 배열로 표현된)를 나타낸다. 당신은 event.dataTypes에 있는 값 중에서 event.currentType 을 설정할 수 있다. 이 필드는 dragOver, dragOperationChanged, and dropAccept 이벤트에서도 수정할 수 있다.

 

위 예제에서, 우리는 테이블에 텍스트나 파일을 드롭할 수 있도록 하였다, 그러나 둘 다 가능한 경우에 우리는 파일을 수락하도록 한다. 파일이 전송되는 경우에 우리는 단지 복사(Copy)만 가능하도록 한다. (이 예제를 돌리면서 운영체제의 파일을 지우길 원하지 않는다. :-)).

 

Lines 41 to 50:

dragOver 이벤트는 사용자가 드롭 타겟 위젯 위로 커서를 옮길 때마다 반복적으로 호출된다. 만약 커서가 움직이지 않으면, dragOver 이벤트는 일정한 간격으로 계속해서 발생할 것이다. 예제에서 보여지는 것에 추가로, 당신은 event.detail 이나 event.currentType 필드의 값을 수정할 수 있다. 이것은 종종 테이블이나 트리에서 당신이 선택한 아이템에 따라 동작이 바뀔 때 처리하곤 한다. 예를 들어, 파일 시스템을 표현하는 트리가 있을 때, 폴더에는 파일을 드롭할 수 있지만 파일에는 그렇지 않을 것이다. event.item 필드는 테이블이나 트리에서 어떤 아이템 위에 커서가 있는지 알려준다.

 

Line 42:

드래그 언더 효과(drag under effect)는 드롭 타겟에서 드롭이 처리될 곳에 대한 보다 상세한 정보를 사용자에게 알려주기 위한 시각 효과나 동작이다. 어플리케이션에서는 아래 표2에서 보는 것 처럼 eent.feedback 필드에 값을 설정함으로써 이 효과를 정의할 수 있다.

 

dragOver event.feedback 값설명
DND.FEEDBACK_SELECT커서 아래의 항목이 선택된다; 테이블이나 트리에서 사용한다.
DND.FEEDBACK_SCROLL드롭될 위치의 항목이 보이지 않는 경우 스크롤되어서 사용자가 볼 수 있도록 한다; 테이블이나 트리에서 사용한다.
DND.FEEDBACK_EXPAND사용자가 서브 항목에서 드롭타겟을 선택할 수 있도록 현재 항목을 펼친다; 트리에서 사용한다.
DND.FEEDBACK_INSERT_BEFORE커서 아래에 위치하는 항목 앞에 삽입 표시를 한다; 트리에서 사용한다.
DND.FEEDBACK_INSERT_AFTER커서 아래에 위치하는 항목 뒤에 삽입 표시를 한다; 트리에서 사용한다.
DND.FEEDBACK_NONE아무런 효과도 보여주지 않는다.

표 2: 드래그 언더 효과 타입(drag under effect types)

 

Lines 43 to 49:

타겟 위로 데이터를 드래깅 했을 때, event 에서 어떤 타입의 데이터가 드래그 되었는지는 알 수 있지만, 실제 데이터를 얻을 수는 없다. 예를 들어, event.currentType 은 파일이 드래그되었음을 알려주기는 하지만 그 파일의 이름이나 추가 정보는 알 수 없다. 만약 당신의 어플리케이션이 자바 파일과 텍스트 파일에 따라 다른 동작을 수행하는 경우라면, dragOver 이벤트에서 데이터를 얻을 수 있다. 불행하게도 이는 모든 플랫폼에서 지원하는 것이 아니다. 2.1 버젼의 경우 윈도우 환경에서만 dragOver 이벤트에서 데이터를 얻을 수 있다. 이 데이터는 event.currentType의 TransferData 객체를 적절한 Transfer 객체의 nativeToJava 메소드에 전달해서 접근할 수 있다. 윈도우를 제외한 모든 운영체제에서는 null 을 리턴할 것이다. 나중에는 이 기능이 다른 운영체제까지 확장될 것이다.

 

(알림: 1.0 에서 2.1 버젼까지는 event.currentType 에서만 데이터를 얻을 수 있었지만 3.0 버젼에서 event.dataTypes 에서 어떠한 타입의 데이터도 얻을 수 있다.)

 

Lines 51 to 65:
dragOperationChanged 이벤트는 사용자가 변경자 키(Ctrl, Shift, Command, Option 같은)를 누르거나 땠을 때 발생한다. 변경자 키는 수행될 동작을 바꿀 때 사용된다. 예를 들어, 윈도우에서는 Ctrl 키를 누르면 복사가되고, Ctrl과 Shift 키를 같이 누르면 바로가기가 된다. 그리고 Shift 키를 누르면 이동이 된다. 변경자 키를 누르지 않으면 기본 동작으로 처리된다. 자세한 정보를 위해 dragEnter 를 살펴보라.

 

Lines 66 to 67:
dragLeave 이벤트는 커서가 드롭 타겟 위젯의 범위 밖으로 움직일 때 발생한다. 만약 dragEnter 에서 어떤 리소스를 할당했다면, dragLeave 에서 이를 해제해야할 것이다. dragLeave 이벤트는 사용자가 ESC키(Escape)를 눌러서 드래그 & 드롭 동작을 취소했을 때와 드롭 동작이 처리되기 직전에도 발생한다.

 

Lines 68 to 69:
dropAccept 이벤트는 드롭 이벤트에서 리턴될 데이터 타입을 정의할 수 있는 마지막 기회를 제공한다. 이것은 event.dataTypes 에 정의되었던 값들 중의 하나를 event.currentDataType에 설정함으로써 이루어진다.

 

Lines 70 to 83:
드롭 이벤트는 사용자가 드롭 타겟 위에서 마우스를 때었을 때, 유요한 동작이고 currentDataType이 이전 이벤트들에서 요청되어진 것일 경우에 발생한다. event.data 필드는 요청된 데이터를 포함하고 있다. event.data 필드에 포함되는 객체 타입은 어떤 Transfer 타입이 요청되었으냐에 따라 정해진다. 이 데이터는 event.currentDataType 필드에 정의된 타입이다.

 

드롭 동작이 완료되면, event.detail 필드가 수행된 동작에 따라 갱신된다.

 

클립보드(Clipboard)

드래그 & 드롭은 드래그 소스와 타겟 사이에서 동시에 데이터를 전송한다. 그러나 때때로 사용자는 나중을 위해 전송된 데이터를 복사해두고 싶어한다. 이 때 사용할 수 있는 클립보드가 데이터의 임시 저장소 역할을 해준다. 게다가 클립보드를 이용하면 같은 데이터를 여러 개의 타겟에 복사할 수도 있다.

 

다음 예제에서, 우리는 두가지 다른 형태의 데이터를 클립보드에 복사하고, 클립보드로 부터 적당한 형태로 데이터를 읽어들일 것이다.

 

1import org.eclipse.swt.dnd.*;
2. . .
3public static void main(String[] args) {
4    Display display = new Display();
5    final Clipboard cb = new Clipboard(display);
6    Shell shell = new Shell(display);
7    final Text text = new Text(shell, SWT.BORDER | SWT.MULTI);
8    text.setBounds(10, 10, 300, 300);
9    Button button = new Button(shell, SWT.PUSH);
10    button.setText("Copy");
11    button.setBounds(320, 10, 100, 40);
12    button.addListener(SWT.Selection, new Listener() {
13        public void handleEvent(Event e) {
14            String textData = text.getSelectionText();
15            if (textData == null) return;
16            // RTF 포맷을 지원하기 위해 텍스트를 볼드체와 이텔릭체로 만든다.
17            String rtfData = "{\\rtf1\\b\\i " + textData + "}";
18            TextTransfer textTransfer = TextTransfer.getInstance();
19            RTFTransfer rtfTransfer = RTFTransfer.getInstance();
20            Transfer[] types = new Transfer[]{textTransfer, rtfTransfer};
21            cb.setContents(new Object[]{textData, rtfData}, types);
22        }
23  });
24  button = new Button(shell, SWT.PUSH);
25  button.setText("Paste");
26  button.setBounds(320, 60, 100, 40);
27  button.addListener(SWT.Selection, new Listener() {
28     public void handleEvent(Event e) {
29         TextTransfer transfer = TextTransfer.getInstance();
30         String data = (String)cb.getContents(transfer);
31         if (data == null) return;
32         text.insert(data);
33     }
34  });
35  shell.open();
36  while (!shell.isDisposed()) {
37     if (!display.readAndDispatch())
38         display.sleep();
39 
40  }
41  cb.dispose();
42  display.dispose();
43}

목록 3: 텍스트 복사를 위해 클립보드 사용하기

 

Line 1:
드래그 & 드롭과 관련된 클래스들은 org.eclipse.swt.dnd 패키지에 정의되어 있다.

 

Line 5:
클립보드 객체를 만든다. 클립보드 객체는 운영체제에서 제공하는 클립보드를 이용할 수 있게 해준다. 이것은 당신의 어플리케이션 내에서나 다른 어플리케이션 사이에서 데이터를 주고받을 수 있게 해준다. 클립보드 객체는 시스템 자원을 사용하기 때문에 사용을 끝냈을 때에는 반드시 반환해주어야 한다(41번째 줄 참조). 클립보드에 데이터를 넣어두고 싶을 때마다 새로운 클립보드 객체를 만들어도 되고, 어플리케이션 내에서 공통으로 사용하는 인스턴스를 만들어 두어도 된다.

 

Lines 14 to 21:
텍스트(Text) 위젯에서 선택된 텍스트를 클립보드에 복사한다. 선택된 텍스트가 없으면 아무 작업도 하지 않는다.

클립보드에 데이터를 위치시킬 때, 한가지 이상의 형태로 데이터를 만드는 것이 가능하다. 위 예제에서는 텍스트 형태와 RTF 텍스트 형태로 저장하였다. 이것은 우리가 만든 어플리케이션과 다른 여러 종류의 어플리케이션 간의 상호작용을 가능하게 해준다. 즉, 우리가 RTF 텍스트 형태로만 클립보드에 저장하게 되면, RTF 텍스트 형태를 모르는 노트패드(Notepad) 같은 어플리케이션과는 상호작용을 할 수가 없다. 당신의 어플리케이션이 여러 환경에서 잘 활용될 수 있도록 하려면 가능한 많은 형태로 데이터를 저장할 수 있도록 한다.

 

Clipboard.setContents 호출이 성공할 때 마다 클립보드에 있던 내용들은 지워진다. 이것은 클립보드에 당신이 저장한 내용에만 적용되는 것이 아니라 모든 데이터에 대해 적용된다. 예를 들어, 클립보드에 다른 어플리케이션이 파일 정보를 저장해둔 상태에서 당신이 텍스트 정보를 Clipboard.setContents 를 호출해서 저장하게 되면 파일 정보는 더 이상 사용할 수 없게 된다. 만약 동시에 여러 종류의 데이터를 클립보드에 저장하고 싶다면 기존에 저장된 내용을 Clipboard.setContents 호출을 할 때 함께 처리해야 한다. Clipboard.setContents API는 데이터 객체의 배열과 데이터 타입의 배열을 인자로 받는다. 데이터 객체의 순서는 데이터 형태의 순서와 동일해야 하며 대응하는 데이터 형태에 대해 유효한 것이어야 한다. (즉, TextTransfer 타입은 String 이어야하고, FileTransfer 타입은 String[] 여야 한다 – 각각의 타입에 대한 javadoc 에는 적절한 데이터 객체에 대한 설명을 포함하고 있다.

 

클립보드에 저장된 내용은 어플리케이션이 종료된 후에도 사용 가능하다. (알림: GTK는 예외다. 현재, GTK에는 어플리케이션이 종료된 후에도 내용을 유지하는 메커니즘이 없다.)

 

몇몇 플랫폼에서는, 실제로 여러 개의 클립보드를 사용할 수 있다. Unix/Linux 에서는 가장 최근에 선택된 내용(선택 동작을 통해 내부적으로 처리된)을 포함하는 PRIMARY 클립보드가 있고, CTRL+Insert 같은 단축키를 이용해 명시적으로 저장하거나 메뉴 아이템을 통해(이것 역시 다른 클립보드지만 드물게 사용된다.) 저장한 내용을 포함하는 CLIPBOARD 클립보드가 있다. GTK와 Motif 에서는 타겟에 클립보드 데이터를 넣거나 꺼낼 때 DND.CLIPBOARD 와 DND.SELECTION_CLIPBOARD를 사용해서 클립보드를 선택할 수 있다. Mac OS X에서 SWT는 기본 Scrap을 지원한다.

 

Lines 29 to 32:
클립보드에 사용가능한 텍스트가 있으면, 텍스트(Text) 위젯에 내용을 입력한다.

 

정확한 타입의 데이터를 요청하기 위해 적절한 타입의 전송(Transfer) 서브클래스를 인자로 Clipboard.getContents 를 호출한다. 위 예제에서는, 텍스를 붙여넣기 때문에 TextTransfer 클래스를 사용한다. 만약 사용가능한 데이터 타입이 없을 경우에는 null이 리턴된다. Clipboard.getContents를 통해 리턴되는 객체의 타입은 전송(Transfer) 클래스를 사용하는 형태이다. 전송(Transfer) 서브클래스의 javadoc에 자바 객체 타입의 상세 설명이 있다.

 

Line 41:
클립보드를 제거(Dispose)한다. 위에서 언급한대로, 클립보드 제거에 실패하게 되면 시스템 자원 누수(leak)의 원인이 된다.

 

3.0에서의 새로운 점 - 클립보드에 데이터 타입을 쿼리하는 방법

SWT 2.1 이전 버전에서는 클립보드 상에 특정 데이터 타입만이 사용 가능할 경우에 이를 확인하는 단 하나의 메커니즘(Clipboard.getContents 를 사용하여 실제로 데이터를 전달해버리는)만이 가능했다. 이 메커니즘은 느리기도 하지만 잘라내기(Cut) 동작에서 문제를 일으킬 수 있었다. 잘라내기를 할 때 하나의 어플리케이션이 클립보드에 데이터를 넣어둔 상태에서 클립보드로 부터 데이터가 전송되어 지면 원본 데이터가 삭제되는 경우가 있었다. 3.0 버전에서는 당신의 어플리케이션에 실제로 데이터를 전송하지 않고 클립보드에서 데이터 타입을 쿼리할 수 있는 새로운 API가 추가되었다. 목록 4에서 이에 대한 사용법을 알아보자.

 

1TransferData[] available = cb.getAvailableTypes();
2boolean enabled = false;
3for (int i = 0; i < available.length; i++) {
4    if (TextTransfer.getInstance().isSupportedType(available[i])) {
5        enabled = true;
6        break;
7    }
8}
9pasteMenuItem.setEnabled(enabled);

목록 4: 클립보드에서 사용 가능한 데이터 타입 쿼리하기.

 

Clipboard.getAvailableTypes 를 호출하면 TransferData 객체의 배열이 리턴된다. TransferData 객체는 데이터 타입을 표현하는 플랫폼 상세 정보를 포함하고 있다. TransferData 객체의 필드들이 공개(public)되어 있는 동안에는, 그것들을 직접 판단(interpret)하려고 하면 안된다. 4째줄에서 보는 것처럼 Transfer 의 서브클래스의 isSupportedType 메소드를 대신 사용해야 한다.

 

전송(Transfer)

Transfer 는 자바 데이터 표현과 플랫폼의 데이터 표현 사이의 컨버팅(converting) 을 위한 메커니즘을 제공하는 추상 클래스이다. 자바 데이터 표현은 현재 어플리케이션에서 사용하는 것이다. 예를 들어, 텍스트는 String 객체로 표현된다. 플랫폼 데이터 표현은 현재 운영체제에서 사용하는 것이고 SWT에서는 TransferData 객체로 표현된다. 표 3에서 org.eclipse.swt.dnd 에서 제공하는 Transfer의 서브클래스들을 나열했다.

 

TransferJava 타입
TextTransferString"hello world"
RTFTransferString"{\\rtf1\\b\\i hello world}"
FileTransferString[]new String[] {file1.getAbsolutePath(), file2.getAbsolutePath()}

표 3: SWT에서 제공되는 전송 타입

 

TransferData 클래스는 플랫폼 종속적인 공개(public) 필드를 포함하고 있다. TransferData 의 이 필드가 플랫폼 간에 차이가 있기 때문에 어플리케이션에서 이 필드를 접근하면 안된다. 이 필드를 공개(public)로 만든 이유는 개발자가 Transfer 클래스를 확장하게 하고 데이터 전송을 위한 추가적인 플랫폼 지원을 위한 것이다. (예. 비트맵 이미지나 웨이브 파일).

 

요약

이 문서에서는 드래그 & 드롭과 클립보드를 이용한 데이터 전송에 대한 SWT 메커니즘을 설명했다. SWT는 최대한의 시스템 통합을 위해 어플리케이션 간의 데이터 전송에 대해 운영체제에서 제공하는 메커니즘을 사용한다. SWT는 기본적인 데이터 전송 유형과 더불어 당신이 정의한 전송 유형 뿐만 아니라 플랫폼에서 정의한 데이터 유형까지 지원해준다.

 

Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.