<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="photo-wall.c" lang="ko">
  <info>
    <title type="text">Photo wall(C)</title>
    <link type="guide" xref="c#examples"/>

    <desc>클러터 그림 보기</desc>

    <revision pkgversion="0.1" version="0.1" date="2011-03-22" status="review"/>
    <credit type="author">
      <name>Chris Kühl</name>
      <email its:translate="no">chrisk@openismus.com</email>
    </credit>
    <credit type="author">
      <name>Johannes Schmid</name>
      <email its:translate="no">jhs@gnome.org</email>
    </credit>
    <credit type="editor">
      <name>Marta Maria Casetti</name>
      <email its:translate="no">mmcasetti@gmail.com</email>
      <years>2013</years>
    </credit>
  <credit type="translator copyright"><name>조성호</name><email>shcho@gnome.org</email><years>2017-2019.</years></credit></info>

<title>포토 월</title>

<synopsis>
  <p>이 예제에서는 클러터로 간단한 그림 보기 프로그램을 만들겠습니다. 다음을 익히는 과정입니다:</p>
  <list>
    <item><p><code>ClutterActor</code>에서 크기 위치 설정하는 방법</p></item>
    <item><p><code>ClutterActor</code>에서 그림 배치하는 방법</p></item>
    <item><p>클러터 애니메이션 프레임워크로 간단하게 트랜지션 처리하는 방법</p></item>
    <item><p>마우스 이벤트에 대해 <code>ClutterActor</code>에서 응답하는 방법</p></item>
    <item><p>디렉터리에서 파일 이름 가져오기</p></item>
  </list>
</synopsis>

<section id="intro">
  <title>도입부</title>
  <p>클러터는 하드웨어 가속 OpenGL을 활용하여 동적 사용자 인터페이스를 만드는 라이브러리입니다. 이 예제는 작은 일부를 보여주지만, 중심적으로는, 간단하지만 매력적인 그림 보기 프로그램을 만드는 클러터 라이브러리의 일부 동작을 시연합니다.</p>
  <p>목적을 위해 GLib 같은 일부 일반적인 부분도 마찬가지로 활용합니다. 상당히 중요하게 <code>GPtrArray</code> 동적 포인터 배열을 활용하여 파일 경로 이름을 넣어두겠습니다. 또한 디렉터리를 다루는 <code>GDir</code>로 그림 디렉터리에 접근하고 경로를 가져올 때 활용하겠습니다.</p>
</section>

<section id="anjuta">
  <title>안주타에서 프로젝트 만들기</title>
  <p>코딩을 시작하기 전에 안주타에서 새 프로젝트를 설정해야합니다. 이 프로그램은 빌드에 필요한 모든 파일을 만들고 그 다음 코드를 실행합니다. 또한 이 모든 상태를 유지 관리하는데 쓸만합니다.</p>
  <steps>
    <item>
    <p>안주타를 시작하고 <guiseq><gui>파일</gui><gui>새로 만들기</gui><gui>프로젝트</gui></guiseq> 를 눌러 프로젝트 마법사를 여십시오.</p>
    </item>
    <item>
    <p><gui>C</gui> 탭에서 <gui>GTK+ (단순)</gui>를 선택하고, <gui>계속</gui>을 누른 다음, 나타난 페이지에서 몇가지 자세한 내용을 입력하십시오. 프로젝트 이름과 디렉터리에 <file>photo-wall</file>을 입력하십시오.</p>
   	</item>
    <item>
    <p>앞서 따라하기 지침을 통해 사용자 인터페이스를 직접 만들 예정이므로 <gui>사용자 인터페이스에 GtkBuilder 사용</gui> 설정을 껐는지 확인하십시오. 인터페이스 빌더 사용법을 알아보려면 <link xref="guitar-tuner.c">기타 조율 프로그램</link> 따라하기 지침서를 확인하십시오.</p>
    </item>
    <item>
    <p><gui>외부 패키지 설정</gui>을 <gui>켬</gui>으로 설정했는지 확인하십시오. 다음 페이지의 목록에서 <em>gstreamer-0.10</em>를 선택하여 프로젝트에 지스트리머 라이브러리를 넣으십시오.</p>
    </item>
    <item>
    <p><gui>적용</gui>을 누르면 프로젝트를 만들어줍니다. <gui>프로젝트</gui>나 <gui>파일</gui>탭에서 <file>src/main.c</file> 파일을 여십시오. 다음 줄로 시작하는 일부 코드를 볼 수 있어야합니다:</p>
    <code mime="text/x-csrc">
