18 июля 2009 г.

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

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

Наш текстовый редактор постепенно становится всё более серьёзным, обрастая новыми функциями. Сегодня добавим возможность отмены/повторения ввода текста.


Как видно на скриншоте, в окно я добавил панель инструментов с двумя кнопками. По умолчанию обе кнопки находятся в неактивном состоянии.
При любом изменении текста в буфере издаётся сигнал 'changed' и вызывается функция on_changed() следующего содержания:
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);
}

Исходный текст, находящийся в свойстве $this->oldtext, сравнивается с текстом, содержащимся в данный момент в буфере. Если они разные, то в заголовок окна добавляется звёздочка, как общепринятый символ изменённого файла. Также кнопка '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);
 
}
 
public function redo()
{
$this->buffer->redo();
if ($this->buffer->can_redo() === FALSE)
{
$this->redo->set_sensitive(FALSE);
}
}

Здесь отдельного внимания заслуживают только методы can_undo()/can_redo(). Они возвращают FALSE в том случае, если дальше отменять/повторять уже невозможно, иначе TRUE.

Также имеются незначительные изменения по сравнению с версией из третьей части, но на них останавливаться не буду.

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

<?php
 
class TextEditor
{
private $buffer, $filename, $oldtext, $window, $title = 'Без имени', $undo, $redo;
 
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'));
 
$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();
 
$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);
 
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);
}
 
$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($scroll, TRUE, TRUE);
 
$this->window->add($vbox);
$this->window->show_all();
}
 
/**
* Вызывается при любом изменении в текстовом поле.
* Производит сравнение текста, находящегося в буфере, и
* исходного. Если они разные, то дополняет заголовок окна
* символом звёздочки.
* Также делает активной кнопку '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();

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