十四、Gtk4-Functions in GtkNotebook

GtkNotebook是文本编辑器的核心对象,管理多个页面,如打开、保存、关闭和新建文件。函数如notebook_page_new创建新页面,notebook_page_new_with_file加载文件,而notebook_page_save和notebook_page_close分别处理保存和关闭操作。file_changed_cb处理程序更新标签以反映文件变化。文章还讨论了GObject的引用计数,特别是浮动引用的概念。
摘要由CSDN通过智能技术生成

  GtkNotebook是文本文件编辑器tfe中一个非常重要的对象。它连接application和TfeTextView对象。tfenotebook.h中声明了一组公开函数。“tfenotebook”这个词只用于文件名。没有“TfeNotebook”对象。

 1 void
 2 notebook_page_save(GtkNotebook *nb);
 3 
 4 void
 5 notebook_page_close (GtkNotebook *nb);
 6 
 7 void
 8 notebook_page_open (GtkNotebook *nb);
 9 
10 void
11 notebook_page_new_with_file (GtkNotebook *nb, GFile *file);
12 
13 void
14 notebook_page_new (GtkNotebook *nb);
15 

这个头文件描述了tfenotebook.c中的公共函数。

  • 1-2: notebook_page_save 将当前页面保存到选项卡中指定的文件中。如果名称是untitled或untitled后跟数字,则会出现FileChooserDialog,用户可以选择或指定文件名。
  • 4-5: notebook_page_close关闭当前页面。
  • 7-8: notebook_page_open显示一个文件选择器对话框,用户可以选择一个文件。该文件被插入到一个新页。
  • 10-11: notebook_page_new_with_file创建一个新页,读取一个文件并将其作为参数插入到该页中。
  • 13-14: notebook_page_new创建一个新的空页。

你可能会发现,除了notebook_page_close之外,其他函数都是的高级函数

  • tfe_text_view_save
  • tef_text_view_open
  • tfe_text_view_new_with_file
  • tfe_text_view_new

分别是。

有两层。其中之一是tfe_text_view…,它是较低层。另一个是note_book…,这是更高的层次。

现在让我们看看每个函数的程序。

1 notebook_page_new

 1 static char*
 2 get_untitled () {
 3   static int c = -1;
 4   if (++c == 0) 
 5     return g_strdup_printf("Untitled");
 6   else
 7     return g_strdup_printf ("Untitled%u", c);
 8 }
 9 
10 static void
11 notebook_page_build (GtkNotebook *nb, GtkWidget *tv, const char *filename) {
12   GtkWidget *scr = gtk_scrolled_window_new ();
13   GtkNotebookPage *nbp;
14   GtkWidget *lab;
15   int i;
16 
17   gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), tv);
18   lab = gtk_label_new (filename);
19   i = gtk_notebook_append_page (nb, scr, lab);
20   nbp = gtk_notebook_get_page (nb, scr);
21   g_object_set (nbp, "tab-expand", TRUE, NULL);
22   gtk_notebook_set_current_page (nb, i);
23   g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb);
24 }
25 
26 void
27 notebook_page_new (GtkNotebook *nb) {
28   g_return_if_fail(GTK_IS_NOTEBOOK (nb));
29 
30   GtkWidget *tv;
31   char *filename;
32 
33   if ((tv = tfe_text_view_new ()) == NULL)
34     return;
35   filename = get_untitled ();
36   notebook_page_build (nb, tv, filename);
37   g_free (filename);
38 }
  • 26-38: notebook_page_new函数。
  • 28: g_return_if_fail用于检查参数。
  • 33-34:创建TfeTextView对象如果失败,则不会创建notebook页,函数返回给调用者。
  • 35:创建文件名,文件名为"Untitled", “Untitled1”, … .
  • 1-8: get_untitled函数。
  • 3:静态变量c在第一次调用这个函数时初始化。此后,除非显式地修改,否则c的值将保持不变。
  • 4-7:将c加1,如果它为0,则返回"Untitled"。如果它是一个正整数,则返回"Untitled<the integer>",例如"Untitled1", “Untitled2”,以此类推。函数g_strdup_printf创建一个字符串,当它变得无用时,g_free应该释放它。get_untitled的调用者负责释放字符串。
  • 36:调用notebook_page_build构建页面的内容。
  • 37:释放文件名。
  • 10- 24: notebook_page_build函数。带有const限定符的形参在函数中不会改变。这意味着参数filename归调用者所有。调用者需要在它变得无用时释放它。
  • 12:创建GtkScrolledWindow。
  • 17:将tv插入GtkscrolledWindow作为子窗口。
  • 18-19:创建GtkLabel,然后将scr和lab附加到GtkNotebook实例nb。
  • 20-21:设置“tab-expand”属性为TRUE。函数g_object_set用于设置对象的属性。对象是派生自GObject的任何对象。在很多情况下,对象都有自己设置属性的函数,但有时也没有。在这种情况下,使用g_object_set来设置属性。
  • 22:将当前页面设置为新创建的页面。
  • 23:连接change-file信号和file_changed_cb处理程序。