#include &lt;config.h&gt;
#include &lt;gtk/gtk.h&gt;</code>
    </item>
  </steps>
</section>

<section id="look">
  <title>포토 월 살펴보기</title>
  <p>그립 보기 프로그램에서는 그림 벽으로 사용자를 나타냅니다.</p>
  <media type="image" mime="image/png" src="media/photo-wall.png"/>
  <p>그림을 누르면 보기 영역을 채울 때 움직입니다. 포커스를 획득한 그림이 눌린 상태면, 500 밀리초 동안 움직임을 활용하여 원래 위치로 돌아옵니다.</p>
  <media type="image" mime="image/png" src="media/photo-wall-focused.png"/>
</section>

<section id="setup">
  <title>초기 설정</title>
  <p>다음 코드 일부에는 다음 섹션에서 활용할 정의와 변수 대부분이 들어갑니다. 이 부분을 다음 섹션 참고 용도로 활용하십시오. 이 코드를 <file>src/main.c</file> 시작 부분에 복사해두십시오:</p>
<code mime="text/x-csrc" style="numbered">
#include &lt;gdk-pixbuf/gdk-pixbuf.h&gt;
#include &lt;clutter/clutter.h&gt;

#define STAGE_WIDTH  800
#define STAGE_HEIGHT 600

#define THUMBNAIL_SIZE 200
#define ROW_COUNT (STAGE_HEIGHT / THUMBNAIL_SIZE)
#define COL_COUNT (STAGE_WIDTH  / THUMBNAIL_SIZE)
#define THUMBNAIL_COUNT (ROW_COUNT * COL_COUNT)

#define ANIMATION_DURATION_MS 500

#define IMAGE_DIR_PATH "./berlin_images/"

static GPtrArray *img_paths;

static ClutterPoint unfocused_pos;

</code>
</section>

<section id="code">
  <title>코드로 넘어가기</title>
  <p><code>main()</code> 함수를 전체로서 먼저 살펴보겠습니다. 그 다음 다른 코드 부분을 자세하게 살펴보면서 이야기하겠습니다. <file>src/main.c</file> 파일을 이 <code>main()</code> 함수 코드가 들어가도록 바꾸십시오. 이 예제를 실행할 때 <code>create_window()</code> 함수는 필요 없으므로 삭제할 수 있습니다.</p>
  <code mime="text/x-csrc" style="numbered">
