/*
* Copyright (C) 2002-2005 Morten Brix Pedersen <morten@wtf.dk>
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <gtkmm/scrollbar.h>
#include <gtkmm/adjustment.h>
#include <gtkmm/style.h>
#include "TextWidget.h"
#include "MainWindow.h"
using std::vector;
using Glib::ustring;
TextWidget::TextWidget(Pango::FontDescription font)
: highlight_mark_pos(highlight_marks.rend())
{
_textview.set_wrap_mode(Gtk::WRAP_WORD_CHAR);
_textview.unset_flags(Gtk::CAN_FOCUS);
_textview.set_editable(false);
set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
set_size_request(0, -1);
add(_textview);
setStyle();
_textview.modify_font(font);
Glib::RefPtr<Gtk::TextBuffer> buffer = Gtk::TextBuffer::create(AppWin->_current_tag_table);
_textview.set_buffer(buffer);
pos = buffer->create_mark(buffer->end());
scrollToBottom();
get_vscrollbar()->signal_size_allocate().connect(sigc::mem_fun(*this, &TextWidget::onResize));
get_vscrollbar()->signal_value_changed().connect(sigc::mem_fun(*this, &TextWidget::onScroll));
_textview.signal_populate_popup().connect(sigc::mem_fun(*this, &TextWidget::populateMenu));
}
void TextWidget::onScroll()
{
if (get_vscrollbar()->get_value() >= (get_vscrollbar()->get_adjustment()->get_upper() - get_vscrollbar()->get_adjustment()->get_page_size() - 1e-12)) {
scrollToBottom();
}
}
void TextWidget::onResize(Gtk::Allocation& alloc)
{
if (get_vscrollbar()->get_value() >= (get_vscrollbar()->get_adjustment()->get_upper() - get_vscrollbar()->get_adjustment()->get_page_size() - 1e-12)) {
scrollToBottom();
} else {
_textview.scroll_to(pos, 0.0, 0.5, 1.0);
}
}
void TextWidget::setHighlightMark()
{
Glib::RefPtr<Gtk::TextBuffer> buffer = _textview.get_buffer();
highlight_marks.push_back(buffer->create_mark(buffer->end()));
highlight_mark_pos = highlight_marks.rbegin();
}
void TextWidget::scrollToHighlightMark()
{
if (!highlight_marks.empty()) {
highlight_mark_pos++;
if (highlight_mark_pos == highlight_marks.rend())
highlight_mark_pos = highlight_marks.rbegin();
_textview.scroll_to(*highlight_mark_pos, 0.1);
}
}
void TextWidget::scrollUpPage()
{
Gtk::Adjustment *vadj = get_vadjustment();
double value = vadj->get_value() - (vadj->get_page_size() - 1);
get_vscrollbar()->set_value(value);
}
void TextWidget::scrollDownPage()
{
Gtk::Adjustment *vadj = get_vadjustment();
double value = vadj->get_value() + (vadj->get_page_size() - 1);
get_vscrollbar()->set_value(value);
}
void TextWidget::scrollToBottom()
{
Glib::RefPtr<Gtk::TextBuffer> buffer = _textview.get_buffer();
buffer->move_mark(pos, buffer->end());
_textview.scroll_to(pos, 0.0);
}
void TextWidget::scrollToTop()
{
Glib::RefPtr<Gtk::TextBuffer> buffer = _textview.get_buffer();
buffer->move_mark(pos, buffer->begin());
_textview.scroll_to(pos, 0.0);
}
void TextWidget::setStyle() {
// TODO: Should this go into a ressource file?
Gdk::Color col1(AppWin->background_color);
_textview.modify_base(Gtk::STATE_NORMAL, col1);
}
void TextWidget::clearText()
{
Glib::RefPtr<Gtk::TextBuffer> buffer = _textview.get_buffer();
buffer->erase(buffer->begin(), buffer->end());
}
void TextWidget::setFont(const Pango::FontDescription& font)
{
_textview.modify_font(font);
}
TextWidget& TextWidget::operator<<(const char * str)
{
return operator<<(std::string(str));
}
TextWidget& TextWidget::operator<<(const ustring& line)
{
// see if the scrollbar is located in the bottom, then we need to scroll
// after insert
bool need_to_scroll = false;
if (get_vadjustment()->get_value() >= (get_vadjustment()->get_upper() - get_vadjustment()->get_page_size() - 1e-12))
need_to_scroll = true;
TextProperties tp;
tp.clear();
// Parse string and insert character by character
for (ustring::size_type i = 0; i < line.length(); ++i)
{
if (line[i] == '\017') { // RESET
tp.clear();
} else if (line[i] == '\002') { // BOLD
tp.bold = !tp.bold;
} else if (line[i] == '\037') { // UNDERLINE
tp.underline = !tp.underline;
} else if (line[i] == '\003') { // COLOR
tp.fgcolor = true;
tp.numbercount = 0;
tp.fgnumber.clear();
tp.bgnumber.clear();
} else if (tp.fgcolor && isdigit(line[i]) && tp.numbercount < 2) {
tp.numbercount++;
tp.fgnumber += line[i];
} else if (tp.fgcolor && line[i] == ',' && tp.numbercount < 3) {
tp.numbercount = 0;
tp.bgcolor = true;
tp.fgcolor = false;
} else if (tp.bgcolor && isdigit(line[i]) && tp.numbercount < 2) {
tp.numbercount++;
tp.bgnumber += line[i];
} else {
tp.numbercount = 0;
tp.fgcolor = false;
tp.bgcolor = false;
if (tp.bgnumber.empty())
tp.bgnumber = "1";
if (tp.fgnumber.empty())
tp.fgnumber = "0";
Glib::ustring text;
text = line[i];
insertText(tp, text);
}
}
removeTopBuffer();
if (need_to_scroll) {
scrollToBottom();
}
return *this;
}
Glib::ustring crop(Glib::ustring str)
{
if (str.size() > 1 && str[0] == '0')
return str.substr(1);
else
return str;
}
void TextWidget::insertText(const TextProperties& tp, const ustring& line)
{
Glib::RefPtr<Gtk::TextBuffer> buffer = _textview.get_buffer();
std::vector< Glib::RefPtr<Gtk::TextTag> > tags;
Glib::RefPtr<Gtk::TextTag> fg = buffer->get_tag_table()->lookup(Glib::ustring("f")+crop(tp.fgnumber));
if (fg == 0)
fg = buffer->get_tag_table()->lookup("f0");
Glib::RefPtr<Gtk::TextTag> bg = buffer->get_tag_table()->lookup(Glib::ustring("b")+crop(tp.bgnumber));
if (bg == 0)
bg = buffer->get_tag_table()->lookup("b0");
tags.push_back(fg);
tags.push_back(bg);
if (tp.bold)
tags.push_back(buffer->get_tag_table()->lookup("B"));
if (tp.underline)
tags.push_back(buffer->get_tag_table()->lookup("U"));
buffer->insert_with_tags(buffer->end(), line, tags);
#if 0
/* below URL handling is broken at the moment, so disabled. It's broken
* because we only receive one character at the time in this function.
*/
ustring::size_type pos1;
pos1 = line.find("http:");
if (pos1 == ustring::npos)
pos1 = line.find("www.");
if (pos1 == ustring::npos)
pos1 = line.find("ftp.");
if (pos1 == ustring::npos)
pos1 = line.find("ftp:");
if (pos1 != ustring::npos) {
// Found an URL - insert the front and end of the line, and insert
// the URL with special markup.
ustring::size_type pos2 = line.find(" ", pos1 + 1);
// What's before the URL
buffer->insert_with_tags(buffer->end(), line.substr(0, pos1), tags),
// The URL
buffer->insert_with_tag(buffer->end(), line.substr(pos1, pos2 - pos1), underlinetag);
// After the URL
if (pos2 != ustring::npos)
buffer->insert_with_tags(buffer->end(), line.substr(pos2), tags);
} else {
// Just insert the line, no URLs were found
buffer->insert_with_tags(buffer->end(), line, tags);
}
#endif
}
void TextWidget::removeTopBuffer()
{
// Remove X number of lines from top of the buffer which we don't want.
Glib::RefPtr<Gtk::TextBuffer> buffer = _textview.get_buffer();
// FIXME: possible performance critical
int buffer_size = App->options.buffer_size;
if (buffer_size && buffer->get_line_count() > buffer_size)
buffer->erase(buffer->begin(), buffer->get_iter_at_line(buffer->get_line_count() - buffer_size));
}
void TextWidget::populateMenu(Gtk::Menu *contextmenu)
{
Gtk::Menu::MenuList& menulist = contextmenu->items();
menulist.push_back(Gtk::Menu_Helpers::MenuElem(("_Menubar"), Gtk::AccelKey("<control>m"), sigc::mem_fun(*AppWin, &MainWindow::hideMenu)));
}
syntax highlighted by Code2HTML, v. 0.9.1