1
/* Copyright 2009-2010 Yorba Foundation
3
* This software is licensed under the GNU Lesser General Public License
4
* (version 2.1 or later). See the COPYING file in this distribution.
9
////////////////////////////////////////////////////////////
10
// Helper functions //
11
////////////////////////////////////////////////////////////
13
Gtk.TextIter get_insert_iter(Gtk.TextBuffer buffer) {
15
buffer.get_iter_at_mark(out iter, buffer.get_insert());
19
void get_line_start_end(Gtk.TextIter iter, out Gtk.TextIter start, out Gtk.TextIter end) {
21
start.set_line_offset(0);
26
void append_with_tag(Gtk.TextBuffer buffer, string text, Gtk.TextTag? tag) {
28
buffer.get_end_iter(out end);
30
buffer.insert_with_tags(end, text, -1, tag);
32
buffer.insert(end, text, -1);
35
void append(Gtk.TextBuffer buffer, string text) {
36
append_with_tag(buffer, text, null);
39
Gtk.TextIter iter_at_line_offset(Gtk.TextBuffer buffer, int line, int offset) {
40
// We must be careful: TextBuffer.get_iter_at_line_offset() will crash if we give it an
41
// offset greater than the length of the line.
43
buffer.get_iter_at_line(out iter, line);
44
int len = iter.get_chars_in_line() - 1; // subtract 1 for \n
45
if (len < 0) // no \n was present, e.g. in an empty file
47
int end = int.min(len, offset);
49
buffer.get_iter_at_line_offset(out ret, line, end);
53
unowned string buffer_contents(Gtk.TextBuffer buffer) {
56
buffer.get_bounds(out start, out end);
57
return buffer.get_text(start, end, true);
60
Gtk.MenuItem get_menu_item(Gtk.UIManager manager, string path) {
61
Gtk.MenuItem item = (Gtk.MenuItem) manager.get_widget(path);
66
public void show_error_dialog(string message) {
67
Gtk.MessageDialog err_dialog = new Gtk.MessageDialog(null, Gtk.DialogFlags.MODAL,
68
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
70
err_dialog.set_title("Error");
75
string get_full_line_from_text_iter(Gtk.TextIter iter) {
76
// Move the iterator back to the beginning of its line
77
iter.backward_chars(iter.get_line_offset());
78
// Get an iterator at the end of the line
79
Gtk.TextIter end = iter;
82
return iter.get_text(end);
85
void get_coords_at_buffer_offset(Gedit.Window window, int offset, bool above, bool beside,
86
out int x, out int y) {
87
Gedit.Document buffer = window.get_active_document();
88
Gtk.TextIter method_iter;
89
buffer.get_iter_at_offset(out method_iter, offset);
91
Gedit.View active_view = window.get_active_view();
93
active_view.get_iter_location(method_iter, out rect);
95
active_view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, rect.x, rect.y,
96
out win_x, out win_y);
97
int widget_x = active_view.allocation.x;
98
int widget_y = active_view.allocation.y;
100
window.window.get_origin(out orig_x, out orig_y);
102
x = win_x + widget_x + orig_x;
103
y = win_y + widget_y + orig_y;
104
x += beside ? rect.height : 0;
105
y -= above ? rect.height : 0;
108
////////////////////////////////////////////////////////////
110
////////////////////////////////////////////////////////////
113
weak Gedit.Window parent;
116
Gtk.TextMark method_mark;
120
public Tooltip(Gedit.Window parent_win) {
123
tip_text = new Gtk.Label("");
124
window = new Gtk.Window(Gtk.WindowType.POPUP);
126
window.add(tip_text);
127
window.set_default_size(1, 1);
128
window.set_transient_for(parent);
129
window.set_destroy_with_parent(true);
131
Gdk.Color background;
132
Gdk.Color.parse("#FFFF99", out background);
133
window.modify_bg(Gtk.StateType.NORMAL, background);
136
public void show(string qualified_method_name, string prototype, int method_pos) {
137
method_name = qualified_method_name;
140
Gedit.Document document = parent.get_active_document();
141
Gtk.TextIter method_iter;
142
document.get_iter_at_offset(out method_iter, method_pos);
143
method_mark = document.create_mark(null, method_iter, true);
144
tip_text.set_text(prototype);
147
get_coords_at_buffer_offset(parent, method_pos, true, false, out x, out y);
157
assert(!method_mark.get_deleted());
158
Gtk.TextBuffer doc = method_mark.get_buffer();
159
doc.delete_mark(method_mark);
165
public bool is_visible() {
169
public string get_method_line() {
170
assert(!method_mark.get_deleted());
171
Gtk.TextBuffer doc = method_mark.get_buffer();
173
doc.get_iter_at_mark(out iter, method_mark);
174
return get_full_line_from_text_iter(iter);
177
public Gtk.TextIter get_iter_at_method() {
178
assert(!method_mark.get_deleted());
179
Gtk.TextBuffer doc = method_mark.get_buffer();
181
doc.get_iter_at_mark(out iter, method_mark);
185
public string get_method_name() {
190
class ProgressBarDialog : Gtk.Window {
193
public ProgressBarDialog(Gtk.Window parent_win, string text) {
194
bar = new Gtk.ProgressBar();
195
Gtk.VBox vbox = new Gtk.VBox(true, 0);
196
Gtk.HBox hbox = new Gtk.HBox(true, 0);
199
bar.set_size_request(226, 25);
200
set_size_request(250, 49);
202
vbox.pack_start(bar, true, false, 0);
203
hbox.pack_start(vbox, true, false, 0);
207
set_resizable(false);
208
set_transient_for(parent_win);
209
set_position(Gtk.WindowPosition.CENTER_ON_PARENT);
214
public void set_percentage(double percent) {
215
bar.set_fraction(percent);
218
public void close() {
223
class SignalConnection : Object {
224
public class SignalIDPair {
225
public weak Object object;
228
public SignalIDPair(Object object, ulong id) {
229
this.object = object;
234
public weak Object base_instance;
235
ArrayList<SignalIDPair> instance_signal_id_pair;
237
public SignalConnection(Object base_instance) {
238
this.base_instance = base_instance;
239
instance_signal_id_pair = new ArrayList<SignalIDPair>();
242
~SignalConnection() {
243
foreach (SignalIDPair pair in instance_signal_id_pair) {
244
if (SignalHandler.is_connected(pair.object, pair.id))
245
SignalHandler.disconnect(pair.object, pair.id);
249
public void add_signal(Object instance, string signal_name, Callback cb, void *data,
250
bool after = false) {
253
id = Signal.connect_after(instance, signal_name, cb, data);
254
else id = Signal.connect(instance, signal_name, cb, data);
255
instance_signal_id_pair.add(new SignalIDPair(instance, id));
259
class ListViewString : Object {
261
Gtk.TreeView treeview;
262
Gtk.TreeViewColumn column_view;
263
public Gtk.ScrolledWindow scrolled_window;
265
public signal void row_activated();
266
public signal void received_focus(Gtk.TreePath? path);
268
public ListViewString(Gtk.TreeViewColumnSizing sizing, int fixed_width) {
269
list = new Gtk.ListStore(1, GLib.Type.from_name("gchararray"));
271
Gtk.CellRendererText renderer = new Gtk.CellRendererText();
272
if (sizing == Gtk.TreeViewColumnSizing.FIXED)
273
renderer.ellipsize = Pango.EllipsizeMode.END;
274
column_view = new Gtk.TreeViewColumn();
275
column_view.pack_start(renderer, true);
276
column_view.set_sizing(sizing);
277
column_view.set_fixed_width(fixed_width);
278
column_view.set_attributes(renderer, "text", 0, null);
279
treeview = new Gtk.TreeView.with_model(list);
280
treeview.append_column(column_view);
281
treeview.headers_visible = false;
282
treeview.focus_in_event.connect(on_received_focus);
284
scrolled_window = new Gtk.ScrolledWindow(null, null);
285
scrolled_window.hscrollbar_policy = Gtk.PolicyType.NEVER;
286
scrolled_window.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
287
scrolled_window.add(treeview);
289
Signal.connect(treeview, "row-activated", (Callback) row_activated_callback, this);
292
bool on_received_focus() {
293
Gtk.TreePath? path = get_path_at_cursor();
294
received_focus(path);
298
static void row_activated_callback(Gtk.TreeView view, Gtk.TreePath path,
299
Gtk.TreeViewColumn column, ListViewString list) {
300
list.row_activated();
303
public void clear() {
307
public void append(string item) {
308
Gtk.TreeIter iterator;
309
list.append(out iterator);
310
list.set(iterator, 0, item, -1);
314
return list.iter_n_children(null);
317
public void set_vscrollbar_policy(Gtk.PolicyType policy) {
318
scrolled_window.vscrollbar_policy = policy;
321
/////////////////////////////////
322
// Treeview selection movement //
323
/////////////////////////////////
325
void select(Gtk.TreePath path, bool scroll = true) {
326
treeview.set_cursor(path, null, false);
328
treeview.scroll_to_cell(path, null, false, 0.0f, 0.0f);
331
void scroll_to_and_select_cell(double adjustment_value, int y) {
332
scrolled_window.vadjustment.set_value(adjustment_value);
336
treeview.get_path_at_pos(0, y, out path, null, out cell_x, out cell_y);
340
Gtk.TreePath? get_path_at_cursor() {
342
Gtk.TreeViewColumn column;
343
treeview.get_cursor(out path, out column);
347
public Gtk.TreePath select_first_cell() {
348
treeview.get_vadjustment().set_value(0);
349
Gtk.TreePath start = new Gtk.TreePath.first();
354
public void select_last_cell() {
355
// The list index is 0-based, the last element is 'size - 1'
356
int size = list.iter_n_children(null) - 1;
357
select(new Gtk.TreePath.from_string(size.to_string()));
360
public void select_previous() {
361
Gtk.TreePath path = get_path_at_cursor();
366
else select_last_cell();
370
public void select_next() {
371
Gtk.TreePath path = get_path_at_cursor();
377
// Make sure the next element iterator is valid
378
if (list.get_iter(out iter, path))
380
else select_first_cell();
384
public void page_up() {
385
// Save the current y position of the selection
386
Gtk.TreePath cursor_path = get_path_at_cursor();
388
treeview.get_cell_area(cursor_path, null, out rect);
390
// Don't wrap page_up
391
if (!cursor_path.prev()) {
395
double adjust_value = scrolled_window.vadjustment.get_value();
396
double page_size = scrolled_window.vadjustment.get_page_size();
397
// If the current page is the top page, just select the top cell
398
if (adjust_value == scrolled_window.vadjustment.lower) {
403
// it is 'y + 1' because only 'y' would be the element before the one we want
404
scroll_to_and_select_cell(adjust_value - (page_size - rect.height), rect.y + 1);
407
public void page_down() {
408
// Save the current y position of the selection
409
Gtk.TreePath cursor_path = get_path_at_cursor();
411
treeview.get_cell_area(cursor_path, null, out rect);
413
// Don't wrap page_down
416
if (!list.get_iter(out iter, cursor_path)) {
420
double adjust_value = scrolled_window.vadjustment.get_value();
421
double page_size = scrolled_window.vadjustment.get_page_size();
422
// If the current page is the bottom page, just select the last cell
423
if (adjust_value >= scrolled_window.vadjustment.upper - page_size) {
428
scroll_to_and_select_cell(adjust_value + (page_size - rect.height), rect.y + 1);
431
string? get_item_at_path(Gtk.TreePath path) {
433
if (!list.get_iter(out iter, path))
437
list.get_value(iter, 0, out v);
439
return v.get_string().substring(0);
442
public string get_selected_item() {
444
Gtk.TreeViewColumn column;
445
treeview.get_cursor(out path, out column);
447
return get_item_at_path(path);
450
bool path_exists(Gtk.TreePath path) {
452
if (list.get_iter(out iter, path))
457
public void select_path(Gtk.TreePath path) {
458
if (path_exists(path))
462
void insert_before(string item, Gtk.TreePath path) {
463
Gtk.TreeIter new_iter;
464
Gtk.TreeIter sibling;
465
list.get_iter(out sibling, path);
466
list.insert_before(out new_iter, sibling);
467
list.set(new_iter, 0, item, -1);
470
void remove(Gtk.TreePath path) {
472
list.get_iter(out iter, path);
476
public void collate(string[] new_list) {
477
Gtk.TreePath current_path = new Gtk.TreePath.first();
478
int new_list_index = 0;
480
string? item = get_item_at_path(current_path);
481
if (item == null || new_list_index == new_list.length)
483
string new_item = new_list[new_list_index];
485
int result = strcmp(item, new_item);
487
remove(current_path);
490
insert_before(new_list[new_list_index], current_path);
496
// The rest of the items in the old list are not present, so remove them
498
if (!path_exists(current_path))
500
remove(current_path);
503
// The rest of the items in the other list must be new, so add them
504
for (; new_list_index < new_list.length; ++new_list_index)
505
append(new_list[new_list_index]);
511
//// Gedit helper functions ////
513
string? document_filename(Gedit.Document document) {
514
string uri = document.get_uri();
518
return Filename.from_uri(uri);
519
} catch (ConvertError e) { return null; }
522
Gedit.Tab? find_tab(string filename, out Gedit.Window window) {
523
string uri = filename_to_uri(filename);
525
foreach (Gedit.Window w in Gedit.App.get_default().get_windows()) {
526
Gedit.Tab tab = w.get_tab_from_uri(uri);