21 июля 2009 г.

Текстовый редактор. Часть 5

Текстовый редактор. Часть 1
Текстовый редактор. Часть 2
Текстовый редактор. Часть 3
Текстовый редактор. Часть 4

Сегодня добавим в текстовый редактор кнопки копировать/вырезать/вставить на панель инструментов и реализуем простой поиск по тексту.


Функции копирования/вырезания/вставки по умолчанию доступны для GtkTextBuffer, доступ к ним можно получить из контекстного меню или с помощью горячих клавиш. Но, по моему, в текстовом редакторе обязательно должны присутствовать соответствующие кнопки на панели инструментов.
При выделении и снятии выделения с текста (сигнал 'notify::has-selection') вызывается функция has_selection(), которая изменяет активность кнопок копировать/вырезать. Скопированный текст помещается в буфер обмена GtkClipboard, это общесистемный буфер, т.е. Вы можете скопировать текст из веб-браузера и вставить в текстовый редактор. Для копирования/вырезания/вставки применяются функции copy_clipboard()/cut_clipboard()/paste_clipboard() соответственно.

Для поиска по тексту выделена отдельная панель. При нажатии на кнопку "Искать" вызывается функция search(), в которой методом forward_search() осуществляется поиск, а методом select_range() - подсветка найденного текста.

Полный код программы:

<?php
 
