Linux 版 (精华区)

发信人: netiscpu (平淡是真), 信区: Linux
标  题: GTK入门导引(16)
发信站: 紫 丁 香 (Mon Dec 14 14:47:28 1998), 转信


发信人: hey (吟风·悠游98), 信区: Unix
标  题: GTK入门导引(16)
发信站: 华南网木棉站 (Tue Nov 10 11:18:28 1998), 转信

16. 选取区域管理

16.1 概说 

GTK所支援的其中一种行程间通讯为selections. 一个selection本身是一笔资
料, 例如, 使用者选取文字的一部份, 又如, 由滑鼠抓出一些东西. 在显示器上一
次只能有一个"选取区域", 上一个选取区域要在该区域撤销时才会生效. 其它的
应用软体以几种各种不同形式取得其内容, 被称为targets. 可以有许多个
selections, 但X软体只能处理一个, 即primary selection. 

在大部份的状况下, GTK程式不需要自行处理选取区域. 标准物件如Entry物
件, 已经有能力来自动产生选取区域, 并从其它物件撷取选取区域. 不过有时候,
您想要给其它物件有能力提供选取区域, 或当内定不支援, 想要撷取资料时. 

一个基本观念需要了解选取区域处理的是atom. 一个atom是个integer, 标记著
一个字串. 有些特定的元素被X server事先定义过, 有些在gtk.h中则为固
定数值, 对映到这些atoms. 例如GDK_PRIMARY_SELECTION对映
到字串"PRIMARY". 在其它状况下, 您应该使用
gdk_atom_intern()这个函数, 用以取得atom对映到string,
及gdk_atom_name(), 用以取得atom的名称. selections及targets都
是一种atoms. 

16.2 撷取selection 

撷取selection是个非同步行程. 您可以呼叫: 

    gint gtk_selection_convert   (GtkWidget           *widget, 
                                  GdkAtom              selection, 
                                  GdkAtom              target,
                                  guint32              time)

这个函数转换选取区域到target所指定的形式. time这一栏是由选取被触
发到事件发生的时间. 这使我们可以保证事件发生的顺序. 您也可以用
GDK_CURRENT_TIME来替代. 

当选取区域的拥有者回应一个要求时, 一个"selection_received"信号会送到您
的程式. 该信号处理器会收到一个指标GtkSelectionData 结构, 定
义如下: 

    struct _GtkSelectionData
    {
      GdkAtom selection;
      GdkAtom target;
      GdkAtom type;
      gint    format;
      guchar *data;
      gint    length;
    };

selection及target 是您在
gtk_selection_convert()中所给的值. type由选区拥有
者返回, 用来辨识资料型态. 可以是这些值"STRING", 字串, "ATOM", 一系列
的atoms, "INTEGER", 一个integer, 等等. a series of atoms, "INTEGER", an
integer, etc. 大部份targets只能返回一种型态. format是每个单位有多少
的bits(如字元为8 bits, guint32为32 bits). 一般来说, 您在收资料的时候, 不必管
这个值. data是返回的资料指标. length是返回资料的长度, 以byte做单
位. 如果length是负值, 那么表示有错误发生, 选取区域无效. 这在所被要
求选区的程式本身不拥有或不支援的时候会发生. 该缓冲区事实上保证一定有
多出一个byte; 多出来的byte永远为零, 所以不需要多复制一份字串备份. 

在以下的例子中, 我们撷取特别的target, "TARGETS", 这是个所有selection
都可以转换进去的target. 

    #include <gtk/gtk.h>

    void selection_received (GtkWidget *widget, 
                             GtkSelectionData *selection_data, 
                             gpointer data);

    /* Signal handler invoked when user clicks on the "Get Targets" button */
    void
    get_targets (GtkWidget *widget, gpointer data)
    {
      static GdkAtom targets_atom = GDK_NONE;

      /* Get the atom corresonding to the string "TARGETS" */
      if (targets_atom == GDK_NONE)
        targets_atom = gdk_atom_intern ("TARGETS", FALSE);

      /* And request the "TARGETS" target for the primary selection */
      gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
                             GDK_CURRENT_TIME);
    }

    /* Signal handler called when the selections owner returns the data */
    void
    selection_received (GtkWidget *widget, GtkSelectionData *selection_data, 
                        gpointer data)
    {
      GdkAtom *atoms;
      GList *item_list;
      int i;

      /* **** IMPORTANT **** Check to see if retrieval succeeded  */
      if (selection_data->length < 0)
        {
          g_print ("Selection retrieval failed\n");
          return;
        }
      /* Make sure we got the data in the expected form */
      if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
        {
          g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
          return;
        }
      
      /* Print out the atoms we received */
      atoms = (GdkAtom *)selection_data->data;

      item_list = NULL;
      for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
        {
          char *name;
          name = gdk_atom_name (atoms[i]);
          if (name != NULL)
            g_print ("%s\n",name);
          else
            g_print ("(bad atom)\n");
        }

      return;
    }

    int 
    main (int argc, char *argv[])
    {
      GtkWidget *window;
      GtkWidget *button;
      
      gtk_init (&argc, &argv);

      /* Create the toplevel window */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title (GTK_WINDOW (window), "Event Box");
      gtk_container_border_width (GTK_CONTAINER (window), 10);

      gtk_signal_connect (GTK_OBJECT (window), "destroy",
                          GTK_SIGNAL_FUNC (gtk_exit), NULL);

      /* Create a button the user can click to get targets */

      button = gtk_button_new_with_label ("Get Targets");
      gtk_container_add (GTK_CONTAINER (window), button);

      gtk_signal_connect (GTK_OBJECT(button), "clicked",
                          GTK_SIGNAL_FUNC (get_targets), NULL);
      gtk_signal_connect (GTK_OBJECT(button), "selection_received",
                          GTK_SIGNAL_FUNC (selection_received), NULL);

      gtk_widget_show (button);
      gtk_widget_show (window);
      
      gtk_main ();
      
      return 0;
    }

