diff --git a/data/net.launchpad.Diodon.gschema.xml b/data/net.launchpad.Diodon.gschema.xml
index da18f25..62b79d9 100644
--- a/data/net.launchpad.Diodon.gschema.xml
+++ b/data/net.launchpad.Diodon.gschema.xml
@@ -35,6 +35,11 @@
Number of recent clipboard items
Number of recent clipboard items shown in clipboard menu.
+
+ []
+ Pinned clipboard items
+ Checksums of clipboard items that are pinned to the top of the menu and preserved on clear.
+
'^\\s+$'
Clipboard content filter pattern
diff --git a/libdiodon/clipboard-item.vala b/libdiodon/clipboard-item.vala
index 02b4f83..93ab0b1 100644
--- a/libdiodon/clipboard-item.vala
+++ b/libdiodon/clipboard-item.vala
@@ -66,6 +66,13 @@ namespace Diodon
*/
public abstract Gtk.Image? get_image();
+ /**
+ * preview pixbuf for tooltip display
+ *
+ * @return scaled preview pixbuf or null if not available
+ */
+ public abstract Gdk.Pixbuf? get_preview_pixbuf();
+
/**
* icon to represent type of clipboard item
*
diff --git a/libdiodon/clipboard-menu-item.vala b/libdiodon/clipboard-menu-item.vala
index 0552137..b0f5fa0 100644
--- a/libdiodon/clipboard-menu-item.vala
+++ b/libdiodon/clipboard-menu-item.vala
@@ -28,6 +28,10 @@ namespace Diodon
class ClipboardMenuItem : Gtk.ImageMenuItem
{
private string _checksum;
+ private IClipboardItem _item;
+ private Gdk.Pixbuf? _cached_preview;
+ private bool _preview_loaded;
+ private static Gtk.Window? _preview_window;
/**
* Clipboard item constructor
@@ -37,6 +41,7 @@ namespace Diodon
public ClipboardMenuItem(IClipboardItem item)
{
_checksum = item.get_checksum();
+ _item = item;
set_label(item.get_label());
// check if image needs to be shown
@@ -45,6 +50,98 @@ namespace Diodon
set_image(image);
set_always_show_image(true);
}
+
+ debug("ClipboardMenuItem: connecting select/deselect for %s", _checksum);
+ select.connect(on_item_select);
+ deselect.connect(on_item_deselect);
+ }
+
+ private Gdk.Pixbuf? get_preview()
+ {
+ if (!_preview_loaded) {
+ _cached_preview = _item.get_preview_pixbuf();
+ _preview_loaded = true;
+ }
+ return _cached_preview;
+ }
+
+ private void on_item_select()
+ {
+ debug("on_item_select: checksum=%s", _checksum);
+ Gdk.Pixbuf? preview = get_preview();
+ if (preview == null) {
+ debug("on_item_select: no preview available");
+ return;
+ }
+ debug("on_item_select: preview %dx%d", preview.width, preview.height);
+ show_preview_pixbuf(preview);
+ }
+
+ public static void show_preview_for(IClipboardItem item)
+ {
+ Gdk.Pixbuf? preview = item.get_preview_pixbuf();
+ if (preview == null) {
+ return;
+ }
+ show_preview_pixbuf(preview);
+ }
+
+ private static void show_preview_pixbuf(Gdk.Pixbuf preview)
+ {
+ if (_preview_window == null) {
+ _preview_window = new Gtk.Window(Gtk.WindowType.POPUP);
+ _preview_window.set_type_hint(Gdk.WindowTypeHint.TOOLTIP);
+ _preview_window.set_app_paintable(true);
+ Gdk.Screen screen = _preview_window.get_screen();
+ Gdk.Visual? visual = screen.get_rgba_visual();
+ if (visual != null) {
+ _preview_window.set_visual(visual);
+ }
+ }
+ _preview_window.foreach((child) => { _preview_window.remove(child); });
+ Gtk.Image preview_image = new Gtk.Image.from_pixbuf(preview);
+ Gtk.CssProvider css = new Gtk.CssProvider();
+ try {
+ css.load_from_data("frame { border: 1px solid #888888; }", -1);
+ } catch (Error e) {
+ warning("Failed to load preview CSS: %s", e.message);
+ }
+ Gtk.Frame frame = new Gtk.Frame(null);
+ frame.set_shadow_type(Gtk.ShadowType.NONE);
+ frame.get_style_context().add_provider(css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ frame.add(preview_image);
+ _preview_window.add(frame);
+ _preview_window.resize(preview.width, preview.height);
+ Gdk.Display? display = Gdk.Display.get_default();
+ Gdk.Seat? seat = display != null ? display.get_default_seat() : null;
+ if (seat != null) {
+ int mouse_x, mouse_y;
+ seat.get_pointer().get_position(null, out mouse_x, out mouse_y);
+ int x = mouse_x + 16;
+ int y = mouse_y + 16;
+ Gdk.Screen screen = _preview_window.get_screen();
+ int screen_height = screen.get_height();
+ if (y + preview.height > screen_height) {
+ y = screen_height - preview.height;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ _preview_window.move(x, y);
+ }
+ _preview_window.show_all();
+ }
+
+ private static void on_item_deselect()
+ {
+ hide_preview();
+ }
+
+ public static void hide_preview()
+ {
+ if (_preview_window != null) {
+ _preview_window.hide();
+ }
}
/**
diff --git a/libdiodon/clipboard-menu.vala b/libdiodon/clipboard-menu.vala
index 4c6bd12..be1645b 100644
--- a/libdiodon/clipboard-menu.vala
+++ b/libdiodon/clipboard-menu.vala
@@ -28,16 +28,19 @@ namespace Diodon
{
private Controller controller;
private unowned List static_menu_items;
+ private int _saved_menu_x = -1;
+ private int _saved_menu_y = -1;
/**
* Create clipboard menu
*
* @param controller reference to controller
+ * @param pinned_items pinned clipboard items shown at the top
* @param items clipboard items to be shown
* @param menu_items additional menu items to be added after separator
* @param privacy_mode check whether privacy mode is enabled
*/
- public ClipboardMenu(Controller controller, List items, List? static_menu_items, bool privace_mode, string? error = null)
+ public ClipboardMenu(Controller controller, List pinned_items, List items, List? static_menu_items, bool privace_mode, string? error = null)
{
this.controller = controller;
this.static_menu_items = static_menu_items;
@@ -46,7 +49,7 @@ namespace Diodon
Gtk.MenuItem error_item = new Gtk.MenuItem.with_label(wrap_label(error));
error_item.set_sensitive(false);
append(error_item);
- } else if(items.length() <= 0) {
+ } else if(pinned_items.length() <= 0 && items.length() <= 0) {
Gtk.MenuItem empty_item = new Gtk.MenuItem.with_label(_(""));
empty_item.set_sensitive(false);
append(empty_item);
@@ -60,9 +63,17 @@ namespace Diodon
append(privacy_item);
}
+ foreach(IClipboardItem item in pinned_items) {
+ append_clipboard_item(item, true);
+ }
+
+ if (pinned_items.length() > 0 && items.length() > 0) {
+ Gtk.SeparatorMenuItem pin_sep = new Gtk.SeparatorMenuItem();
+ append(pin_sep);
+ }
foreach(IClipboardItem item in items) {
- append_clipboard_item(item);
+ append_clipboard_item(item, false);
}
Gtk.SeparatorMenuItem sep_item = new Gtk.SeparatorMenuItem();
@@ -89,21 +100,68 @@ namespace Diodon
show_all();
this.key_press_event.connect(on_key_pressed);
+ this.hide.connect(() => { ClipboardMenuItem.hide_preview(); });
+ this.map.connect(() => {
+ if (get_window() != null) {
+ int x, y;
+ get_window().get_origin(out x, out y);
+ _saved_menu_x = x;
+ _saved_menu_y = y;
+ }
+ });
}
/**
* Append given clipboard item to menu.
*
- * @param entry entry to be added
+ * @param item clipboard item to add
+ * @param pinned whether the item is pinned
*/
- public void append_clipboard_item(IClipboardItem item)
+ public void append_clipboard_item(IClipboardItem item, bool pinned = false)
{
ClipboardMenuItem menu_item = new ClipboardMenuItem(item);
menu_item.activate.connect(on_clicked_item);
+ menu_item.button_press_event.connect((event) => {
+ if (event.button == 3) {
+ show_pin_context_menu(menu_item, pinned, event);
+ return true;
+ }
+ return false;
+ });
menu_item.show();
append(menu_item);
}
+ private void show_pin_context_menu(ClipboardMenuItem menu_item, bool pinned, Gdk.EventButton event)
+ {
+ Gtk.Menu ctx = new Gtk.Menu();
+ string label = pinned ? _("Unpin") : _("Pin");
+ Gtk.MenuItem pin_item = new Gtk.MenuItem.with_label(label);
+ int reopen_x = _saved_menu_x;
+ int reopen_y = _saved_menu_y;
+ pin_item.activate.connect(() => {
+ controller.toggle_pin_item.begin(menu_item.get_item_checksum(), false);
+ });
+ ctx.append(pin_item);
+ ctx.show_all();
+ ctx.deactivate.connect(() => {
+ controller.rebuild_recent_menu.begin((obj, res) => {
+ controller.rebuild_recent_menu.end(res);
+ if (reopen_x >= 0 && reopen_y >= 0) {
+ Gtk.Menu new_menu = controller.get_recent_menu() as Gtk.Menu;
+ if (new_menu != null) {
+ new_menu.popup(null, null, (menu, ref x, ref y, out push_in) => {
+ x = reopen_x;
+ y = reopen_y;
+ push_in = false;
+ }, 0, Gtk.get_current_event_time());
+ }
+ }
+ });
+ });
+ ctx.popup_at_pointer(event);
+ }
+
public void show_menu()
{
// timer is needed to workaround race condition between X11 and Gdk event
@@ -181,12 +239,14 @@ namespace Diodon
}
/**
- * Allow moving of cursor with vi-style j and k keys
+ * Allow moving of cursor with vi-style j and k keys,
+ * and p to toggle pin on selected item.
*/
private bool on_key_pressed(Gdk.EventKey event)
{
uint down_keyval = Gdk.keyval_from_name("j");
uint up_keyval = Gdk.keyval_from_name("k");
+ uint pin_keyval = Gdk.keyval_from_name("p");
uint pressed_keyval = Gdk.keyval_to_lower(event.keyval);
if(pressed_keyval == down_keyval) {
@@ -204,6 +264,14 @@ namespace Diodon
move_selected(-1);
return true;
}
+ if(pressed_keyval == pin_keyval) {
+ Gtk.MenuItem? selected = get_selected_item() as Gtk.MenuItem;
+ if (selected != null && selected is ClipboardMenuItem) {
+ ClipboardMenuItem clip_item = (ClipboardMenuItem) selected;
+ controller.toggle_pin_item.begin(clip_item.get_item_checksum());
+ }
+ return true;
+ }
return false;
}
diff --git a/libdiodon/controller.vala b/libdiodon/controller.vala
index 5e7fec7..af65bdd 100644
--- a/libdiodon/controller.vala
+++ b/libdiodon/controller.vala
@@ -66,6 +66,8 @@ namespace Diodon
*/
public signal void on_recent_menu_changed(Gtk.Menu recent_menu);
+ public signal void on_pinned_items_changed();
+
public delegate void ActionCallback(string[] args);
public Controller()
@@ -531,11 +533,30 @@ namespace Diodon
items = new List();
}
+ List pinned = yield get_pinned_items();
+
+ // remove pinned items from the regular list to avoid duplicates
+ string[] pinned_checksums = settings_clipboard.get_strv("pinned-items");
+ List filtered_items = new List();
+ foreach (IClipboardItem item in items) {
+ bool is_pinned = false;
+ foreach (string pc in pinned_checksums) {
+ if (item.get_checksum() == pc) {
+ is_pinned = true;
+ break;
+ }
+ }
+ if (!is_pinned) {
+ filtered_items.append(item);
+ }
+ }
+
if(recent_menu != null) {
recent_menu.destroy_menu();
}
- recent_menu = new ClipboardMenu(this, items, static_recent_menu_items,
+ recent_menu = new ClipboardMenu(this, pinned, filtered_items,
+ static_recent_menu_items,
storage.is_privacy_mode_enabled(), error);
on_recent_menu_changed(recent_menu);
}
@@ -643,7 +664,73 @@ namespace Diodon
*/
public void show_preferences()
{
- preferences_view.show(configuration);
+ preferences_view.show(configuration, this);
+ }
+
+ public void set_pinned_checksums(string[] checksums)
+ {
+ settings_clipboard.set_strv("pinned-items", checksums);
+ rebuild_recent_menu.begin();
+ }
+
+ public bool is_item_pinned(string checksum)
+ {
+ string[] pinned = settings_clipboard.get_strv("pinned-items");
+ foreach (string c in pinned) {
+ if (c == checksum) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public async void toggle_pin_item(string checksum, bool rebuild = true)
+ {
+ string[] pinned = settings_clipboard.get_strv("pinned-items");
+ string[] new_pinned = {};
+ bool found = false;
+ foreach (string c in pinned) {
+ if (c == checksum) {
+ found = true;
+ }
+ else {
+ new_pinned += c;
+ }
+ }
+ if (!found) {
+ new_pinned += checksum;
+ }
+ settings_clipboard.set_strv("pinned-items", new_pinned);
+ on_pinned_items_changed();
+ if (rebuild) {
+ yield rebuild_recent_menu();
+ }
+ }
+
+ public async void add_and_pin_text(string text)
+ {
+ IClipboardItem item = new TextClipboardItem(ClipboardType.CLIPBOARD, text, null, new DateTime.now_utc());
+ yield storage.add_item(item);
+ string checksum = item.get_checksum();
+ if (!is_item_pinned(checksum)) {
+ yield toggle_pin_item(checksum);
+ }
+ else {
+ yield rebuild_recent_menu();
+ }
+ }
+
+ public async List get_pinned_items()
+ {
+ string[] pinned = settings_clipboard.get_strv("pinned-items");
+ List items = new List();
+ foreach (string checksum in pinned) {
+ IClipboardItem? item = yield storage.get_item_by_checksum(checksum);
+ if (item != null) {
+ items.append(item);
+ }
+ }
+ return items;
}
/**
@@ -651,12 +738,13 @@ namespace Diodon
*/
public async void clear()
{
+ List pinned = yield get_pinned_items();
yield storage.clear();
on_clear();
-
- // Bug #1383013:
- // in some rare circumstances doesn't the recent menu get refreshed
- // when clear is executed; therefore forcing it here as a workaround
+ foreach (IClipboardItem item in pinned) {
+ yield storage.add_item(item);
+ }
+ // Bug #1383013: force menu refresh as workaround
yield rebuild_recent_menu();
}
diff --git a/libdiodon/file-clipboard-item.vala b/libdiodon/file-clipboard-item.vala
index 53f41c8..c4d0b7f 100644
--- a/libdiodon/file-clipboard-item.vala
+++ b/libdiodon/file-clipboard-item.vala
@@ -153,6 +153,29 @@ namespace Diodon
return image;
}
+ /**
+ * {@inheritDoc}
+ */
+ public Gdk.Pixbuf? get_preview_pixbuf()
+ {
+ string[] paths = convert_to_paths(_paths);
+ if (paths.length == 0) {
+ return null;
+ }
+ string path = paths[0];
+ string mime_type = get_mime_type();
+ if (!mime_type.has_prefix("image/")) {
+ return null;
+ }
+ try {
+ Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file(path);
+ return ImageClipboardItem.create_preview_pixbuf(pixbuf);
+ } catch (Error e) {
+ warning("Could not load preview for file %s: %s", path, e.message);
+ return null;
+ }
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/libdiodon/image-clipboard-item.vala b/libdiodon/image-clipboard-item.vala
index 0faccbd..10b8e1d 100644
--- a/libdiodon/image-clipboard-item.vala
+++ b/libdiodon/image-clipboard-item.vala
@@ -150,6 +150,14 @@ namespace Diodon
return new Gtk.Image.from_pixbuf(pixbuf_preview);
}
+ /**
+ * {@inheritDoc}
+ */
+ public Gdk.Pixbuf? get_preview_pixbuf()
+ {
+ return create_preview_pixbuf(_pixbuf);
+ }
+
/**
* {@inheritDoc}
*/
@@ -224,6 +232,20 @@ namespace Diodon
*
* @param pixbuf scaled pixbuf
*/
+ public static Gdk.Pixbuf create_preview_pixbuf(Gdk.Pixbuf pixbuf)
+ {
+ int max_size = 500;
+ int width = pixbuf.width;
+ int height = pixbuf.height;
+ if (width <= max_size && height <= max_size) {
+ return pixbuf;
+ }
+ double scale = double.min((double) max_size / width, (double) max_size / height);
+ int new_width = (int) (width * scale);
+ int new_height = (int) (height * scale);
+ return pixbuf.scale_simple(new_width, new_height, Gdk.InterpType.BILINEAR);
+ }
+
private static Gdk.Pixbuf create_scaled_pixbuf(Gdk.Pixbuf pixbuf)
{
// get menu icon size
diff --git a/libdiodon/preferences-view.vala b/libdiodon/preferences-view.vala
index 0d635fb..2649eb7 100644
--- a/libdiodon/preferences-view.vala
+++ b/libdiodon/preferences-view.vala
@@ -27,7 +27,6 @@ namespace Diodon
class PreferencesView : GLib.Object
{
private Gtk.Dialog preferences;
-
public PreferencesView()
{
}
@@ -35,9 +34,10 @@ namespace Diodon
/**
* Show preferences view
*
- * @param configuraiton configuration to initialize dialog
+ * @param configuration configuration to initialize dialog
+ * @param controller controller for pinned items management
*/
- public void show(ClipboardConfiguration configuration)
+ public void show(ClipboardConfiguration configuration, Controller? controller = null)
{
// check if preferences window is already open
if(preferences == null) {
@@ -110,6 +110,12 @@ namespace Diodon
Gtk.Box plugins_box = builder.get_object("plugins_box") as Gtk.Box;
plugins_box.pack_start(manager);
+ // pinned items tab
+ if (controller != null) {
+ Gtk.Notebook notebook = builder.get_object("notebook_preferences") as Gtk.Notebook;
+ build_pinned_tab(notebook, controller);
+ }
+
// close
Gtk.Button close = builder.get_object("button_close") as Gtk.Button;
close.clicked.connect(hide);
@@ -125,6 +131,203 @@ namespace Diodon
}
}
+ private bool _inhibit_save = false;
+ private int _select_after_repopulate = -1;
+ private Gtk.TreeView? _pinned_tree_view = null;
+
+ private void build_pinned_tab(Gtk.Notebook notebook, Controller controller)
+ {
+ Gtk.Box pinned_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 6);
+ pinned_box.border_width = 12;
+
+ Gtk.ListStore store = new Gtk.ListStore(3, typeof(Gdk.Pixbuf), typeof(string), typeof(string));
+ Gtk.TreeView tree_view = new Gtk.TreeView.with_model(store);
+ _pinned_tree_view = tree_view;
+ tree_view.headers_visible = false;
+ tree_view.reorderable = true;
+
+ Gtk.CellRendererPixbuf icon_cell = new Gtk.CellRendererPixbuf();
+ tree_view.insert_column_with_attributes(-1, "Icon", icon_cell, "pixbuf", 0);
+
+ Gtk.CellRendererText cell = new Gtk.CellRendererText();
+ cell.ellipsize = Pango.EllipsizeMode.END;
+ tree_view.insert_column_with_attributes(-1, "Label", cell, "text", 1);
+
+ populate_pinned_list(store, controller);
+
+ store.row_deleted.connect(() => {
+ if (!_inhibit_save) {
+ save_pinned_order(store, controller);
+ }
+ });
+
+ string _hover_checksum = "";
+ tree_view.add_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK);
+ tree_view.motion_notify_event.connect((event) => {
+ Gtk.TreePath? path;
+ if (tree_view.get_path_at_pos((int) event.x, (int) event.y, out path, null, null, null)) {
+ Gtk.TreeIter iter;
+ if (store.get_iter(out iter, path)) {
+ string checksum;
+ store.get(iter, 2, out checksum);
+ if (checksum == _hover_checksum) {
+ return false;
+ }
+ ClipboardMenuItem.hide_preview();
+ _hover_checksum = checksum;
+ controller.get_item_by_checksum.begin(checksum, null, (obj, res) => {
+ IClipboardItem? item = controller.get_item_by_checksum.end(res);
+ if (item != null) {
+ ClipboardMenuItem.show_preview_for(item);
+ }
+ });
+ }
+ }
+ else {
+ _hover_checksum = "";
+ ClipboardMenuItem.hide_preview();
+ }
+ return false;
+ });
+ tree_view.leave_notify_event.connect(() => {
+ _hover_checksum = "";
+ ClipboardMenuItem.hide_preview();
+ return false;
+ });
+
+ Gtk.Box list_and_buttons = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
+
+ Gtk.ScrolledWindow scroll = new Gtk.ScrolledWindow(null, null);
+ scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
+ scroll.add(tree_view);
+ list_and_buttons.pack_start(scroll, true, true, 0);
+
+ Gtk.Box side_buttons = new Gtk.Box(Gtk.Orientation.VERTICAL, 4);
+ Gtk.Button up_button = new Gtk.Button.with_label("\xe2\x96\xb2");
+ Gtk.Button down_button = new Gtk.Button.with_label("\xe2\x96\xbc");
+ Gtk.Button remove_button = new Gtk.Button.with_label(_("Unpin"));
+
+ up_button.clicked.connect(() => {
+ Gtk.TreeModel model;
+ Gtk.TreeIter iter;
+ if (tree_view.get_selection().get_selected(out model, out iter)) {
+ Gtk.TreeIter prev = iter;
+ if (store.iter_previous(ref prev)) {
+ store.swap(iter, prev);
+ save_pinned_order(store, controller);
+ }
+ }
+ });
+
+ down_button.clicked.connect(() => {
+ Gtk.TreeModel model;
+ Gtk.TreeIter iter;
+ if (tree_view.get_selection().get_selected(out model, out iter)) {
+ Gtk.TreeIter next = iter;
+ if (store.iter_next(ref next)) {
+ store.swap(iter, next);
+ save_pinned_order(store, controller);
+ }
+ }
+ });
+
+ remove_button.clicked.connect(() => {
+ Gtk.TreeModel model;
+ Gtk.TreeIter iter;
+ if (tree_view.get_selection().get_selected(out model, out iter)) {
+ string checksum;
+ model.get(iter, 2, out checksum);
+ ClipboardMenuItem.hide_preview();
+ Gtk.TreePath? path = store.get_path(iter);
+ int removed_index = path != null ? path.get_indices()[0] : -1;
+ _inhibit_save = true;
+ store.remove(ref iter);
+ _inhibit_save = false;
+ _select_after_repopulate = removed_index;
+ controller.toggle_pin_item.begin(checksum);
+ }
+ });
+
+ side_buttons.pack_start(up_button, false, false, 0);
+ side_buttons.pack_start(down_button, false, false, 0);
+ side_buttons.pack_start(remove_button, false, false, 0);
+ list_and_buttons.pack_start(side_buttons, false, false, 0);
+
+ pinned_box.pack_start(list_and_buttons, true, true, 0);
+
+ controller.on_pinned_items_changed.connect(() => {
+ ClipboardMenuItem.hide_preview();
+ populate_pinned_list(store, controller);
+ });
+
+ Gtk.Entry add_entry = new Gtk.Entry();
+ add_entry.placeholder_text = _("Text to pin...");
+ Gtk.Button add_button = new Gtk.Button.with_label(_("Add"));
+ add_button.clicked.connect(() => {
+ string text = add_entry.get_text().strip();
+ if (text.length > 0) {
+ controller.add_and_pin_text.begin(text);
+ add_entry.set_text("");
+ }
+ });
+ add_entry.activate.connect(() => {
+ add_button.clicked();
+ });
+
+ Gtk.Box add_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
+ add_box.pack_start(add_entry, true, true, 0);
+ add_box.pack_start(add_button, false, false, 0);
+ pinned_box.pack_start(add_box, false, false, 0);
+
+ Gtk.Label tab_label = new Gtk.Label(_("Pinned"));
+ notebook.append_page(pinned_box, tab_label);
+ }
+
+ private void save_pinned_order(Gtk.ListStore store, Controller controller)
+ {
+ string[] checksums = {};
+ Gtk.TreeIter iter;
+ if (store.get_iter_first(out iter)) {
+ do {
+ string checksum;
+ store.get(iter, 2, out checksum);
+ checksums += checksum;
+ } while (store.iter_next(ref iter));
+ }
+ controller.set_pinned_checksums(checksums);
+ }
+
+ private void populate_pinned_list(Gtk.ListStore store, Controller controller)
+ {
+ _inhibit_save = true;
+ store.clear();
+ _inhibit_save = false;
+ controller.get_pinned_items.begin((obj, res) => {
+ List items = controller.get_pinned_items.end(res);
+ int count = 0;
+ foreach (IClipboardItem item in items) {
+ Gtk.TreeIter iter;
+ store.append(out iter);
+ Gtk.Image? image = item.get_image();
+ Gdk.Pixbuf? pixbuf = null;
+ if (image != null) {
+ pixbuf = image.get_pixbuf();
+ }
+ store.set(iter, 0, pixbuf, 1, item.get_label(), 2, item.get_checksum());
+ count++;
+ }
+ if (_select_after_repopulate >= 0 && _pinned_tree_view != null && count > 0) {
+ int idx = _select_after_repopulate;
+ if (idx >= count) {
+ idx = count - 1;
+ }
+ Gtk.TreePath path = new Gtk.TreePath.from_indices(idx);
+ _pinned_tree_view.get_selection().select_path(path);
+ _select_after_repopulate = -1;
+ }
+ });
+ }
+
/**
* Hide preferences view
*/
@@ -138,8 +341,10 @@ namespace Diodon
*/
public void reset()
{
+ ClipboardMenuItem.hide_preview();
+ _pinned_tree_view = null;
+ _select_after_repopulate = -1;
preferences = null;
}
}
}
-
diff --git a/libdiodon/text-clipboard-item.vala b/libdiodon/text-clipboard-item.vala
index 800df2b..46694f0 100644
--- a/libdiodon/text-clipboard-item.vala
+++ b/libdiodon/text-clipboard-item.vala
@@ -117,6 +117,14 @@ namespace Diodon
return null; // no image available for text content
}
+ /**
+ * {@inheritDoc}
+ */
+ public Gdk.Pixbuf? get_preview_pixbuf()
+ {
+ return null;
+ }
+
/**
* {@inheritDoc}
*/