1 /* vim:set et sts=4 sw=4:
5 * Copyright(c) 2011 Peng Huang <shawn.p.huang@gmail.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or(at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307 USA
23 public class CandidatePanel : Gtk.HBox{
24 private bool m_vertical = true;
25 private Gtk.Window m_toplevel;
26 private Gtk.Box m_vbox;
28 private Gtk.Label m_preedit_label;
29 private Gtk.Label m_aux_label;
30 private CandidateArea m_candidate_area;
31 private HSeparator m_hseparator;
33 private Gdk.Rectangle m_cursor_location;
35 public signal void cursor_up();
36 public signal void cursor_down();
37 public signal void page_up();
38 public signal void page_down();
39 public signal void candidate_clicked(uint index,
43 public CandidatePanel() {
44 // Call base class constructor
46 name : "IBusCandidate",
50 m_toplevel = new Gtk.Window(Gtk.WindowType.POPUP);
51 m_toplevel.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
52 m_toplevel.button_press_event.connect((w, e) => {
53 if (e.button != 1 || (e.state & Gdk.ModifierType.CONTROL_MASK) == 0)
55 set_vertical(!m_vertical);
59 Handle handle = new Handle();
60 handle.set_visible(true);
61 pack_start(handle, false, false, 0);
63 m_vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
64 m_vbox.set_visible(true);
65 pack_start(m_vbox, false, false, 0);
72 public void set_vertical(bool vertical) {
73 if (m_vertical == vertical)
75 m_vertical = vertical;
76 m_candidate_area.set_vertical(vertical);
79 private void set_orientation(IBus.Orientation orientation) {
80 switch (orientation) {
81 case IBus.Orientation.VERTICAL:
82 m_candidate_area.set_vertical(true);
84 case IBus.Orientation.HORIZONTAL:
85 m_candidate_area.set_vertical(false);
87 case IBus.Orientation.SYSTEM:
88 m_candidate_area.set_vertical(m_vertical);
93 public void set_cursor_location(int x, int y, int width, int height) {
94 Gdk.Rectangle location = Gdk.Rectangle(){
95 x = x, y = y, width = width, height = height };
96 if (m_cursor_location == location)
98 m_cursor_location = location;
99 adjust_window_position();
102 private void set_labels(IBus.Text[] labels) {
103 m_candidate_area.set_labels(labels);
106 public void set_preedit_text(IBus.Text? text, uint cursor) {
108 m_preedit_label.set_text(text.get_text());
109 m_preedit_label.show();
111 m_preedit_label.set_text("");
112 m_preedit_label.hide();
117 public void set_auxiliary_text(IBus.Text? text) {
119 m_aux_label.set_text(text.get_text());
122 m_aux_label.set_text("");
128 public void set_lookup_table(IBus.LookupTable? table) {
129 IBus.Text[] candidates = {};
130 uint cursor_in_page = 0;
131 bool show_cursor = true;
132 IBus.Text[] labels = {};
133 IBus.Orientation orientation = IBus.Orientation.SYSTEM;
136 uint page_size = table.get_page_size();
137 uint ncandidates = table.get_number_of_candidates();
138 uint cursor = table.get_cursor_pos();
139 cursor_in_page = table.get_cursor_in_page();
140 show_cursor = table.is_cursor_visible();
142 uint page_start_pos = cursor / page_size * page_size;
143 uint page_end_pos = uint.min(page_start_pos + page_size, ncandidates);
144 for (uint i = page_start_pos; i < page_end_pos; i++)
145 candidates += table.get_candidate(i);
147 for (uint i = 0; i < page_size; i++) {
148 IBus.Text? label = table.get_label(i);
153 orientation = (IBus.Orientation)table.get_orientation();
156 m_candidate_area.set_candidates(candidates, cursor_in_page, show_cursor);
160 // Do not change orientation if table is null to avoid recreate
162 set_orientation(orientation);
165 if (candidates.length != 0)
166 m_candidate_area.show_all();
168 m_candidate_area.hide();
173 private void update() {
174 if (m_candidate_area.get_visible() ||
175 m_preedit_label.get_visible() ||
176 m_aux_label.get_visible())
181 if (m_aux_label.get_visible() &&
182 (m_candidate_area.get_visible() || m_preedit_label.get_visible()))
188 public override void get_preferred_width(out int minimum_width, out int natural_width) {
189 base.get_preferred_width(out minimum_width, out natural_width);
190 m_toplevel.resize(1, 1);
193 public override void get_preferred_height(out int minimum_width, out int natural_width) {
194 base.get_preferred_height(out minimum_width, out natural_width);
195 m_toplevel.resize(1, 1);
198 private void create_ui() {
199 m_preedit_label = new Gtk.Label(null);
200 m_preedit_label.set_size_request(20, -1);
201 m_preedit_label.set_alignment(0.0f, 0.5f);
202 m_preedit_label.set_padding(8, 0);
203 m_preedit_label.set_no_show_all(true);
205 m_aux_label = new Gtk.Label(null);
206 m_aux_label.set_size_request(20, -1);
207 m_aux_label.set_alignment(0.0f, 0.5f);
208 m_aux_label.set_padding(8, 0);
209 m_aux_label.set_no_show_all(true);
211 m_candidate_area = new CandidateArea(m_vertical);
212 m_candidate_area.candidate_clicked.connect(
213 (w, i, b, s) => candidate_clicked(i, b, s));
214 m_candidate_area.page_up.connect((c) => page_up());
215 m_candidate_area.page_down.connect((c) => page_down());
216 m_candidate_area.cursor_up.connect((c) => cursor_up());
217 m_candidate_area.cursor_down.connect((c) => cursor_down());
219 m_hseparator = new HSeparator();
220 m_hseparator.set_visible(true);
225 private void pack_all_widgets() {
226 m_vbox.pack_start(m_preedit_label, false, false, 4);
227 m_vbox.pack_start(m_aux_label, false, false, 4);
228 m_vbox.pack_start(m_hseparator, false, false, 0);
229 m_vbox.pack_start(m_candidate_area, false, false, 0);
232 public new void show() {
233 m_toplevel.show_all();
236 public new void hide() {
240 private void move(int x, int y) {
241 m_toplevel.move(x, y);
244 private void adjust_window_position() {
245 Gdk.Point cursor_right_bottom = {
246 m_cursor_location.x + m_cursor_location.width,
247 m_cursor_location.y + m_cursor_location.height
250 Gtk.Allocation allocation;
251 m_toplevel.get_allocation(out allocation);
252 Gdk.Point window_right_bottom = {
253 cursor_right_bottom.x + allocation.width,
254 cursor_right_bottom.y + allocation.height
257 Gdk.Window root = Gdk.get_default_root_window();
258 int root_width = root.get_width();
259 int root_height = root.get_height();
262 if (window_right_bottom.x > root_width)
263 x = root_width - allocation.width;
265 x = cursor_right_bottom.x;
267 if (window_right_bottom.y > root_height)
268 y = m_cursor_location.y - allocation.height;
270 y = cursor_right_bottom.y;