aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/chat_input/occupants_tab_completer.vala
blob: 6d2a74345c60afa9701601526ca252ff2afe033b (plain) (blame)
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
115
116
117
118
119
120
121
122
123
124
125
using Gdk;
using Gee;
using Gtk;

using Dino.Entities;
using Xmpp;

namespace Dino.Ui {

/**
 * - 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
 */
public 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.last_index_of(completions[index]),
                text.substring(0, text.length - 1).last_index_of("\n")
            );
            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(string? prefix = null) {
        Gee.List<string> ret = new ArrayList<string>();
        Gee.List<ContentItem> content_items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_n_latest(conversation, 10);
        for (int i = content_items.size - 1; i > 0; i--) {
            string resourcepart = content_items[i].jid.resourcepart;
            Jid? own_nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
            if (resourcepart != null && resourcepart != "" && (own_nick == null || resourcepart != own_nick.resourcepart) && !ret.contains(resourcepart)) {
                if (prefix != null && !resourcepart.to_string().down().has_prefix(prefix.down())) continue;
                ret.add(resourcepart);
            }
        }
        return ret;
    }

    private Gee.List<string> generate_completions_from_occupants(string prefix) {
        // First suggest nicks that have recently writen something
        Gee.List<string> ret = generate_completions_from_messages(prefix);

        // Then, suggest other nicks in alphabetical order
        Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(conversation.counterpart, conversation.account);
        Gee.List<string> filtered_occupants = new ArrayList<string>();
        if (occupants != null) {
            foreach (Jid jid in occupants) {
                string resourcepart = jid.resourcepart.to_string();
                if (resourcepart.down().has_prefix(prefix.down()) && !ret.contains(resourcepart)) {
                    filtered_occupants.add(resourcepart);
                }
            }
        }
        filtered_occupants.sort();

        ret.add_all(filtered_occupants);
        return ret;
    }
}

}