1//
2//  Copyright (C) 2010-2011 Robert Dyer
3//
4//  This file is part of Plank.
5//
6//  Plank is free software: you can redistribute it and/or modify
7//  it under the terms of the GNU General Public License as published by
8//  the Free Software Foundation, either version 3 of the License, or
9//  (at your option) any later version.
10//
11//  Plank is distributed in the hope that it will be useful,
12//  but WITHOUT ANY WARRANTY; without even the implied warranty of
13//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14//  GNU General Public License for more details.
15//
16//  You should have received a copy of the GNU General Public License
17//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18//
19
20using Plank;
21
22namespace Docky
23{
24	public class ClippyDockItem : DockletItem
25	{
26		Gtk.Clipboard clipboard;
27		Gee.ArrayList<string> clips;
28		int cur_position = 0;
29		ulong handler_id = 0U;
30
31		/**
32		 * {@inheritDoc}
33		 */
34		public ClippyDockItem.with_dockitem_file (GLib.File file)
35		{
36			GLib.Object (Prefs: new ClippyPreferences.with_file (file));
37		}
38
39		construct
40		{
41			unowned ClippyPreferences prefs = (ClippyPreferences) Prefs;
42
43			Icon = "edit-cut";
44
45			if (prefs.TrackMouseSelections)
46				clipboard = Gtk.Clipboard.get (Gdk.Atom.intern ("PRIMARY", true));
47			else
48				clipboard = Gtk.Clipboard.get (Gdk.Atom.intern ("CLIPBOARD", true));
49
50			clips = new Gee.ArrayList<string> ();
51			handler_id = clipboard.owner_change.connect (check_clipboard);
52
53			updated ();
54		}
55
56		~ClippyDockItem ()
57		{
58			if (handler_id > 0U)
59				clipboard.disconnect (handler_id);
60		}
61
62		[CCode (instance_pos = -1)]
63		void check_clipboard (Gtk.Clipboard clipboard, Gdk.Event event)
64		{
65			clipboard.request_text ((Gtk.ClipboardTextReceivedFunc) clipboard_text_received);
66		}
67
68		[CCode (instance_pos = -1)]
69		void clipboard_text_received (Gtk.Clipboard clipboard, string? text)
70		{
71			if (text == null || text == "")
72				return;
73
74			unowned ClippyPreferences prefs = (ClippyPreferences) Prefs;
75
76			clips.remove (text);
77			clips.add (text);
78			while (clips.size > prefs.MaxEntries)
79				clips.remove_at (0);
80
81			cur_position = clips.size;
82
83			updated ();
84		}
85
86		void updated ()
87		{
88			if (clips.size == 0)
89				Text = _("Clipboard is currently empty.");
90			else if (cur_position == 0 || cur_position > clips.size)
91				Text = get_entry_at (clips.size);
92			else
93				Text = get_entry_at (cur_position);
94		}
95
96		string get_entry_at (int pos)
97		{
98			return clips.get (pos - 1).replace ("\n", "").replace ("\t", "");
99		}
100
101		void copy_entry_at (int pos)
102		{
103			if (pos < 1 || pos > clips.size)
104				return;
105
106			var str = clips.get (pos - 1);
107			clipboard.set_text (str, (int) str.length);
108
109			updated ();
110		}
111
112		void copy_entry ()
113		{
114			if (cur_position == 0)
115				copy_entry_at (clips.size);
116			else
117				copy_entry_at (cur_position);
118		}
119
120		void clear ()
121		{
122			// Make sure we own the current clipboard content,
123			// so we are allowed to clear it
124			clipboard.set_text ("", 0);
125
126			clipboard.clear ();
127			clips.clear ();
128			cur_position = 0;
129
130			updated ();
131		}
132
133		protected override AnimationType on_scrolled (Gdk.ScrollDirection direction, Gdk.ModifierType mod, uint32 event_time)
134		{
135			if (direction == Gdk.ScrollDirection.UP)
136				cur_position++;
137			else
138				cur_position--;
139
140			if (cur_position < 1)
141				cur_position = clips.size;
142			else if (cur_position > clips.size)
143				cur_position = 1;
144
145			updated ();
146
147			return AnimationType.NONE;
148		}
149
150		protected override AnimationType on_clicked (PopupButton button, Gdk.ModifierType mod, uint32 event_time)
151		{
152			if (button == PopupButton.LEFT && clips.size > 0) {
153				copy_entry ();
154				return AnimationType.BOUNCE;
155			}
156
157			return AnimationType.NONE;
158		}
159
160		public override Gee.ArrayList<Gtk.MenuItem> get_menu_items ()
161		{
162			var items = new Gee.ArrayList<Gtk.MenuItem> ();
163
164			for (var i = clips.size ; i > 0; i--) {
165				var item = create_literal_menu_item (clips.get (i - 1), "edit-cut");
166				var pos = i;
167				item.activate.connect (() => {
168					copy_entry_at (pos);
169				});
170				items.add (item);
171			}
172
173			if (clips.size > 0) {
174				var item = create_menu_item (_("_Clear"), "edit-clear-all", true);
175				item.activate.connect (clear);
176				items.add (item);
177			}
178
179			return items;
180		}
181	}
182}
183