* Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru>
* Copyright 2011 kubtek <kubtek@mail.com>
*
* This file is part of StarDict.
*
* StarDict is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* StarDict is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with StarDict. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <cstring>
#include <iostream>
#include <map>
#include "gtktextviewpango.h"
#include "lib/utils.h"
#include "skin.h"
#include "lib/xml_str.h"
#include "pangoview.h"
class TextPangoWidget : public PangoWidgetBase {
public:
TextPangoWidget();
GtkWidget *widget() { return GTK_WIDGET(textview_); }
GtkTextView *get_text_view() { return textview_; }
void set_font_name(const gchar *fontname);
std::string get_text();
void insert_pango_text(const char *str, const char *where_mark_name,
int char_offset = 0);
void append_pango_text_with_links(const std::string&, const LinksPosList&);
void insert_pango_text_with_links(const std::string& str,
const LinksPosList& links, const char *where_mark_name, int char_offset = 0);
void delete_text(const char *where_mark_name, int char_length,
int char_offset = 0);
void clear();
void append_mark(const char *mark_name, bool left_gravity = true);
void insert_mark(const char *mark_name, const char *where_mark_name,
int char_offset = 0, bool left_gravity = true);
void clone_mark(const char *mark_name, const char *where_mark_name,
bool left_gravity = true);
bool delete_mark(const char *mark);
void append_pixbuf(GdkPixbuf *pixbuf, const char *label);
void insert_pixbuf(GdkPixbuf *pixbuf, const char *label,
const char *where_mark_name, int char_offset = 0);
void append_widget(GtkWidget *widget);
void insert_widget(GtkWidget *widget, const char *where_mark_name,
int char_offset = 0);
void begin_update();
void end_update();
void goto_begin();
void goto_end();
#if GTK_MAJOR_VERSION >= 3
void modify_bg(GtkStateFlags state, const GdkRGBA *color);
#else
void modify_bg(GtkStateType state, const GdkColor *color);
#endif
void indent_region(const char *mark_begin, int char_offset_begin = 0,
const char *mark_end = NULL, int char_offset_end = 0);
void reindent(void);
protected:
void do_set_text(const char *str);
void do_append_text(const char *str);
void do_append_pango_text(const char *str);
void do_set_pango_text(const char *str);
private:
GtkTextView *textview_;
typedef std::list<GtkTextMark *> MarkList;
MarkList marklist_;
class TextBufPos {
public:
GtkTextMark *beg_, *end_;
std::string link_;
TextBufPos(gint beg, gint end, std::string link, TextPangoWidget *widget)
: beg_(NULL), end_(NULL), link_(link), widget_(widget)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
GtkTextIter iter;
gtk_text_buffer_get_iter_at_offset(buffer, &iter, beg);
beg_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
gtk_text_buffer_get_iter_at_offset(buffer, &iter, end);
end_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
}
TextBufPos(const TextBufPos& tbp)
: beg_(NULL), end_(NULL), link_(tbp.link_), widget_(tbp.widget_)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(buffer, &iter, tbp.beg_);
beg_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, tbp.end_);
end_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
}
TextBufPos& operator=(const TextBufPos& tbp)
{
clear();
link_ = tbp.link_;
widget_ = tbp.widget_;
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
GtkTextIter iter;
gtk_text_buffer_get_iter_at_mark(buffer, &iter, tbp.beg_);
beg_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, tbp.end_);
end_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
return *this;
}
~TextBufPos(void)
{
clear();
}
void text_iter_beg(GtkTextIter* iter) const
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
gtk_text_buffer_get_iter_at_mark(buffer, iter, beg_);
}
void text_iter_end(GtkTextIter* iter) const
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
gtk_text_buffer_get_iter_at_mark(buffer, iter, end_);
}
private:
TextBufPos(void);
void clear(void)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
gtk_text_buffer_delete_mark(buffer, beg_);
gtk_text_buffer_delete_mark(buffer, end_);
beg_ = end_ = NULL;
}
private:
TextPangoWidget *widget_;
};
typedef std::vector<TextBufPos> TextBufLinks;
* marks. When user clicks somewhere in the buffer, this list is searched
* for a matching region. If a matching region is found, the user hit a link
* and the text contained in the link_ member is processed. See also the
* find_link member. */
TextBufLinks tb_links_;
GtkTextIter iter_;
SkinCursor hand_cursor_, regular_cursor_;
typedef std::vector<GtkTextTag*> IndentTags;
* indent_tags_[i] - a tag that indent by i+1. Use the get_indent_tag
* method to get the tag desired. */
IndentTags indent_tags_;
static const int indent_size_pxl_ = 10;
static const int left_margin_size_pxl_ = 5;
static const int right_margin_size_pxl_ = 5;
class IndentedRegion {
public:
* be included in the indented region, but the text added to the
* right will not. In order to changes take effect, the region must
* be reindented!
* The beg_ mark must be preceded by a new line character or be at the
* beginning of the buffer, otherwise the first paragraph of the indent
* region will not be indented. */
GtkTextMark *beg_, *end_;
int indent_;
IndentedRegion(const GtkTextIter *beg, const GtkTextIter *end, int indent,
TextPangoWidget *widget)
: beg_(NULL), end_(NULL), indent_(indent), widget_(widget)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
beg_ = gtk_text_buffer_create_mark(buffer, NULL, beg, TRUE);
end_ = gtk_text_buffer_create_mark(buffer, NULL, end, TRUE);
}
IndentedRegion(const IndentedRegion& ir)
: beg_(NULL), end_(NULL), indent_(ir.indent_), widget_(ir.widget_)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
GtkTextIter iter;
ir.text_iter_beg(&iter);
beg_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
ir.text_iter_end(&iter);
end_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
}
IndentedRegion& operator=(const IndentedRegion& ir)
{
clear();
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
widget_ = ir.widget_;
indent_ = ir.indent_;
GtkTextIter iter;
ir.text_iter_beg(&iter);
beg_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
ir.text_iter_end(&iter);
end_ = gtk_text_buffer_create_mark(buffer, NULL, &iter, TRUE);
return *this;
}
~IndentedRegion(void)
{
clear();
}
void text_iter_beg(GtkTextIter* iter) const
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
gtk_text_buffer_get_iter_at_mark(buffer, iter, beg_);
}
void text_iter_end(GtkTextIter* iter) const
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
gtk_text_buffer_get_iter_at_mark(buffer, iter, end_);
}
* explicitly by the IndentedRegions object user, it should not be
* called from a IndentedRegion constructor. */
void indent(void) const
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
GtkTextIter beg, end;
text_iter_beg(&beg);
text_iter_end(&end);
GtkTextTag *tag = widget_->get_indent_tag(indent_);
gtk_text_buffer_apply_tag(buffer, tag, &beg, &end);
}
private:
IndentedRegion(void);
void clear(void)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(widget_->get_text_view());
gtk_text_buffer_delete_mark(buffer, beg_);
gtk_text_buffer_delete_mark(buffer, end_);
beg_ = end_ = NULL;
}
private:
TextPangoWidget *widget_;
};
typedef std::list<IndentedRegion> IndentedRegions;
IndentedRegions indented_regions_;
* functions calls */
int buffer_user_action_cnt;
static gboolean on_mouse_move(GtkWidget *, GdkEventMotion *, gpointer);
static gboolean on_button_release(GtkWidget *, GdkEventButton *, gpointer);
static void on_destroy(GtkWidget *widget, gpointer);
bool goto_mark(const char *where_mark_name, int char_offset = 0);
TextBufLinks::const_iterator find_link(gint x, gint y);
MarkList::iterator find_mark(const char* mark);
GtkTextTag* get_indent_tag(int indent);
void indent_region(const GtkTextIter *begin, const GtkTextIter *end,
int indent_rel = 1);
#ifdef DEBUG
public:
void print_indent_regions(void);
void print_tb_links(void);
void print_marks(void);
#endif
};
class LabelPangoWidget : public PangoWidgetBase {
public:
LabelPangoWidget();
GtkWidget *widget() { return GTK_WIDGET(label_); }
void set_font_name(const gchar *fontname);
std::string get_text();
void insert_pango_text(const char *str, const char *where_mark_name,
int char_offset = 0);
void insert_pango_text_with_links(const std::string& str,
const LinksPosList& links, const char *where_mark_name, int char_offset = 0);
void delete_text(const char *where_mark_name, int char_length,
int char_offset = 0);
void clear();
void append_mark(const char *mark_name, bool left_gravity = true);
void insert_mark(const char *mark_name, const char *where_mark_name,
int char_offset = 0, bool left_gravity = true);
void clone_mark(const char *mark_name, const char *where_mark_name,
bool left_gravity = true);
bool delete_mark(const char *mark_name);
void append_pixbuf(GdkPixbuf *pixbuf, const char *label);
void insert_pixbuf(GdkPixbuf *pixbuf, const char *label,
const char *where_mark_name, int char_offset = 0);
void append_widget(GtkWidget *widget);
void insert_widget(GtkWidget *widget, const char *where_mark_name,
int char_offset = 0);
void begin_update();
void end_update();
#if GTK_MAJOR_VERSION >= 3
void modify_bg(GtkStateFlags state, const GdkRGBA *color);
#else
void modify_bg(GtkStateType state, const GdkColor *color);
#endif
protected:
void do_set_text(const char *str);
void do_append_text(const char *str);
void do_append_pango_text(const char *str);
void do_set_pango_text(const char *str);
private:
void flush_label(void);
std::string get_pixbuf_replace_pango_text(GdkPixbuf *pixbuf, const char *label);
std::string get_widget_replace_pango_text(GtkWidget *widget);
size_t get_index_by_offset(const char *where_mark_name, int char_offset);
struct Mark;
static bool position_less(const Mark& mark1, const Mark& mark2);
static bool position_equal(const Mark& mark1, const Mark& mark2);
static bool position_more(const Mark& mark1, const Mark& mark2);
static bool position_less(Mark * mark1, Mark * mark2);
private:
* across text modifications.
*
* In string a mark points between characters.
* We have a string "01234", marks m_l, m_r point before the character 2,
* have left and right gravity respectively.
* |0|1|2|3|4|
* ^
* |
* m_l
* m_r
*
* after inserting string "xx" after 1 we have:
* |0|1|x|x|2|3|4|
* ^ ^
* | |
* m_l m_r
*
* Back to the initial string, now remove substring "123", then we have:
* |0|4|
* ^
* |
* m_l
* m_r
*
* */
struct Mark
{
typedef std::string Name;
typedef std::list<Mark*> MarksList;
* Must be in range [0, len], where len - pango text length.
* We count raw characters, which may be part of utf8-encoded char. */
size_t index_;
bool left_gravity_;
Name name_;
MarksList::iterator iter_;
Mark(size_t index, bool left_gravity, const Name& name = "");
friend bool LabelPangoWidget::position_less(const Mark& mark1,
const Mark& mark2);
friend bool LabelPangoWidget::position_equal(const Mark& mark1,
const Mark& mark2);
friend bool LabelPangoWidget::position_more(const Mark& mark1,
const Mark& mark2);
friend bool LabelPangoWidget::position_less(Mark * mark1, Mark * mark2);
private:
Mark(void);
};
class Marks
{
public:
typedef Mark::MarksList MarksList;
typedef std::map<Mark::Name, Mark*> MarksNamesMap;
public:
~Marks(void);
Mark* add_mark(size_t index, bool left_gravity);
Mark* add_mark(size_t index, bool left_gravity, const Mark::Name& name);
bool delete_mark(Mark *pMark);
bool delete_mark(const Mark::Name& name);
* inserted
* len - length of the inserted text in raw chars */
void insert_text(size_t where, size_t len);
* has been deleted.
* len - length of the inserted text in raw chars */
void delete_text(size_t where, size_t len);
void delete_all_text(void);
void clear(void);
Mark* find_mark(const Mark::Name& name) const;
private:
* Mark name if present must be unique across the Marks object.
* Any number of marks may be anonymous.
* Marks class consumers should refer to marks by their names or
* Mark structure pointers.
* A Mark object is always allocated in the heap with a help of new
* operator. A Mark object is not copied and the pointer to the object
* remains consistent until the mark is deleted explicitly by the
* delete_mark method and similar or implicitly by the clear method or
* the Marks class destructor. */
MarksList marks_list_;
* contains only named marks. */
MarksNamesMap marks_names_map_;
#ifdef DEBUG
public:
void print_string_with_marks(const std::string& pango_text);
#endif
};
private:
GtkLabel *label_;
GtkWidget *viewport_;
std::string pango_text_;
* then if update_mode_ is false the widget is updated. */
bool update_mode_;
Marks marks_;
#ifdef DEBUG
public:
void print_string_with_marks(void);
#endif
};
LabelPangoWidget::Mark::Mark(size_t index, bool left_gravity, const Name& name)
:
index_(index),
left_gravity_(left_gravity),
name_(name)
{
}
bool LabelPangoWidget::position_less(const LabelPangoWidget::Mark& mark1,
const LabelPangoWidget::Mark& mark2)
{
if(mark1.index_ < mark2.index_)
return true;
if(mark1.index_ == mark2.index_)
if(mark1.left_gravity_ && !mark2.left_gravity_)
return true;
return false;
}
bool LabelPangoWidget::position_equal(const LabelPangoWidget::Mark& mark1,
const LabelPangoWidget::Mark& mark2)
{
return mark1.index_ == mark2.index_
&& mark1.left_gravity_ == mark2.left_gravity_;
}
bool LabelPangoWidget::position_more(const LabelPangoWidget::Mark& mark1,
const LabelPangoWidget::Mark& mark2)
{
return position_less(mark2, mark1);
}
bool LabelPangoWidget::position_less(LabelPangoWidget::Mark * mark1,
LabelPangoWidget::Mark * mark2)
{
return position_less(*mark1, *mark2);
}
LabelPangoWidget::Marks::~Marks(void)
{
clear();
}
LabelPangoWidget::Mark* LabelPangoWidget::Marks::add_mark(size_t index,
bool left_gravity)
{
#ifdef DDEBUG
std::cout << "add_mark anonymous" << std::endl;
#endif
std::unique_ptr<Mark> pMark(new Mark(index, left_gravity));
pMark->iter_ = marks_list_.insert(marks_list_.end(), pMark.get());
return pMark.release();
}
LabelPangoWidget::Mark* LabelPangoWidget::Marks::add_mark(size_t index,
bool left_gravity, const Mark::Name& name)
{
#ifdef DDEBUG
std::cout << "add_mark " << name << std::endl;
#endif
if(name.empty())
return add_mark(index, left_gravity);
std::unique_ptr<Mark> pMark(new Mark(index, left_gravity, name));
std::pair<MarksNamesMap::iterator, bool> res
= marks_names_map_.insert(MarksNamesMap::value_type(name, pMark.get()));
if(!res.second) {
g_warning("mark %s already exists", name.c_str());
return res.first->second;
}
pMark->iter_ = marks_list_.insert(marks_list_.end(), pMark.get());
return pMark.release();
}
bool LabelPangoWidget::Marks::delete_mark(Mark *pMark)
{
#ifdef DDEBUG
std::cout << "delete_mark unspecified" << std::endl;
#endif
if(!pMark) {
g_warning("mark does not exist");
return false;
}
if(!pMark->name_.empty()) {
if(marks_names_map_.erase(pMark->name_) != 1)
g_warning("map corrupted, mark %s is not found in the map",
pMark->name_.c_str());
}
marks_list_.erase(pMark->iter_);
delete pMark;
return true;
}
bool LabelPangoWidget::Marks::delete_mark(const Mark::Name& name)
{
#ifdef DDEBUG
std::cout << "delete_mark " << name << std::endl;
#endif
MarksNamesMap::iterator it = marks_names_map_.find(name);
if(it == marks_names_map_.end()) {
g_warning("mark %s does not exist", name.c_str());
return false;
}
Mark *pMark = it->second;
marks_names_map_.erase(it);
marks_list_.erase(pMark->iter_);
delete pMark;
return true;
}
void LabelPangoWidget::Marks::insert_text(size_t where, size_t len)
{
Mark mark_beg(where, true);
MarksList::iterator it;
for(it = marks_list_.begin(); it != marks_list_.end(); ++it)
if(position_less(mark_beg, **it))
(*it)->index_ += len;
}
void LabelPangoWidget::Marks::delete_text(size_t where, size_t len)
{
Mark mark_beg(where, true), mark_end(where+len, false);
MarksList::iterator it;
for(it = marks_list_.begin(); it != marks_list_.end(); ++it)
if(position_less(mark_beg, **it)) {
if(position_less(**it, mark_end))
(*it)->index_ = where;
else
(*it)->index_ -= len;
}
}
void LabelPangoWidget::Marks::delete_all_text(void)
{
MarksList::iterator it;
for(it = marks_list_.begin(); it != marks_list_.end(); ++it)
(*it)->index_ = 0;
}
void LabelPangoWidget::Marks::clear(void)
{
marks_names_map_.clear();
MarksList::iterator it;
for(it = marks_list_.begin(); it != marks_list_.end(); ++it)
delete *it;
marks_list_.clear();
}
LabelPangoWidget::Mark* LabelPangoWidget::Marks::find_mark(
const Mark::Name& name) const
{
MarksNamesMap::const_iterator it = marks_names_map_.find(name);
if(it == marks_names_map_.end())
return NULL;
else
return it->second;
}
#ifdef DEBUG
void LabelPangoWidget::Marks::print_string_with_marks(
const std::string& pango_text)
{
MarksList t_marks_list(marks_list_);
t_marks_list.sort<bool (*)(Mark*, Mark*)>(LabelPangoWidget::position_less);
std::string str;
std::string mark_tag;
Marks::MarksList::const_iterator it;
size_t ind1, ind2;
for(ind1 = 0, it = t_marks_list.begin(); it != t_marks_list.end(); ++it) {
mark_tag = "<";
if((*it)->name_.empty())
mark_tag += "anonymous mark ";
else
mark_tag += (*it)->name_ + " ";
mark_tag += (*it)->left_gravity_ ? "gr=l" : "gr=r";
mark_tag += ">";
ind2 = (*it)->index_;
if(ind1<ind2)
str.append(pango_text, ind1, ind2-ind1);
str.append(mark_tag);
ind1 = ind2;
}
ind2 = pango_text.length();
if(ind1<ind2)
str.append(pango_text, ind1, ind2-ind1);
std::cout << "\nlabel text with marks:\n" << str;
std::cout.flush();
}
#endif
void PangoWidgetBase::begin_update()
{
update_ = true;
}
void PangoWidgetBase::end_update()
{
if (update_) {
update_ = false;
flush();
}
}
void PangoWidgetBase::append_text(const char *str)
{
if (update_) {
gchar *mark = g_markup_escape_text(str, -1);
cache_ += mark;
g_free(mark);
} else {
do_append_text(str);
}
}
void PangoWidgetBase::append_pango_text(const char *str)
{
if (update_)
cache_ += str;
else
do_append_pango_text(str);
}
void PangoWidgetBase::append_pango_text_with_links(const std::string& str,
const LinksPosList&)
{
append_pango_text(str.c_str());
}
void PangoWidgetBase::set_pango_text(const char *str)
{
if (update_) {
cache_ = str;
append_cache_ = false;
} else
do_set_pango_text(str);
}
TextPangoWidget::TextPangoWidget()
{
hand_cursor_.reset(gdk_cursor_new(GDK_HAND2));
regular_cursor_.reset(gdk_cursor_new(GDK_XTERM));
textview_ = GTK_TEXT_VIEW(gtk_text_view_new());
gtk_widget_show(GTK_WIDGET(textview_));
gtk_text_view_set_editable(textview_, FALSE);
gtk_text_view_set_cursor_visible(textview_, FALSE);
gtk_text_view_set_wrap_mode(textview_, GTK_WRAP_WORD_CHAR);
gtk_text_view_set_left_margin(textview_, left_margin_size_pxl_);
gtk_text_view_set_right_margin(textview_, right_margin_size_pxl_);
g_signal_connect(textview_, "button-release-event",
G_CALLBACK(on_button_release), this);
g_signal_connect(textview_, "motion-notify-event",
G_CALLBACK(on_mouse_move), this);
g_signal_connect(textview_, "destroy",
G_CALLBACK(on_destroy), this);
gtk_text_buffer_get_iter_at_offset(gtk_text_view_get_buffer(textview_),
&iter_, 0);
scroll_win_ = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
gtk_widget_show(GTK_WIDGET(scroll_win_));
gtk_scrolled_window_set_policy(scroll_win_,
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(scroll_win_), GTK_WIDGET(textview_));
gtk_scrolled_window_set_shadow_type(scroll_win_, GTK_SHADOW_IN);
buffer_user_action_cnt = 0;
}
#if GTK_MAJOR_VERSION >= 3
void LabelPangoWidget::modify_bg(GtkStateFlags state, const GdkRGBA *color)
{
gtk_widget_override_background_color(viewport_, state, color);
}
#else
void LabelPangoWidget::modify_bg(GtkStateType state, const GdkColor *color)
{
gtk_widget_modify_bg(viewport_, state, color);
}
#endif
#if GTK_MAJOR_VERSION >= 3
void TextPangoWidget::modify_bg(GtkStateFlags state, const GdkRGBA *color)
{
gtk_widget_override_background_color(widget(), state, color);
}
#else
void TextPangoWidget::modify_bg(GtkStateType state, const GdkColor *color)
{
gtk_widget_modify_base(widget(), state, color);
}
#endif
LabelPangoWidget::LabelPangoWidget()
{
update_mode_ = false;
label_ = GTK_LABEL(gtk_label_new(NULL));
gtk_label_set_justify(label_, GTK_JUSTIFY_LEFT);
scroll_win_ = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
gtk_scrolled_window_set_shadow_type(scroll_win_, GTK_SHADOW_NONE);
gtk_scrolled_window_set_policy(scroll_win_, GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
viewport_ =
gtk_viewport_new(gtk_scrolled_window_get_hadjustment(scroll_win_),
gtk_scrolled_window_get_vadjustment(scroll_win_));
gtk_widget_add_events(viewport_, GDK_BUTTON1_MOTION_MASK);
gtk_widget_add_events(viewport_, GDK_BUTTON_RELEASE_MASK);
gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport_), GTK_SHADOW_NONE);
gtk_container_add(GTK_CONTAINER(scroll_win_), viewport_);
gtk_container_add(GTK_CONTAINER(viewport_), GTK_WIDGET(label_));
}
void TextPangoWidget::begin_update()
{
gtk_text_buffer_begin_user_action(gtk_text_view_get_buffer(textview_));
++buffer_user_action_cnt;
PangoWidgetBase::begin_update();
}
void LabelPangoWidget::begin_update()
{
update_mode_ = true;
PangoWidgetBase::begin_update();
}
void TextPangoWidget::end_update()
{
PangoWidgetBase::end_update();
--buffer_user_action_cnt;
gtk_text_buffer_end_user_action(gtk_text_view_get_buffer(textview_));
}
void LabelPangoWidget::end_update()
{
PangoWidgetBase::end_update();
update_mode_ = false;
flush_label();
}
PangoWidgetBase *PangoWidgetBase::create(bool autoresize)
{
if (!autoresize)
return new TextPangoWidget;
else
return new LabelPangoWidget;
}
void TextPangoWidget::do_set_text(const char *text)
{
std::string text2(text);
clear();
goto_begin();
do_append_text(text2.c_str());
}
void LabelPangoWidget::do_set_text(const char *text)
{
gchar *mstr = g_markup_escape_text(text, -1);
do_set_pango_text(mstr);
g_free(mstr);
}
void PangoWidgetBase::set_text(const char *str)
{
if (update_) {
gchar *mark = g_markup_escape_text(str, -1);
cache_ = mark;
g_free(mark);
append_cache_ = false;
} else {
do_set_text(str);
}
}
void TextPangoWidget::do_append_text(const char *str)
{
goto_end();
gtk_text_buffer_insert(gtk_text_view_get_buffer(textview_),
&iter_, str, strlen(str));
}
void LabelPangoWidget::do_append_text(const char *str)
{
gchar *mstr = g_markup_escape_text(str, -1);
do_append_pango_text(mstr);
g_free(mstr);
}
void TextPangoWidget::do_append_pango_text(const char *str)
{
goto_end();
gtk_text_buffer_insert_markup(gtk_text_view_get_buffer(textview_),
&iter_, str);
}
void TextPangoWidget::do_set_pango_text(const char *str)
{
std::string str2(str);
clear();
goto_begin();
do_append_pango_text(str2.c_str());
}
void LabelPangoWidget::do_set_pango_text(const char *str)
{
marks_.delete_all_text();
marks_.insert_text(0, strlen(str));
pango_text_ = str;
if(!update_mode_)
flush_label();
}
void LabelPangoWidget::do_append_pango_text(const char *str)
{
marks_.insert_text(pango_text_.length(), strlen(str));
pango_text_ += str;
if(!update_mode_)
flush_label();
}
void TextPangoWidget::append_mark(const char *mark_name, bool left_gravity)
{
GtkTextBuffer *buffer=gtk_text_view_get_buffer(textview_);
flush();
goto_end();
marklist_.push_back(gtk_text_buffer_create_mark(buffer, mark_name, &iter_,
left_gravity ? TRUE : FALSE));
}
void LabelPangoWidget::append_mark(const char *mark_name, bool left_gravity)
{
flush();
marks_.add_mark(pango_text_.length(), left_gravity, mark_name);
}
void TextPangoWidget::insert_mark(const char *mark_name,
const char *where_mark_name, int char_offset, bool left_gravity)
{
flush();
goto_mark(where_mark_name, char_offset);
GtkTextBuffer *buffer=gtk_text_view_get_buffer(textview_);
marklist_.push_back(gtk_text_buffer_create_mark(buffer, mark_name, &iter_,
left_gravity ? TRUE : FALSE));
}
void LabelPangoWidget::insert_mark(const char *mark_name,
const char *where_mark_name, int char_offset, bool left_gravity)
{
flush();
size_t ind = get_index_by_offset(where_mark_name, char_offset);
if(ind != std::string::npos) {
marks_.add_mark(ind, left_gravity, mark_name);
} else
g_warning("label::insert_mark incorrect index. Mark = %s", mark_name);
}
void TextPangoWidget::clone_mark(const char *mark_name,
const char *where_mark_name, bool left_gravity)
{
insert_mark(mark_name, where_mark_name, 0, left_gravity);
}
void LabelPangoWidget::clone_mark(const char *mark_name,
const char *where_mark_name, bool left_gravity)
{
flush();
Mark *pMark = marks_.find_mark(where_mark_name);
if(pMark == NULL) {
g_warning("Mark \"%s\" does not exist", where_mark_name);
return;
}
marks_.add_mark(pMark->index_, left_gravity, mark_name);
}
bool TextPangoWidget::delete_mark(const char *mark)
{
MarkList::iterator it = find_mark(mark);
if(it != marklist_.end()) {
GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview_);
gtk_text_buffer_delete_mark(buffer, *it);
marklist_.erase(it);
return true;
}
return false;
}
bool LabelPangoWidget::delete_mark(const char *mark_name)
{
return marks_.delete_mark(mark_name);
}
void PangoWidgetBase::clear(void)
{
cache_.clear();
append_cache_ = true;
}
#if 0
void collectTags(GtkTextTag *tag, gpointer data)
{
std::vector<GtkTextTag *>* allTags = (std::vector<GtkTextTag *>*)data;
allTags->push_back(tag);
}
#endif
void TextPangoWidget::clear()
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview_);
MarkList::const_iterator it;
for (it = marklist_.begin(); it != marklist_.end(); ++it)
gtk_text_buffer_delete_mark(buffer, *it);
marklist_.clear();
tb_links_.clear();
GtkTextIter start, end;
gtk_text_buffer_get_bounds(buffer, &start, &end);
gtk_text_buffer_remove_all_tags(buffer, &start, &end);
gtk_text_buffer_delete(buffer, &start, &end);
scroll_to(0);
indented_regions_.clear();
goto_begin();
indent_tags_.clear();
PangoWidgetBase::clear();
#if 0
GtkTextTagTable * tagTable = gtk_text_buffer_get_tag_table(buffer);
gint tagcnt = gtk_text_tag_table_get_size(tagTable);
g_print("number of tags: %d\n", tagcnt);
#endif
#if 0
std::vector<GtkTextTag *> allTags;
allTags.reserve(tagcnt);
gtk_text_tag_table_foreach(tagTable, collectTags, (gpointer)&allTags);
g_print("number of tags collected: %lu\n", (unsigned long)allTags.size());
for(size_t i = 0; i<allTags.size(); ++i) {
}
#endif
for(int i=0; i<buffer_user_action_cnt; ++i)
gtk_text_buffer_end_user_action(buffer);
* gtk_text_buffer_get_tag_table(buffer)
* otherwise the tag table will grow and grow slowing down buffer update.
* Unfortunately I do not know other was to clear the table,
* gtk_text_buffer_remove_all_tags does not help. The tags in question are
* added by the gtk_text_buffer_real_insert_markup function. */
GtkTextBuffer *new_buffer = gtk_text_buffer_new(NULL);
gtk_text_view_set_buffer(textview_, new_buffer);
g_object_unref(G_OBJECT(new_buffer));
for(int i=0; i<buffer_user_action_cnt; ++i)
gtk_text_buffer_begin_user_action(new_buffer);
}
void LabelPangoWidget::clear()
{
pango_text_.clear();
marks_.clear();
PangoWidgetBase::clear();
flush_label();
}
void TextPangoWidget::goto_begin()
{
gtk_text_buffer_get_iter_at_offset(
gtk_text_view_get_buffer(textview_), &iter_, 0
);
}
void TextPangoWidget::goto_end()
{
gtk_text_buffer_get_iter_at_offset(gtk_text_view_get_buffer(textview_),
&iter_, -1);
}
std::string TextPangoWidget::get_text()
{
std::string res;
GtkTextIter start, end;
GtkTextBuffer *buffer=gtk_text_view_get_buffer(textview_);
gtk_text_buffer_get_bounds(buffer, &start, &end);
gchar *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
res = text;
g_free(text);
return res;
}
std::string LabelPangoWidget::get_text()
{
return pango_text_;
}
void PangoWidgetBase::insert_text(const char *str, const char *where_mark_name,
int char_offset)
{
gchar *mark = g_markup_escape_text(str, -1);
insert_pango_text(mark, where_mark_name, char_offset);
g_free(mark);
}
void TextPangoWidget::insert_pango_text(const char *str, const char *where_mark_name,
int char_offset)
{
flush();
goto_mark(where_mark_name, char_offset);
gtk_text_buffer_insert_markup(gtk_text_view_get_buffer(textview_), &iter_, str);
}
void LabelPangoWidget::insert_pango_text(const char *str,
const char *where_mark_name, int char_offset)
{
flush();
size_t ind = get_index_by_offset(where_mark_name, char_offset);
if(ind != std::string::npos) {
pango_text_.insert(ind, str);
marks_.insert_text(ind, strlen(str));
} else {
return;
}
if(!update_mode_)
flush_label();
}
void TextPangoWidget::append_pango_text_with_links(const std::string& str,
const LinksPosList& links)
{
if (links.empty()) {
append_pango_text(str.c_str());
return;
}
flush();
goto_end();
gint beg = gtk_text_iter_get_offset(&iter_);
gtk_text_buffer_insert_markup(gtk_text_view_get_buffer(textview_),
&iter_, str.c_str());
for (LinksPosList::const_iterator it = links.begin();
it != links.end(); ++it) {
tb_links_.push_back(
TextBufPos(beg + it->pos_, beg + it->pos_ + it->len_, it->link_, this));
}
}
void TextPangoWidget::insert_pango_text_with_links(const std::string& str,
const LinksPosList& links, const char *where_mark_name, int char_offset)
{
if(links.empty()) {
insert_pango_text(str.c_str(), where_mark_name, char_offset);
return;
}
flush();
if(!goto_mark(where_mark_name, char_offset))
return;
gint beg = gtk_text_iter_get_offset(&iter_);
gtk_text_buffer_insert_markup(gtk_text_view_get_buffer(textview_), &iter_,
str.c_str());
for (LinksPosList::const_iterator it = links.begin();
it != links.end(); ++it) {
tb_links_.push_back(
TextBufPos(beg + it->pos_, beg + it->pos_ + it->len_, it->link_, this));
}
}
void LabelPangoWidget::insert_pango_text_with_links(const std::string& str,
const LinksPosList& links, const char *where_mark_name, int char_offset)
{
insert_pango_text(str.c_str(), where_mark_name, char_offset);
}
void TextPangoWidget::delete_text(const char *where_mark_name, int char_length,
int char_offset)
{
flush();
if(!goto_mark(where_mark_name, char_offset))
return;
GtkTextIter end = iter_;
gtk_text_iter_forward_chars(&end, char_length);
gtk_text_buffer_delete(gtk_text_view_get_buffer(textview_), &iter_, &end);
}
void LabelPangoWidget::delete_text(const char *where_mark_name, int char_length,
int char_offset)
{
if(char_length < 0) {
g_warning("incorrect char_length");
return;
}
if(char_length == 0)
return;
size_t beg_ind = get_index_by_offset(where_mark_name, char_offset);
if(beg_ind == std::string::npos)
return;
const char * beg = pango_text_.c_str();
const char * end = xml_utf8_offset_to_pointer(beg + beg_ind, char_length - 1);
if(!end || !*end) {
g_warning("incorrect char_length");
return;
}
end = xml_utf8_end_of_char(end);
if(!end) {
g_warning("incorrect char_length");
return;
}
size_t end_ind = end - beg;
size_t len = 1 + end_ind - beg_ind;
pango_text_.erase(beg_ind, len);
marks_.delete_text(beg_ind, len);
if(!update_mode_)
flush_label();
}
void TextPangoWidget::append_pixbuf(GdkPixbuf *pixbuf, const char *label)
{
flush();
goto_end();
gtk_text_buffer_insert_pixbuf (gtk_text_view_get_buffer(textview_), &iter_,
pixbuf);
}
void LabelPangoWidget::append_pixbuf(GdkPixbuf *pixbuf, const char *label)
{
append_pango_text(get_pixbuf_replace_pango_text(pixbuf, label).c_str());
}
void TextPangoWidget::insert_pixbuf(GdkPixbuf *pixbuf, const char *label,
const char *where_mark_name, int char_offset)
{
flush();
if(!goto_mark(where_mark_name, char_offset))
return;
gtk_text_buffer_insert_pixbuf(gtk_text_view_get_buffer(textview_), &iter_,
pixbuf);
}
void LabelPangoWidget::insert_pixbuf(GdkPixbuf *pixbuf, const char *label,
const char *where_mark_name, int char_offset)
{
insert_pango_text(get_pixbuf_replace_pango_text(pixbuf, label).c_str(),
where_mark_name, char_offset);
}
void TextPangoWidget::set_font_name(const gchar *fontname)
{
if (textview_) {
std::string family;
gint size = 0;
fontname_to_family_and_size(fontname, family, size);
PangoFontDescription *font_desc = pango_font_description_new();
if (!family.empty()) {
pango_font_description_set_family(font_desc, family.c_str());
}
if (size != 0) {
pango_font_description_set_size(font_desc, size);
}
#if GTK_MAJOR_VERSION >= 3
gtk_widget_override_font(GTK_WIDGET(textview_), font_desc);
#else
gtk_widget_modify_font(GTK_WIDGET(textview_), font_desc);
#endif
pango_font_description_free(font_desc);
}
}
void LabelPangoWidget::set_font_name(const gchar *fontname)
{
}
void TextPangoWidget::append_widget(GtkWidget *widget)
{
flush();
goto_end();
GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor (
gtk_text_view_get_buffer(textview_), &iter_);
gtk_text_view_add_child_at_anchor (textview_, widget, anchor);
}
void LabelPangoWidget::append_widget(GtkWidget *widget)
{
append_pango_text(get_widget_replace_pango_text(widget).c_str());
if (widget) {
gtk_widget_destroy(widget);
}
}
void TextPangoWidget::insert_widget(GtkWidget *widget,
const char *where_mark_name, int char_offset)
{
flush();
if(!goto_mark(where_mark_name, char_offset))
return;
GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor (
gtk_text_view_get_buffer(textview_), &iter_);
gtk_text_view_add_child_at_anchor (textview_, widget, anchor);
}
void LabelPangoWidget::insert_widget(GtkWidget *widget,
const char *where_mark_name, int char_offset)
{
insert_pango_text(get_widget_replace_pango_text(widget).c_str(),
where_mark_name, char_offset);
if (widget) {
gtk_widget_destroy(widget);
}
}
void TextPangoWidget::indent_region(const char *mark_begin,
int char_offset_begin, const char *mark_end, int char_offset_end)
{
flush();
GtkTextBuffer *buffer=gtk_text_view_get_buffer(textview_);
GtkTextMark *tm_begin = gtk_text_buffer_get_mark(buffer, mark_begin),
*tm_end = gtk_text_buffer_get_mark(buffer, mark_end);
GtkTextIter tit_begin, tit_end;
if(tm_begin == NULL) {
g_warning("Mark \"%s\" not found", mark_begin);
return;
} else {
gtk_text_buffer_get_iter_at_mark(buffer, &tit_begin, tm_begin);
gtk_text_iter_forward_chars(&tit_begin, char_offset_begin);
}
if(tm_end == NULL) {
goto_end();
tit_end = iter_;
} else {
gtk_text_buffer_get_iter_at_mark(buffer, &tit_end, tm_end);
gtk_text_iter_forward_chars(&tit_end, char_offset_end);
}
indent_region(&tit_begin, &tit_end);
}
* inserted after indentation. */
void TextPangoWidget::reindent(void)
{
IndentedRegions::iterator it;
for(it = indented_regions_.begin(); it != indented_regions_.end(); ++it) {
it->indent();
}
}
void PangoWidgetBase::flush(void)
{
if (!cache_.empty()) {
if(append_cache_)
do_append_pango_text(cache_.c_str());
else
do_set_pango_text(cache_.c_str());
cache_.clear();
}
append_cache_ = true;
}
bool TextPangoWidget::goto_mark(const char *where_mark_name, int char_offset)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview_);
GtkTextMark *tm_where = gtk_text_buffer_get_mark(buffer, where_mark_name);
if(!tm_where) {
g_warning("Mark \"%s\" does not exist", where_mark_name);
return false;
}
gtk_text_buffer_get_iter_at_mark(buffer, &iter_, tm_where);
gtk_text_iter_forward_chars(&iter_, char_offset);
return true;
}
TextPangoWidget::TextBufLinks::const_iterator TextPangoWidget::find_link(gint x,
gint y)
{
GtkTextIter iter;
GtkTextIter beg, end;
gtk_text_view_get_iter_at_location(textview_, &iter, x, y);
TextBufLinks::const_iterator it;
for (it = tb_links_.begin(); it != tb_links_.end(); ++it) {
it->text_iter_beg(&beg);
it->text_iter_end(&end);
if (gtk_text_iter_compare(&iter, &beg) < 0)
return tb_links_.end();
if (gtk_text_iter_compare(&beg, &iter) <= 0
&& gtk_text_iter_compare(&iter, &end) < 0)
break;
}
return it;
}
TextPangoWidget::MarkList::iterator TextPangoWidget::find_mark(const char* mark)
{
if(mark == NULL)
return marklist_.end();
MarkList::iterator it;
for (it = marklist_.begin(); it != marklist_.end(); ++it) {
if(strcmp(gtk_text_mark_get_name(*it), mark) == 0)
return it;
}
return marklist_.end();
}
GtkTextTag* TextPangoWidget::get_indent_tag(int indent)
{
if(indent <= 0 || indent > 20) {
g_warning("incorrect indent %d", indent);
return NULL;
}
if(indent_tags_.size() < static_cast<size_t>(indent))
indent_tags_.resize(indent, NULL);
if(!indent_tags_[indent-1])
indent_tags_[indent-1] = gtk_text_buffer_create_tag (
gtk_text_view_get_buffer(textview_), NULL,
"left_margin", left_margin_size_pxl_ + indent_size_pxl_*indent, NULL);
return indent_tags_[indent-1];
}
void TextPangoWidget::indent_region(const GtkTextIter *begin,
const GtkTextIter *end, int indent_rel)
{
GtkTextIter new_reg_beg = *begin, new_reg_end = *end;
GtkTextIter lst_reg_beg, lst_reg_end;
GtkTextIter *sub_reg_beg, *sub_reg_end;
gtk_text_iter_order(&new_reg_beg, &new_reg_end);
if(gtk_text_iter_compare(&new_reg_beg, &new_reg_end) == 0)
return;
IndentedRegions::iterator it;
for(it = indented_regions_.begin(); it != indented_regions_.end(); ++it) {
it->text_iter_end(&lst_reg_end);
if(gtk_text_iter_compare(&new_reg_beg, &lst_reg_end) < 0)
break;
}
while(it != indented_regions_.end()) {
it->text_iter_beg(&lst_reg_beg);
it->text_iter_end(&lst_reg_end);
if(gtk_text_iter_compare(&lst_reg_beg, &lst_reg_end) >= 0) {
it = indented_regions_.erase(it);
continue;
}
sub_reg_beg = gtk_text_iter_min(&new_reg_beg, &lst_reg_beg);
sub_reg_end = gtk_text_iter_min(&new_reg_end, &lst_reg_beg);
if(gtk_text_iter_compare(sub_reg_beg, sub_reg_end) < 0) {
IndentedRegion ir(sub_reg_beg, sub_reg_end, indent_rel, this);
ir.indent();
indented_regions_.insert(it, ir);
}
sub_reg_beg = &lst_reg_beg;
sub_reg_end = gtk_text_iter_max(&new_reg_beg, &lst_reg_beg);
if(gtk_text_iter_compare(sub_reg_beg, sub_reg_end) < 0) {
IndentedRegion ir(sub_reg_beg, sub_reg_end, it->indent_, this);
ir.indent();
indented_regions_.insert(it, ir);
}
sub_reg_beg = gtk_text_iter_max(&new_reg_beg, &lst_reg_beg);
sub_reg_end = gtk_text_iter_min(&new_reg_end, &lst_reg_end);
if(gtk_text_iter_compare(sub_reg_beg, sub_reg_end) < 0) {
IndentedRegion ir(sub_reg_beg, sub_reg_end, it->indent_ + indent_rel,
this);
ir.indent();
indented_regions_.insert(it, ir);
}
sub_reg_beg = gtk_text_iter_min(&new_reg_end, &lst_reg_end);
sub_reg_end = &lst_reg_end;
if(gtk_text_iter_compare(sub_reg_beg, sub_reg_end) < 0) {
IndentedRegion ir(sub_reg_beg, sub_reg_end, it->indent_, this);
ir.indent();
indented_regions_.insert(it, ir);
}
* and should be removed. */
it = indented_regions_.erase(it);
new_reg_beg = *gtk_text_iter_min(&lst_reg_end, &new_reg_end);
if(gtk_text_iter_compare(&new_reg_beg, &new_reg_end) >= 0)
break;
}
if(gtk_text_iter_compare(&new_reg_beg, &new_reg_end) < 0) {
IndentedRegion ir(&new_reg_beg, &new_reg_end, indent_rel, this);
ir.indent();
indented_regions_.push_back(ir);
}
}
void LabelPangoWidget::flush_label(void)
{
scroll_to(0);
gtk_label_set_markup(label_, "");
gtk_widget_set_size_request(GTK_WIDGET(label_), -1, -1);
gtk_label_set_line_wrap(label_, FALSE);
gtk_label_set_markup(label_, pango_text_.c_str());
}
std::string LabelPangoWidget::get_pixbuf_replace_pango_text(GdkPixbuf *pixbuf,
const char *label)
{
std::string res;
if (label) {
gchar *markup = g_markup_printf_escaped(
"<span foreground=\"red\">[Image:%s]</span>", label);
res.assign(markup);
g_free(markup);
} else {
res.assign("<span foreground=\"red\">[Image]</span>");
}
return res;
}
std::string LabelPangoWidget::get_widget_replace_pango_text(GtkWidget *widget)
{
return "<span foreground=\"red\">[Widget]</span>";
}
* if offset is out of range.
* Index may be in range [0, pango_text_.length()] */
size_t LabelPangoWidget::get_index_by_offset(const char *where_mark_name,
int char_offset)
{
if(char_offset < 0) {
g_warning("Negative offsets are not supported");
return std::string::npos;
}
Mark *pMark = marks_.find_mark(where_mark_name);
if(pMark == NULL) {
g_warning("Mark \"%s\" does not exist", where_mark_name);
return std::string::npos;
}
const char *beg = pango_text_.c_str();
const char *end = xml_utf8_offset_to_pointer(beg + pMark->index_, char_offset);
if(end == NULL) {
size_t len = xml_utf8_strlen(beg + pMark->index_);
g_warning("Incorrect offset %d. string length = %zu", char_offset, len);
}
return end ? (end - beg) : std::string::npos;
}
gboolean TextPangoWidget::on_mouse_move(GtkWidget *widget, GdkEventMotion *event,
gpointer userdata)
{
TextPangoWidget *tpw = static_cast<TextPangoWidget *>(userdata);
GtkTextWindowType win_type =
gtk_text_view_get_window_type(tpw->textview_, event->window);
gint x, y;
gtk_text_view_window_to_buffer_coords(tpw->textview_, win_type,
gint(event->x), gint(event->y),
&x, &y);
TextBufLinks::const_iterator it = tpw->find_link(x, y);
if (it != tpw->tb_links_.end()) {
gdk_window_set_cursor(
gtk_text_view_get_window(tpw->textview_,
GTK_TEXT_WINDOW_TEXT),
get_impl(tpw->hand_cursor_));
} else {
gdk_window_set_cursor(
gtk_text_view_get_window(tpw->textview_,
GTK_TEXT_WINDOW_TEXT),
get_impl(tpw->regular_cursor_));
}
return FALSE;
}
gboolean TextPangoWidget::on_button_release(GtkWidget *, GdkEventButton *event,
gpointer userdata)
{
if (event->button != 1)
return FALSE;
TextPangoWidget *tpw = static_cast<TextPangoWidget *>(userdata);
GtkTextBuffer *buf = gtk_text_view_get_buffer(tpw->textview_);
GtkTextIter beg, end;
gtk_text_buffer_get_selection_bounds (buf, &beg, &end);
if (gtk_text_iter_get_offset (&beg) != gtk_text_iter_get_offset (&end))
return FALSE;
GtkTextWindowType win_type =
gtk_text_view_get_window_type(tpw->textview_, event->window);
gint x, y;
gtk_text_view_window_to_buffer_coords(tpw->textview_, win_type,
gint(event->x), gint(event->y),
&x, &y);
TextBufLinks::const_iterator it = tpw->find_link(x, y);
if (it != tpw->tb_links_.end()) {
tpw->on_link_click_.emit(it->link_);
}
return FALSE;
}
void TextPangoWidget::on_destroy(GtkWidget *object, gpointer userdata)
{
TextPangoWidget *tpw = static_cast<TextPangoWidget *>(userdata);
* IndentedRegion and TextBufPos classes destructors requires a valid widget
* to operate. */
tpw->indented_regions_.clear();
tpw->tb_links_.clear();
}
#ifdef DEBUG
void TextPangoWidget::print_indent_regions(void)
{
GtkTextIter lst_reg_beg, lst_reg_end;
IndentedRegions::iterator it;
for(it = indented_regions_.begin(); it != indented_regions_.end(); ++it) {
it->text_iter_beg(&lst_reg_beg);
it->text_iter_end(&lst_reg_end);
std::cout << "\nregions offsets (" << gtk_text_iter_get_offset(&lst_reg_beg)
<< ", " << gtk_text_iter_get_offset(&lst_reg_end) << ")"
<< " indent: " << it->indent_ << "\n"
<< "text: \"" << gtk_text_iter_get_text(&lst_reg_beg, &lst_reg_end)
<< "\"\n";
}
std::cout.flush();
}
void TextPangoWidget::print_tb_links(void)
{
std::cout << "tb_links begin\n";
size_t num = 0;
for(TextBufLinks::const_iterator i = tb_links_.begin(); i != tb_links_.end();
++i, ++num)
std::cout << "link " << num << "\n"
<< "beg: " << i->beg_ << "\n"
<< "end: " << i->end_ << "\n"
<< "link: " << i->link_.c_str() << "\n";
std::cout << "tb_links end\n" << std::endl;
}
void TextPangoWidget::print_marks(void)
{
std::cout << "marks begin\n";
for(MarkList::const_iterator it = marklist_.begin(); it != marklist_.end();
++it) {
std::cout << gtk_text_mark_get_name(*it) << ", ";
}
std::cout << "\nmarks end" << std::endl;
}
void LabelPangoWidget::print_string_with_marks(void)
{
marks_.print_string_with_marks(pango_text_);
}
#endif