让GtkTextView支持undo/redo操作

GtkTextView支持undo/redo操作

 

转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

作者联系方式:李先静 <xianjimli at hotmail dot com>

更新时间: 2007-7-18

 

GtkTextView是一个功能强大的编辑控件,也是GTK+中最为复杂的控件之一。它基于MVC模型设计,GtkTextView是处理用户界面的视图部分,GtkTextBuffer是负责缓冲区管理的模型部分。它除实现了普通编辑控件的功能之外,还支持HTML中的一些基本TAG和图文混排功能,使用也很方便。

 

唯一不爽的就是它不支持undo/redo操作,让人挺郁闷的,偏偏SPEC又要求这个功能。一直没有时间管它,最近进入完善阶段了,今天花了点时间给它加了这个功能。在网上找了一下,发现最新的GTK+也没有提供这个功能,看来GTK官方是没有打算去做了,这次又要自己动手了。

 

至于undo/redo的实现,我倒是有类似的编程经验,其实现虽然不算复杂,不过也要花不少时间。呵,现在忙得很,可没有时间去玩这个,最好是能从其它编辑中移植过来。在网上找了一下,发现GtkSourceView支持undo/redo功能,马上下了一个源码包。

 

解开之后,浏览了一下里面的代码,运气不错:它的undo/redo功能实现得非常独立,只要把gtksourceundomanager.cgtksourceundomanager.hgtksourceview-marshal.h拿过来就行了。

 

剩下的就是要与GtkTextView/GtkTextView结合起来,本来想参考GtkSourceView/GtkSourceBuffer里面做法,不过发现要修改不少地方。我当然不希望修改太多地方,那样可能会引入一些其它BUG。经过几次试验,终于找到一种不修改GtkTextView/GtkTextView的方法。其代码如下:

#include "gtksourceundomanager.h" 
#define GET_UNDO_MANAGER(buffer) (GtkSourceUndoManager*)g_object_get_data(G_OBJECT(buffer), "undo_manager")

static void undo_cb (GtkWidget   *menuitem, GtkTextView* view)
{
    GtkTextBuffer *buffer = NULL;
    GtkSourceUndoManager* undo_manager = NULL;

    g_return_if_fail (GTK_IS_TEXT_VIEW (view));
    
    buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
    undo_manager = GET_UNDO_MANAGER(buffer);
    
    if(gtk_source_undo_manager_can_undo(undo_manager))
    {
        gtk_source_undo_manager_undo(undo_manager);
        gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
                            gtk_text_buffer_get_insert (buffer));
    }
}

static void redo_cb (GtkWidget   *menuitem, GtkTextView* view)
{
    GtkTextBuffer *buffer = NULL;
    GtkSourceUndoManager* undo_manager = NULL;

    g_return_if_fail (GTK_IS_TEXT_VIEW (view));

    buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
    undo_manager = GET_UNDO_MANAGER(buffer);

    if(gtk_source_undo_manager_can_redo(undo_manager))
    {
        gtk_source_undo_manager_redo(undo_manager);
        gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
                            gtk_text_buffer_get_insert (buffer));
    }
}

static void on_populate_popup (GtkTextView* view, GtkMenu *menu, GtkSourceUndoManager* undo_manager)
{
    GtkWidget* menu_item = NULL;

    menu_item = gtk_menu_item_new ();
    gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
    gtk_widget_show (menu_item);

    /* create redo menu_item. */ 
    menu_item = gtk_image_menu_item_new_from_stock ("gtk-redo", NULL);
    g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (redo_cb), view);
    gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
    gtk_widget_set_sensitive (menu_item, gtk_source_undo_manager_can_redo(undo_manager));

    gtk_widget_show (menu_item);

    /* create undo menu_item. */ 
    menu_item = gtk_image_menu_item_new_from_stock ("gtk-undo", NULL);
    g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (undo_cb), view);
    gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
    gtk_widget_set_sensitive (menu_item, gtk_source_undo_manager_can_undo(undo_manager));

    gtk_widget_show (menu_item);

    return;
}

gboolean gtk_text_view_decorate_undo_redo(GtkTextView* view)
{
    GtkTextBuffer *buffer = NULL;
    GtkSourceUndoManager* undo_manager = NULL;

    g_return_val_if_fail(GTK_IS_TEXT_VIEW(view), FALSE);

    buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));

    if((undo_manager = gtk_source_undo_manager_new(GTK_TEXT_BUFFER (buffer))) != NULL)
    {
        g_object_set_data_full(G_OBJECT(buffer), "undo_manager", undo_manager, (GDestroyNotify)g_object_unref);
        g_signal_connect(G_OBJECT(view), "populate_popup",
            G_CALLBACK(on_populate_popup), undo_manager);
    }

    return undo_manager != NULL;
}

 

这里采用类似装饰模式的方法动态的给GtkTextView增加undo/redo功能。通过注册GtkTextViewpopulate_popup事件,向右键弹出菜单中增加undoredo菜单项,然后在菜单的事件处理函数中调用undo_manager的函数。

 

之所以这么简单,完全得益于GtkTextViewGtkSourceView精良的设计。

 

~~end~~

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值