int
main(int argc, char *argv[])
{
    ClutterColor stage_color = { 16, 16, 16, 255 };
    ClutterActor *stage = NULL;

    if (clutter_init (&amp;argc, &amp;argv) != CLUTTER_INIT_SUCCESS)
        return 1;

    stage = clutter_stage_new();
    clutter_actor_set_size(stage, STAGE_WIDTH, STAGE_HEIGHT);
    clutter_actor_set_background_color(stage, &amp;stage_color);
    clutter_stage_set_title(CLUTTER_STAGE (stage), "Photo Wall");
    g_signal_connect(stage, "destroy", G_CALLBACK(clutter_main_quit), NULL);

    load_image_path_names();

    guint row = 0;
    guint col = 0;
    for(row=0; row &lt; ROW_COUNT; ++row)
    {
        for(col=0; col &lt; COL_COUNT; ++col)
        {
            const char *img_path = g_ptr_array_index(img_paths, (row * COL_COUNT) + col);
            GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(img_path, STAGE_HEIGHT, STAGE_HEIGHT, NULL);
            ClutterContent *image = clutter_image_new ();
            ClutterActor *actor = clutter_actor_new ();

            if (pixbuf != NULL)
            {
                clutter_image_set_data(CLUTTER_IMAGE(image),
                                       gdk_pixbuf_get_pixels(pixbuf),
                                       gdk_pixbuf_get_has_alpha(pixbuf)
                                           ? COGL_PIXEL_FORMAT_RGBA_8888
                                           : COGL_PIXEL_FORMAT_RGB_888,
                                       gdk_pixbuf_get_width(pixbuf),
                                       gdk_pixbuf_get_height(pixbuf),
                                       gdk_pixbuf_get_rowstride(pixbuf),
                                       NULL);
            }

            clutter_actor_set_content(actor, image);
            g_object_unref(image);
            g_object_unref(pixbuf);

            initialize_actor(actor, row, col);
            clutter_actor_add_child(stage, actor);
        }
    }

    /* Show the stage. */
    clutter_actor_show(stage);

    /* Start the clutter main loop. */
    clutter_main();

    g_ptr_array_unref(img_paths);

    return 0;
}</code>
  <list>
    <item><p>4번째 줄: <code>ClutterColor</code>는 적색, 녹색, 청색, 투명도(alpha) 값을 설정하여 정의합니다. 값 범위는 0부터 255까지입니다. 255는 투명 수치가 가장 낮습니다.</p></item>
    <item><p>7번째 줄: 클러터를 초기화해야 합니다. 까먹었다면 이상한 오류가 납니다. 유의하세요.</p></item>
    <item><p>10~14번째 줄: 여기서 새 <code>ClutterStage</code>를 만듭니다. 앞 부분에서 정의한 코드로 사이즈를 설정하고 방금 정의한 <code>ClutterColor</code> 의 위치를 정의합니다.</p>
      <note><p><code>ClutterStage</code>는 다른 <code>ClutterActor</code>가 놓인 곳의 최상위 <code>ClutterActor</code> 입니다.</p></note>
</item>
    <item><p>16번째 줄: 여기서 그림 파일 경로를 가져오는 함수를 호출합니다. 이 부분을 조금 살펴보겠습니다.</p></item>
    <item><p>18~49번째 줄: 이 부분은 그림을 불러오고 그림 벽 부분에 두는 <code>ClutterActor</code>을 설정하는 부분입니다. 다음 섹션에서 자세하게 이 부분을 살펴보겠습니다.</p></item>
    <item><p>52번째 줄: 그림의 스테이지와 <em>하위 요소</em>를 나타냅니다.</p></item>
    <item><p>55번째 줄: 클러터 메인 루프를 시작합니다.</p></item>
  </list>
</section>

<section id="actors">
  <title>그림 액터 구성</title>
 <note><p>클러터에서 액터는 사장 기본적인 시각 구성 요소입니다. 근본적으로 여러분이 보는 모든 구성 요소는 액터입니다.</p></note>
<p>이 부분에서는 그림을 나타내는 <code>ClutterActor</code>를 설정하는 루프를 더 자세하게 살펴보겠습니다.</p>
  <code mime="text/x-csrc" style="numbered">
guint row = 0;
guint col = 0;
for(row=0; row &lt; ROW_COUNT; ++row)
{
    for(col=0; col &lt; COL_COUNT; ++col)
    {
        const char *img_path = g_ptr_array_index(img_paths, (row * COL_COUNT) + col);
        GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(img_path, STAGE_HEIGHT, STAGE_HEIGHT, NULL);
        ClutterContent *image = clutter_image_new ();
        ClutterActor *actor = clutter_actor_new ();

        if (pixbuf != NULL)
        {
            clutter_image_set_data(CLUTTER_IMAGE(image),
                                   gdk_pixbuf_get_pixels(pixbuf),
                                   gdk_pixbuf_get_has_alpha(pixbuf)
                                       ? COGL_PIXEL_FORMAT_RGBA_8888
                                       : COGL_PIXEL_FORMAT_RGB_888,
                                   gdk_pixbuf_get_width(pixbuf),
                                   gdk_pixbuf_get_height(pixbuf),
                                   gdk_pixbuf_get_rowstride(pixbuf),
                                   NULL);
        }

        clutter_actor_set_content(actor, image);
        g_object_unref(image);
        g_object_unref(pixbuf);

        initialize_actor(actor, row, col);
        clutter_actor_add_child(stage, actor);
    }
}

</code>
<list>
  <item><p>7번째 줄:  여기서는 그림 경로 이름을 넣는 <code>GPtrArray</code>의 <var>n</var>번째 위치에서 경로를 가져오려고 합니다. <var>n</var>번째  위치는 <code>행</code>과 <code>열</code> 기반으로 계산합니다.</p>
  </item>
  <item><p>8~23번째 줄: 실제로 <code>ClutterActor</code>를 만들고 액터에 그림을 넣는 부분입니다. 첫 인자는 <code>GSList</code> 노드로 접근할 경로입니다. 두번째 인자는 오류 보고용이지만 단순성을 유지할 목적으로 무시합니다.</p>
  </item>
  <item><p>47번째 줄: <code>ClutterActor</code>를 컨테이너 스테이지에 추가합니다. 또한 여러분이 그놈 개발상 깊숙한 부분을 보려는 <code>ClutterActor</code>의 소유자를 간주합니다. 적나라한 세부 내용은 <link href="http://library.gnome.org/devel/gobject/stable/gobject-memory.html"><code>GObject</code> 문서</link>를 참고하십시오.</p>
  </item>
</list>
</section>

<section id="load">
  <title>그림 불러오기</title>
  <p>그림 디렉터리에서 파일 이름을 가져오는 방법을 살펴볼 참에 클러터 내용 다루는 부분은 잠시 접어두겠습니다.</p>
  <code mime="text/x-csrc" style="numbered">
static void
load_image_path_names()
{
    /* Ensure we can access the directory. */
    GError *error = NULL;
    GDir *dir = g_dir_open(IMAGE_DIR_PATH, 0, &amp;error);
    if(error)
    {
        g_warning("g_dir_open() failed with error: %s\n", error-&gt;message);
        g_clear_error(&amp;error);
        return;
    }

    img_paths = g_ptr_array_new_with_free_func (g_free);

    const gchar *filename = g_dir_read_name(dir);
    while(filename)
    {
        if(g_str_has_suffix(filename, ".jpg") || g_str_has_suffix(filename, ".png"))
        {
            gchar *path = g_build_filename(IMAGE_DIR_PATH, filename, NULL);
            g_ptr_array_add (img_paths, path);
        }
        filename = g_dir_read_name(dir);
    }
}</code>
  <list>
    <item><p>5~12번째 줄: 디렉터리를 열거나 오류가 발생했을 경우 오류 메시지를 출력한 후 값을 반환합니다.</p></item>
    <item><p>16~25번째 줄: 첫 줄은 앞서 열어둔 <code>GDir</code>에서 다른 파일 이름을 가져옵니다. 디렉터리에 그림 파일(".png" 또는 ".jpg" 확장자를 보고 확인)이 있다면 파일 이름에 그림 디렉터리 경로를 붙이고 앞서 설정한 목록에 추가합니다. 마지막으로, 그 다음 경로 이름을 가져온 다음, 해당 경로에서 다른 파일을 찾으면 반복문에 다시 들어갑니다.</p></item>
  </list>
</section>

<section id="actors2">
  <title>액터 구성</title>
  <p>이제 <code>ClutterActor</code>의 크기 조절 및 위치 설정, 사용자 반응 대기 부분을 살펴보겠습니다.</p>
  <code mime="text/x-csrc" style="numbered">
/* This function handles setting up and placing the rectangles. */
static void
initialize_actor(ClutterActor *actor, guint row, guint col)
{
    clutter_actor_set_size(actor, THUMBNAIL_SIZE, THUMBNAIL_SIZE);
    clutter_actor_set_position(actor, col * THUMBNAIL_SIZE, row * THUMBNAIL_SIZE);
    clutter_actor_set_reactive(actor, TRUE);

    g_signal_connect(actor,
                     "button-press-event",
                     G_CALLBACK(actor_clicked_cb),
                     NULL);
}</code>
  <list>
    <item>
      <p>7번째 줄: 액터 반응 설정은 액터가 <code>button-press-event</code> 같은 이벤트에 반응하게 합니다. 포토 월에서는 벽에 있는 모든 <code>ClutterActor</code>에 반응 규칙을 초기화해야합니다.</p>
    </item>
    <item>
      <p>9~12번째 줄: 이제 <code>button-press-event</code> 이벤트를 다음에 살펴볼 <code>actor_clicked_cb</code> 콜백 함수에 연결합니다.</p>
    </item>
  </list>
  <p>여기서 볼 준비를 마친 그림 벽을 가져옵니다.</p>