16.3 提供选取区域 

提供选取区域比较复杂. 您必须注册handler, 当您被要求提供选区时, handler
会被呼叫到. 对每个selection/target, 您必须呼叫: 

    void gtk_selection_add_handler (GtkWidget           *widget, 
                                    GdkAtom              selection,
                                    GdkAtom              target,
                                    GtkSelectionFunction function,
                                    GtkRemoveFunction    remove_func,
                                    gpointer             data);

widget, selection, 及target 表示这个处理器会处理的要求.
如果remove_func不为NULL, 当信号处里器被移除时, 这个函数会被
移除. 这很有用, 例如说, 给解译式语言用, 因为它会保持追踪并维护其自身的资
料. 

该callback函数有以下的形式: 

    typedef void (*GtkSelectionFunction) (GtkWidget *widget, 
                                          GtkSelectionData *selection_data,
                                          gpointer data);

GtkSelectionData跟上面一样, 但这一次, 我们必须要填type, format,
data, 及length这几栏. (format这一栏很重要 - X server用来决定
是否需要做byte-swap, 因为有X是多平台的系统, 一般8是character, 32是
integer.) 这是由以下函数所完成的: 

    void gtk_selection_data_set (GtkSelectionData *selection_data,
                                 GdkAtom           type,
                                 gint              format,
                                 guchar           *data,
                                 gint              length);

这个函数会将资料备一份, 因此您不需要自行维护. (这就是说您不应该自己手
动去填该资料结构的资料.) 

您可以用以下函数设定该选区的拥有者: 

    gint gtk_selection_owner_set (GtkWidget           *widget,
                                  GdkAtom              selection,
                                  guint32              time);

如果有其它程式设定了该选区的拥有权, 您会收到一个
"selection_clear_event"信号. 

做为一个提供选区的例子, 以下程式将选取功能加到一个双态按钮. 当双态按
钮被按下时, 该程式会设定拥有该选区. 而唯一支援的target是"STRING"
target. 当该target被要求时, 将会返回一个显示时间的字串. 

    #include <gtk/gtk.h>
    #include <time.h>

    /* Callback when the user toggles the selection */
    void
    selection_toggled (GtkWidget *widget, gint *have_selection)
    {
      if (GTK_TOGGLE_BUTTON(widget)->active)
        {
          *have_selection = gtk_selection_owner_set (widget,
                                                     GDK_SELECTION_PRIMARY,
                                                     GDK_CURRENT_TIME);
          /* if claiming the selection failed, we return the button to
             the out state */
          if (!*have_selection)
            gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
        }
      else
        {
          if (*have_selection)
            {
              /* Before clearing the selection by setting the owner to NULL,
                 we check if we are the actual owner */
              if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
                gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
                                         GDK_CURRENT_TIME);
              *have_selection = FALSE;
            }
        }
    }

    /* Called when another application claims the selection */
    gint
    selection_clear (GtkWidget *widget, GdkEventSelection *event,
                     gint *have_selection)
    {
      *have_selection = FALSE;
      gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);

      return TRUE;
    }

    /* Supplies the current time as the selection. */
    void
    selection_handle (GtkWidget *widget, 
                      GtkSelectionData *selection_data,
                      gpointer data)
    {
      gchar *timestr;
      time_t current_time;

      current_time = time (NULL);
      timestr = asctime (localtime(&current_time)); 
      /* When we return a single string, it should not be null terminated.
         That will be done for us */

      gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
                              8, timestr, strlen(timestr));
    }

    int
    main (int argc, char *argv[])
    {
      GtkWidget *window;

      GtkWidget *selection_button;

      static int have_selection = FALSE;
      
      gtk_init (&argc, &argv);

      /* Create the toplevel window */

      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title (GTK_WINDOW (window), "Event Box");
      gtk_container_border_width (GTK_CONTAINER (window), 10);

      gtk_signal_connect (GTK_OBJECT (window), "destroy",
                          GTK_SIGNAL_FUNC (gtk_exit), NULL);

      /* Create a toggle button to act as the selection */

      selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
      gtk_container_add (GTK_CONTAINER (window), selection_button);
      gtk_widget_show (selection_button);

      gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
                          GTK_SIGNAL_FUNC (selection_toggled), &have_selection);
      gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
                          GTK_SIGNAL_FUNC (selection_clear), &have_selection);

      gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
                                 GDK_SELECTION_TYPE_STRING,
                                 selection_handle, NULL, NULL);

      gtk_widget_show (selection_button);
      gtk_widget_show (window);
      
      gtk_main ();
      
      return 0;
    }

--
        6m3m┌───────────────────────┐0m
        6m3m│     4m疾如风,徐如林,侵掠如火,不动如山       3m│4m 0m
        6m3m└───────────────────────┘0m4m 0m
          4m                                                 0m

m;32m※ 来源:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 202.38.212.66]m
--
m;32m※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: mtlab.hit.edu.cn]
--

                              Enjoy Linux!
                          -----It's FREE!-----

※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: mtlab.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:208.210毫秒