Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Программирование с использованием gtkmm 3. Буфер обмена

Оригинал: Programming with gtkmm 3
Авторы: Murray Cumming, Bernhard Rieder, Jonathon Jongsma, Ole Laursen, Marko Anastasov, Daniel Elstner, Chris Vine, David King, Pedro Ferreira, Kjell Ahlstedt
Дата публикации: 15 Октября 2013 г.
Перевод: А.Панин
Дата перевода: 28 марта 2014 г.

19. Буфер обмена

Простые функции копирования и вставки текста предоставляются по умолчанию таким виджетам, как представленный классом Gtk::Entry виджет ввода строки и представленный классом Gtk::TextView виджет поля ввода текста, но вы можете ощутить потребность в реализации специального кода для работы с вашими собственными форматами данных. Например, программа для рисования может использовать специальный код для предоставления возможности копирования и вставки данных в рамках текущего изображения или различных документов.

Практически в любой ситуации вы можете рассматривать класс Gtk::Clipboard как синглтон. При этом вы можете получить экземпляр стандартного буфера обмена с помощью метода Gtk::Clipboard::get(). Скорее всего, это единственный буфер обмена, который вам когда-либо потребуется.

Вашему приложению не потребуется ожидать завершения операций с буфером обмена, особенно в период между активацией пользователем функции "Копировать" и активацией пользователем функции "Вставить". Большая часть методов класса Gtk::Clipboard принимает слоты типа std::slot, которые указывают на методы обратного вызова. В момент, когда объект буфера обмена на основе класса Gtk::Clipboard получает сигнал о готовности, он осуществляет вызов этих методов либо предоставляя запрашиваемые данные, либо запрашивая данные.

Справочная информация

19.1. Цели

Различные приложения работают с различными типами данных, причем они могут преобразовывать эти данные во множество форматов. В рамках gtkmm эти типы данных называются целями.

Например, приложение gedit может предоставлять и принимать цель "UTF8_STRING", поэтому вы можете вставить данные, переданные любым предоставляющим эту цель приложением, в поле редактирования текста приложения gedit. Также два приложения для редактирования изображений могут предоставлять и принимать изображения во множестве форматов, рассматривая эти форматы в качестве целей. Если одно приложение может принимать одну из целей, которую также предоставляет другое приложение, у вас будет возможность копировать данные из одного приложения в другое.

Цель может быть представлена множеством бинарных форматов. В данной главе и в соответствующих примерах мы будем считать, что наши данные являются исключительно 8-битным текстом. Это позволит нам использовать формат разметки текста XML для данных в буфере обмена. Однако, этот формат, вероятно, не подходит для хранения бинарных данных, таких, как изображения. Класс Gtk::Clipboard предоставляет перегруженные методы, которые позволят вам описать формат более подробно в случае необходимости.

API захвата и перемещения данных (Drag and Drop) использует аналогичный механизм. Вам, скорее всего, придется использовать одни и те же цели и форматы данных как для операций с буфером обмена, так и операций захвата и перемещения данных.

19.2. Копирование

В момент, когда пользователь выполняет копирование каких-либо данных, вы должны сообщить объекту буфера обмена о том, какие цели доступны и передать ему методы обратного вызова, которые он сможет использовать для получения данных. В этот же момент вы должны сохранить копию ваших данных для передачи ее методу обратного вызова в ответ на осуществление пользователем вставки данных.

Примером может служить следующий код:
Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get();

//Цели:
std::vector<Gtk::TargetEntry> targets;
targets.push_back( Gtk::TargetEntry("example_custom_target") );
targets.push_back( Gtk::TargetEntry("UTF8_STRING") );

refClipboard->set( targets,
    sigc::mem_fun(*this, &ExampleWindow::on_clipboard_get),
    sigc::mem_fun(*this, &ExampleWindow::on_clipboard_clear) );
После этого ваш метод обратного вызова передаст сохраненные данные в момент, когда пользователь осуществит вставку данных. Этот метод может быть реализован следующим образом:
void ExampleWindow::on_clipboard_get(
    Gtk::SelectionData& selection_data, guint /* info */)
{
  const std::string target = selection_data.get_target();

  if(target == "example_custom_target")
    selection_data.set("example_custom_target", m_ClipboardStore);
}

В идеальном примере ниже может передаваться более чем одна цель буфера обмена.

Метод обратного вызова clear позволяет вам освободить зарезервированную память, в которой хранились данные, в момент, когда текущие данные буфера обмена заменяются на какие-либо другие данные.

19.3. Вставка

В момент, когда пользователь запрашивает вставку данных из буфера обмена, вы должны осуществить запрос определенного формата и предоставить метод обратного вызова, который будет вызван для передачи самих данных. Примером может служить следующий код:
refClipboard->request_contents("example_custom_target",
    sigc::mem_fun(*this, &ExampleWindow::on_clipboard_received) );
А ниже приведен пример метода обратного вызова:
void ExampleWindow::on_clipboard_received(
    const Gtk::SelectionData& selection_data)
{
  Glib::ustring clipboard_data = selection_data.get_data_as_string();
  //Выполнение каких-либо операций с вставленными данными.
}

19.3.1. Установление доступных целей

Для установления того, какие цели доступны на данный момент для вставки данных, следует вызвать метод request_targets(), указав метод, который будет вызван для передачи информации. Примером может служить следующий код:
refClipboard->request_targets( sigc::mem_fun(*this,
    &ExampleWindow::on_clipboard_received_targets) );
В рамках вашего метода обратного вызова вы должны осуществить сравнение элементов вектора доступных целей с элементами вектора целей, которые поддерживает ваше приложение для вставки данных. В результате вы должны активировать или деактивировать элемент меню "Вставить" в зависимости от того, имеется ли на данный момент возможность вставки данных. Примером реализации метода обратного вызова может служить следующий код:
void ExampleWindow::on_clipboard_received_targets(
  const std::vector<Glib::ustring>& targets)
{
  const bool bPasteIsPossible =
    std::find(targets.begin(), targets.end(),
      example_target_custom) != targets.end();

  // Активация/дективация элемента "Вставить" в соответствии с наличием возможности вставки данных:
  m_Button_Paste.set_sensitive(bPasteIsPossible);
}

19.4. Примеры

19.4.1. Простой пример

Данный пример позволяет копировать и вставлять специфичные для приложения данные с использованием стандартной текстовой цели. Хотя этот пример и является достаточно простым, он не является идеальным, поскольку не идентифицирует данные буфера обмена в плане принадлежности к определенному типу данных.

Рисунок 19-1: Буфер обмена - Простой пример
Буфер обмена - Простой пример

Исходный код

Файл: examplewindow.h (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл: examplewindow.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл: main.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)

19.4.2. Идеальный пример

Этот пример похож на простой пример, но он
  1. Устанавливает специальную цель для буфера обмена, хотя форматом этой цели все равно является текст.
  2. Поддерживает вставку с использованием 2 целей - как с помощью специальной цели, так и с помощью текстовой цели, которая создает произвольное текстовое представление специфических данных.
  3. Использует метод request_targets() и сигнал "owner_change" и деактивирует кнопку "Вставить" в том случае, если данные из буфера не могут быть использованы.

Рисунок 19-2: Буфер обмена - Идеальный пример
Буфер обмена - Идеальный пример

Исходный код

Файл: examplewindow.h (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл: examplewindow.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл: main.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)


Следующий раздел : 20.Печать.