</section>

<section id="click">
  <title>누름 동작에 반응</title>
  <p>

  </p>
  <code mime="text/x-csrc" style="numbered">
static gboolean
actor_clicked_cb(ClutterActor *actor,
                 ClutterEvent *event,
                 gpointer      user_data)
{
    /* Flag to keep track of our state. */
    static gboolean is_focused = FALSE;
    ClutterActorIter iter;
    ClutterActor *child;

    /* Reset the focus state on all the images */
    clutter_actor_iter_init (&amp;iter, clutter_actor_get_parent(actor));
    while (clutter_actor_iter_next(&amp;iter, &amp;child))
      clutter_actor_set_reactive(child, is_focused);

    clutter_actor_save_easing_state(actor);
    clutter_actor_set_easing_duration(actor, ANIMATION_DURATION_MS);

    if(is_focused)
    {
        /* Restore the old location and size. */
        clutter_actor_set_position(actor, unfocused_pos.x, unfocused_pos.y);
        clutter_actor_set_size(actor, THUMBNAIL_SIZE, THUMBNAIL_SIZE);
    }
    else
    {
        /* Save the current location before animating. */
        clutter_actor_get_position(actor, &amp;unfocused_pos.x, &amp;unfocused_pos.y);
        /* Only the currently focused image should receive events. */
        clutter_actor_set_reactive(actor, TRUE);

        /* Put the focused image on top. */
        clutter_actor_set_child_above_sibling(clutter_actor_get_parent(actor), actor, NULL);

        clutter_actor_set_position(actor, (STAGE_WIDTH - STAGE_HEIGHT) / 2.0, 0);
        clutter_actor_set_size(actor, STAGE_HEIGHT, STAGE_HEIGHT);
    }

    clutter_actor_restore_easing_state(actor);

    /* Toggle our flag. */
    is_focused = !is_focused;

    return TRUE;
}</code>
  <list>
    <item><p>1~4번째 줄: <code>button_clicked_event</code> 시그널에 필요한 시그니쳐에 일치하는 콜백 함수를 확인해야합니다. 예를 들면 실제로 누를 <code>ClutterActor</code>가 들어가는 첫번째 인자만 씁니다.</p>
<note>
  <p>이 예제에서 사용하지 않는 인자에 대해 몇가지 알아둘 내용이 있습니다. <code>ClutterEvent</code>는 어떤 이벤트를 처리할지에 따라 다릅니다. 예를 들면, 키 이벤트는 여러 이벤트 중에 키누름 이벤트를 받을 수 있는 <code>ClutterKeyEvent</code>를 내보냅니다. 마우스 단추 누름 이벤트 상황에선 <code>x</code>, <code>y</code> 값을 받을 수 있는 <code>ClutterButtonEvent</code>를 취할 수 있습니다. 다른 <code>ClutterEvent</code> 형식을 알아보려면 클러터 문서를 참고하십시오.</p>
  <p><code>user_data</code>는 함수에 데이터를 전달할 때 활용합니다. 어떤 데이터 형식의 포인터든 전달할 수 있습니다. 콜백에 여러 데이터를 넘겨야 한다면, 구조체에 데이터를 넣고 전달할 수 있습니다.</p>