class TextEditor
{
private $buffer, $filename, $oldtext, $window, $title = 'Без имени',
$undo, $redo, $copy, $cut, $paste, $clipboard;
 
function __construct()
{
$this->window = new GtkWindow();
$this->window->set_default_size(600, 300);
$this->window->set_title($this->title);
$this->window->set_position(Gtk::WIN_POS_CENTER);
$this->window->connect_simple('destroy', array('Gtk', 'main_quit'));
 
$this->clipboard = new GtkClipboard();
 
$menubar = new GtkMenuBar();
$file_menu_item = new GtkMenuItem('_Файл');
$menubar->append($file_menu_item);
$sub_file = new GtkMenu();
$file_menu_item->set_submenu($sub_file);
 
$open = new GtkImageMenuItem(Gtk::STOCK_OPEN);
$open->connect_simple('activate', array($this, 'open_file'));
$sub_file->append($open);
 
$save = new GtkImageMenuItem(Gtk::STOCK_SAVE);
$save->connect_simple('activate', array($this, 'save_file'));
$sub_file->append($save);
 
$sub_file->append(new GtkSeparatorMenuItem());
 
$close = new GtkImageMenuItem(Gtk::STOCK_CLOSE);
$close->connect_simple('activate', array('Gtk', 'main_quit'));
$sub_file->append($close);
 
$toolbar = new GtkToolBar();
 
$save = GtkToolButton::new_from_stock(Gtk::STOCK_SAVE);
$save->connect_simple('clicked', array($this, 'save_file'));
$toolbar->insert($save, -1);
 
$toolbar->insert(new GtkSeparatorToolItem(), -1);
 
$this->undo = GtkToolButton::new_from_stock(Gtk::STOCK_UNDO);
$this->undo->connect_simple('clicked', array($this, 'undo'));
$this->undo->set_sensitive(FALSE);
$toolbar->insert($this->undo, -1);
 
$this->redo = GtkToolButton::new_from_stock(Gtk::STOCK_REDO);
$this->redo->connect_simple('clicked', array($this, 'redo'));
$this->redo->set_sensitive(FALSE);
$toolbar->insert($this->redo, -1);
 
$toolbar->insert(new GtkSeparatorToolItem(), -1);
 
$this->copy = GtkToolButton::new_from_stock(Gtk::STOCK_COPY);
$this->copy->connect_simple('clicked', array($this, 'copy_clipboard'));
$this->copy->set_sensitive(FALSE);
$toolbar->insert($this->copy, -1);
 
$this->cut = GtkToolButton::new_from_stock(Gtk::STOCK_CUT);
$this->cut->connect_simple('clicked', array($this, 'cut_clipboard'));
$this->cut->set_sensitive(FALSE);
$toolbar->insert($this->cut, -1);
 
$this->paste = GtkToolButton::new_from_stock(Gtk::STOCK_PASTE);
$this->paste->connect_simple('clicked', array($this, 'paste_clipboard'));
$toolbar->insert($this->paste, -1);
 
$searchbar = new GtkHBox();
 
$searchbar->pack_start(new GtkLabel('Поиск:'), FALSE, FALSE);
$searchbar->pack_start($entry = new GtkEntry(), FALSE, FALSE);
$searchbar->pack_start($btn_search = new GtkButton('Искать'), FALSE, FALSE);
$btn_search->connect_simple('clicked', array($this, 'search'), $entry);
 
if (class_exists('GtkSourceBuffer') AND
class_exists('GtkSourceView') AND
class_exists('GtkSourceLanguagesManager'))
{
$lang = new GtkSourceLanguagesManager();
$lang = $lang->get_language_from_mime_type("application/x-php");
 
$this->buffer = GtkSourceBuffer::new_with_language($lang);
$this->buffer->set_highlight(TRUE);
$this->buffer->connect('changed', array($this, 'on_changed'));
 
$text = GtkSourceView::new_with_buffer($this->buffer);
$text->set_highlight_current_line(TRUE);
$text->set_show_line_numbers(TRUE);
}
else
{
$this->buffer = new GtkTextBuffer();
$text = new GtkTextView();
$text->set_buffer($this->buffer);
}
$this->buffer->connect_simple('notify::has-selection', array($this, 'has_selection'));
 
$scroll = new GtkScrolledWindow();
$scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
$scroll->add($text);
 
$vbox = new GtkVBox();
$vbox->pack_start($menubar, FALSE, FALSE, 0);
$vbox->pack_start($toolbar, FALSE, FALSE);
$vbox->pack_start($searchbar, FALSE, FALSE);
$vbox->pack_start($scroll, TRUE, TRUE);
 
$this->window->add($vbox);
$this->window->show_all();
}
 
/**
* Осуществляет поиск по текстую
* @param GtkEntry $entry Искомая строка
*/

public function search($entry)
{
$str = $entry->get_text();
$start = $this->buffer->get_start_iter();
$end = $this->buffer->get_end_iter();
$start_iter = $this->buffer->get_start_iter();
$last = $this->buffer->get_mark('last');
if ($last !== NULL)
{
$start_iter = $this->buffer->get_iter_at_mark($last);
}
$search = $start_iter->forward_search(
$str,
0,
$start,
$end,
NULL
);
if ($search)
{
$this->buffer->select_range($start, $end);
$this->buffer->create_mark('last', $end, FALSE);
}
}
 
/**
* Копирует текст.
*/

public function copy_clipboard()
{
$this->buffer->copy_clipboard($this->clipboard);
}
 
/**
* Вырезает текст.
*/

public function cut_clipboard()
{
$this->buffer->cut_clipboard($this->clipboard, TRUE);
}
 
/**
* Вставляет текст из буфера обмена.
*/

public function paste_clipboard()
{
$this->buffer->paste_clipboard($this->clipboard, NULL, TRUE);
}
 
/**
* Изменяет активность кнопок копировать/вставить на панели инструментов.
*/

public function has_selection()
{
if ($this->buffer->get_selection_bounds())
{
$this->copy->set_sensitive(TRUE);
$this->cut->set_sensitive(TRUE);
}
else
{
$this->copy->set_sensitive(FALSE);
$this->cut->set_sensitive(FALSE);
}
}
 
/**
* Вызывается при любом изменении в текстовом поле.
* Производит сравнение текста, находящегося в буфере, и
* исходного. Если они разные, то дополняет заголовок окна
* символом звёздочки.
* Также делает активной кнопку 'Undo' на панели инструментов.
*/

public function on_changed()
{
$start = $this->buffer->get_start_iter();
$end = $this->buffer->get_end_iter();
if ($this->oldtext == $this->buffer->get_text($start, $end))
{
$this->window->set_title($this->title);
}
else
{
$this->window->set_title($this->title . ' (*)');
}
$this->undo->set_sensitive(TRUE);
}
 
/**
* Вызывается при нажатии на кнопку 'Undo' на панели инструментов.
* Отменяет изменения текстового поля и изменяет
* активность кнопок undo/redo на панели инструментов.
*/

public function undo()
{
$this->buffer->undo();
if ($this->buffer->can_undo() === FALSE)
{
$this->undo->set_sensitive(FALSE);
}
$this->redo->set_sensitive(TRUE);
 
}
 
/**
* Вызывается при нажатии на кнопку 'Redo' на панели инструментов.
* Повторяет изменения текстового поля.
*/

public function redo()
{
$this->buffer->redo();
if ($this->buffer->can_redo() === FALSE)
{
$this->redo->set_sensitive(FALSE);
}
}
 
/**
* Отображает окно для открытия файлов.
*/

public function open_file()
{
$dialog = new GtkFileChooserDialog(
'Выбрать файл',
NULL,
Gtk::FILE_CHOOSER_ACTION_OPEN,
array(
Gtk::STOCK_CANCEL, Gtk::RESPONSE_CANCEL,
Gtk::STOCK_OK, Gtk::RESPONSE_OK
)
);
$dialog->show_all();
$result = $dialog->run();
if ($result == Gtk::RESPONSE_OK)
{
$this->filename = $dialog->get_filename();
$this->oldtext = trim(file_get_contents($this->filename));
$this->buffer->set_text($this->oldtext);
$this->title = basename($this->filename);
$this->window->set_title($this->title);
}
$dialog->destroy();
}
 
/**
* Производит сохранение файлов.
*/

public function save_file()
{
$start = $this->buffer->get_start_iter();
$end = $this->buffer->get_end_iter();
$text = $this->buffer->get_text($start, $end);
$fopen = fopen($this->filename, 'w+');
fwrite($fopen, $text);
fclose($fopen);
}
}
 
new TextEditor();
Gtk::main();

Комментариев нет: