1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
using Gdk;
using Gee;
using Gtk;
using Dino.Entities;
namespace Dino.Ui.ChatInput {
/**
* - With given prefix: Complete from occupant list (sorted lexicographically)
* - W/o prefix: Complete from received messages (most recent first)
* - At the start (with ",") and in the middle of a text
* - Backwards tabbing
*/
class OccupantsTabCompletor {
private StreamInteractor stream_interactor;
private Conversation? conversation;
private TextView text_input;
private Gee.List<string> completions = new ArrayList<string>();
private bool active = false;
private int index = -1;
public OccupantsTabCompletor(StreamInteractor stream_interactor, TextView text_input) {
this.stream_interactor = stream_interactor;
this.text_input = text_input;
text_input.key_press_event.connect(on_text_input_key_press);
}
public void initialize_for_conversation(Conversation conversation) {
this.conversation = conversation;
}
public bool on_text_input_key_press(EventKey event) {
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
if (event.keyval == Key.Tab || event.keyval == Key.ISO_Left_Tab) {
string text = text_input.buffer.text;
int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1;
string word = text.substring(start_index);
if (!active) {
if (word == "") {
completions = generate_completions_from_messages();
} else {
completions = generate_completions_from_occupants(word);
}
if (completions.size > 0) {
active = true;
index = -1;
}
}
if (event.keyval != Key.ISO_Group_Shift && active) {
text_input.buffer.text = next_completion(event.keyval == Key.ISO_Left_Tab);
return true;
}
} else if (event.keyval != Key.Shift_L && active) {
active = false;
}
}
return false;
}
private string next_completion(bool backwards) {
string text = text_input.buffer.text;
int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1;
string prev_completion = text.substring(start_index);
if (index > -1) {
start_index = int.max(
text.substring(0, text.length - 1).last_index_of(" "),
text.substring(0, text.length - 1).last_index_of("\n")
) + 1;
prev_completion = text.substring(start_index);
}
if (backwards) {
index = int.max(index, 0) - 1;
if (index < 0) index = completions.size - 1;
} else {
index = (index + 1) % (completions.size);
}
if (start_index == 0) {
return completions[index] + ", ";
} else {
return text.substring(0, text.length - prev_completion.length) + completions[index] + " ";
}
}
private Gee.List<string> generate_completions_from_messages() {
Gee.List<string> ret = new ArrayList<string>();
Gee.List<Message> messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages(conversation, 10);
for (int i = messages.size - 1; i > 0; i--) {
string resourcepart = messages[i].from.resourcepart;
string own_nick = stream_interactor.get_module(MucManager.IDENTITY).get_nick(conversation.counterpart, conversation.account);
if (resourcepart != null && resourcepart != "" && resourcepart != own_nick && !ret.contains(resourcepart)) {
ret.add(resourcepart);
}
}
return ret;
}
private Gee.List<string> generate_completions_from_occupants(string prefix) {
Gee.List<string> ret = new ArrayList<string>();
Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(conversation.counterpart, conversation.account);
if (occupants != null) {
foreach (Jid jid in occupants) {
if (jid.resourcepart.to_string().has_prefix(prefix)) ret.add(jid.resourcepart.to_string());
}
}
ret.sort();
return ret;
}
}
}
|