</note></item>
    <item><p>7번째 줄: 우리가 보는 상태를 보여줄 정적 플래그 벽 모드 또는 포커스 모드를 설정합니다. 벽 모드로 시작해서 어떤 이미지에도 포커스를 맞추지 않도록 합니다. 따라서 초기 플래그 값은 <code>FALSE</code> 입니다.</p></item>
    <item><p>12~14번째 줄: 이 부분에서 그림에 포커스를 맞췄을 때 이벤트를 받을 그림 액터를 설정합니다.</p></item>
    <item><p>16~17번째 줄: 여기서 움직임 시간을 설정하고 현재 상태를 저장합니다.</p></item>
    <item><p>21~23번째 줄: 이 코드에 도달한다는 건 그림 하나에 포커스를 맞췄으며, 벽 모드로 돌아가려 함을 의미합니다. <code>ClutterActor</code>에 위치를 설정하면 17번째 줄에서 설정한 시간만큼 그림 움직임을 시작합니다.</p>
    </item>
    <item><p>24번째 줄: 이 코드 줄에 도달하면 현재 벽 상태에 있으며 <code>ClutterActor</code> 포커스를 주려는 상황입니다. 여기서 시작 위치를 저장하여 나중에 다시 이 위치로 돌아올 수 있습니다.</p></item>
    <item><p>25번째 줄: <code>ClutterActor</code>의 <code>reactive</code> 속성을 <code>TRUE</code>로 설정하면, <code>ClutterActor</code>에서 이벤트에 반응합니다. 이 포커스 상태에서 우리가 이벤트를 받으려는 <code>ClutterActor</code>는 우리가 보고 있는 <code>ClutterActor</code>입니다. <code>ClutterActor</code>를 누르면 액터의 시작 위치로 돌아갑니다.</p></item>
    <item><p>27~36번째 줄: 이 부분은 그림의 현재 위치를 저장하고, 이벤트를 받도록 설정하며, 다른 그림 위로 나타나 스테이지를 채우는 움직임을 시작하는 부분입니다.</p></item>
    <item><p>39번째 줄: 이 부분에서는 16번째 줄에서 바꾸기 전의 움직임 처리 상태로 복원합니다.</p></item>
    <item><p>42번째 줄: 여기서 <code>is_focused</code> 값을 현재 상태 값으로 바꿉니다.</p></item>
<item><p>앞에서 말씀드린대로, 좀 더 큰 <code>depth</code> 값이 들어간 <code>ClutterActor</code>에서 이벤트를 받는데 <code>ClutterActor</code>의 아래에 있는 <code>ClutterActor</code>도 이벤트를 받을 수 있습니다. <code>TRUE</code>를 반환하면 이벤트를 전달해 내려가는 과정을 멈추며, <code>FALSE</code>를 반환하면 이벤트를 계속 하부로 전달해내려갑니다.</p>
 <note>
   <p>허나, <code>ClutterActor</code> 이벤트를 받으려면 <code>reactive</code>를 설정해야합니다.</p>
 </note>
</item>
 </list>
</section>

<section id="run">
  <title>프로그램 빌드 및 실행</title>
  <p>모든 코드 준비가 끝났습니다. 이제 그림을 불러오는 일이 필요합니다. 기본적으로 그림은 <file>berlin_images</file> 디렉터리에서 불러옵니다. 원한다면 사진 디렉터리 또는 <guiseq><gui>프로젝트</gui><gui>새 디렉터리…</gui></guiseq>를 누른 후 <file>photo-wall</file> 디렉터리의 하위 디렉터리로 <file>berlin_images</file> 디렉터리를 만든 후 참조하도록 상단 근처의 <code>#define IMAGE_DIR_PATH</code> 줄을 바꿀 수 있습니다. 디렉터리에 그림을 최소한 12개 넣었는지 확인하십시오.</p>
  <p>다 끝났으면 <guiseq><gui>빌드</gui><gui>프로젝트 빌드</gui></guiseq>를 눌러 모두 다시 빌드하고, <guiseq><gui>실행</gui><gui>실행</gui></guiseq>을 눌러 프로그램을 시작하십시오.</p>
  <p>아직 마무리가 안됐다면 나타나는 대화상자에서 <file>Debug/src/photo-wall</file> 프로그램을 선택하십시오. 마지막으로 <gui>실행</gui>을 치고 즐기시죠!</p>
</section>

<section id="impl">
 <title>참조 구현체</title>
 <p>지침서를 따라하는 실행하는 과정에 문제가 있다면, <link href="photo-wall/photo-wall.c">참조 코드</link>와 여러분의 코드를 비교해보십시오.</p>
</section>

</page>