2 notebook_page_new_with_file

 1 void
 2 notebook_page_new_with_file (GtkNotebook *nb, GFile *file) {
 3   g_return_if_fail(GTK_IS_NOTEBOOK (nb));
 4   g_return_if_fail(G_IS_FILE (file));
 5 
 6   GtkWidget *tv;
 7   char *filename;
 8 
 9   if ((tv = tfe_text_view_new_with_file (file)) == NULL)
10     return; /* read error */
11   filename = g_file_get_basename (file);
12   notebook_page_build (nb, tv, filename);
13   g_free (filename);
14 }
  • 9-10:调用tfe_text_view_new_with_file。如果函数返回NULL,说明发生了错误。然后,它什么都不做并返回。
  • 11-13:获取文件名,构建页面内容并释放文件名。

3 notebook_page_open

 1 static void
 2 open_response (TfeTextView *tv, int response, GtkNotebook *nb) {
 3   GFile *file;
 4   char *filename;
 5 
 6   if (response != TFE_OPEN_RESPONSE_SUCCESS) {
 7     g_object_ref_sink (tv);
 8     g_object_unref (tv);
 9   }else {
10     file = tfe_text_view_get_file (tv);
11     filename = g_file_get_basename (file);
12     g_object_unref (file);
13     notebook_page_build (nb, GTK_WIDGET (tv), filename);
14     g_free (filename);
15   }
16 }
17 
18 void
19 notebook_page_open (GtkNotebook *nb) {
20   g_return_if_fail(GTK_IS_NOTEBOOK (nb));
21 
22   GtkWidget *tv;
23 
24   if ((tv = tfe_text_view_new ()) == NULL)
25     return;
26   g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response), nb);
27   tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW)));
28 }
  • 18-28: notebook_page_open函数。
  • 24-25:创建TfeTextView对象。如果返回NULL,说明发生了错误。然后,它返回给调用者。
  • 26:连接信号“open-response”和处理程序open_response。
  • 27:调用tfe_text_view_open。稍后会发出“open-response”信号,通知打开和读取文件的结果。
  • 1-16: open_response处理程序。
  • 6-8:如果响应代码不是TFE_OPEN_RESPONSE_SUCCESS,则打开和读取新文件失败。然后,需要取消notebook_page_open事先做的事情。实例tv还不是GtkScrolledWindow的子部件。这样的实例具有浮动引用。稍后会解释浮动引用。首先需要调用g_object_ref_sink。然后将浮点引用转换为普通引用。现在调用g_object_unref将引用计数减少1。
  • 9-15:其他一切正常。获取文件名,构建页面的内容并释放文件名。

4 Floating reference

所有构件都派生自GInitiallyUnowned。GObject和GInitiallyUnowned几乎是一样的。区别是这样的。当一个GInitiallyUnowned的实例被创建时,该实例有一个浮动引用并且它的引用计数为零。另一方面,当GObject(不是GInitiallyUnowned)的实例被创建时,没有给出浮动引用。并且该实例具有普通的引用计数而不是浮动引用。它们的后代会继承它们,所以每个widget一开始都有一个浮动引用。例如,非Widget组件类GtkTextBuffer是GObject的直接子类,它没有浮动引用。它被创建时的引用计数是1。

函数g_object_ref_sink将浮动引用转换为普通引用。如果实例没有浮动引用,g_object_ref_sink将引用计数加1。当一个构件作为子构件被添加到另一个构件时,使用它。

GtkTextView *tv = gtk_text_view_new (); // floating reference
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, tv); // tv's reference count is one

在tv作为子构件添加到scr时,需要使用g_object_ref_sink。

g_object_ref_sink (tv);

因此,浮点引用被转换为普通引用。也就是说,浮点引用被删除,引用计数变为1。正因为如此,调用者不需要减少tv的引用计数。如果Object_A不是GInitiallyUnowned的后代,程序如下所示:

Object_A *obj_a = object_a_new (); // reference count is one
GtkScrolledWindow *scr = gtk_scrolled_window_new ();
gtk_scrolled_window_set_child (scr, obj_a); // obj_a's reference count is two
// obj_a is referred by the caller (this program) and scrolled window
g_object_unref (obj_a); // obj_a's reference count is one because the caller no longer refers obj_a.

这个例子告诉我们调用者需要unref obj_a。

如果将g_object_unref用于具有浮动引用的实例,则需要提前将浮动引用转换为普通引用。更多信息请参见GObject API参考。

5 notebook_page_close

 1 void
 2 notebook_page_close (GtkNotebook *nb) {
 3   g_return_if_fail(GTK_IS_NOTEBOOK (nb));
 4 
 5   GtkWidget *win;
 6   int i;
 7 
 8   if (gtk_notebook_get_n_pages (nb) == 1) {
 9     win = gtk_widget_get_ancestor (GTK_WIDGET (nb), GTK_TYPE_WINDOW);
10     gtk_window_destroy(GTK_WINDOW (win));
11   } else {
12     i = gtk_notebook_get_current_page (nb);
13     gtk_notebook_remove_page (GTK_NOTEBOOK (nb), i);
14   }
15 }

该函数关闭当前页面。如果该页面是notebook中唯一的页面,那么该函数会销毁顶层窗口并退出应用程序。

8-10:如果当前页是notebook拥有的唯一一页,则调用gtk_window_destroy销毁顶层窗口。
11-13:否则,删除当前页面。子部件(TfeTextView)也会被销毁。

6 notebook_page_save

 1 static TfeTextView *
 2 get_current_textview (GtkNotebook *nb) {
 3   int i;
 4   GtkWidget *scr;
 5   GtkWidget *tv;
 6 
 7   i = gtk_notebook_get_current_page (nb);
 8   scr = gtk_notebook_get_nth_page (nb, i);
 9   tv = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (scr));
10   return TFE_TEXT_VIEW (tv);
11 }
12 
13 void
14 notebook_page_save (GtkNotebook *nb) {
15   g_return_if_fail(GTK_IS_NOTEBOOK (nb));
16 
17   TfeTextView *tv;
18 
19   tv = get_current_textview (nb);
20   tfe_text_view_save (TFE_TEXT_VIEW (tv));
21 }
  • 13-21: notebook_page_save。
  • 19:获取属于当前页面的TfeTextView
  • 20:调用tfe_text_view_save。
  • 1 - 11: get_current_textview。这个函数获取当前页面的TfeTextView对象。
  • 7:获取当前页面的页码。
  • 8:获取当前页面的子构件scr,它是一个GtkScrolledWindow实例。
  • 9-10:取得scr的子构件件,也就是一个TfeTextView实例,并返回它。

7 file_changed_cb handler

file_changed_cb函数是一个连接到“change-file”信号的处理程序。如果TfeTextView实例中的文件被更改,它会发出这个信号。这个处理程序更改GtkNotebookPage的标签。

 1 static void
 2 file_changed_cb (TfeTextView *tv, GtkNotebook *nb) {
 3   GtkWidget *scr;
 4   GtkWidget *label;
 5   GFile *file;
 6   char *filename;
 7 
 8   file = tfe_text_view_get_file (tv);
 9   scr = gtk_widget_get_parent (GTK_WIDGET (tv));
10   if (G_IS_FILE (file)) {
11     filename = g_file_get_basename (file);
12     g_object_unref (file);
13   } else
14     filename = get_untitled ();
15   label = gtk_label_new (filename);
16   g_free (filename);
17   gtk_notebook_set_tab_label (nb, scr, label);
18 }
  • 8:从tv获取GFile实例。
  • 9:获取tv的父组件GkScrolledWindow实例。
  • 10-12:如果file指向GFile,则将GFile的文件名赋值给filename。然后,取消引用GFile对象文件。
  • 13-14:否则(file为NULL),将untitled字符串赋值给filename。
  • 15-16:用文件名创建一个GtkLabel实例标签,用label设置GtkNotebookPage的标签。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值