~ubuntu-branches/ubuntu/trusty/reinteract/trusty

« back to all changes in this revision

Viewing changes to lib/reinteract/completion_popup.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2009-03-28 00:53:14 UTC
  • Revision ID: james.westby@ubuntu.com-20090328005314-ramzoo0q6r8rmwuc
Tags: upstream-0.5.0
ImportĀ upstreamĀ versionĀ 0.5.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2007 Owen Taylor
 
2
#
 
3
# This file is part of Reinteract and distributed under the terms
 
4
# of the BSD license. See the file COPYING in the Reinteract
 
5
# distribution for full details.
 
6
#
 
7
########################################################################
 
8
 
 
9
import gtk
 
10
import inspect
 
11
 
 
12
from popup import Popup
 
13
from doc_popup import DocPopup
 
14
from data_format import is_data_object
 
15
from shell_buffer import ADJUST_NONE
 
16
 
 
17
# Space between the line of text where the cursor is and the popup
 
18
VERTICAL_GAP = 5
 
19
 
 
20
# Size of the popup
 
21
WIDTH = 300
 
22
HEIGHT = 300
 
23
 
 
24
# If the user is just typing, the number of characters we require before
 
25
# we start suggesting completions
 
26
SPONTANEOUS_MIN_LENGTH = 3
 
27
 
 
28
class CompletionPopup(Popup):
 
29
    
 
30
    """Class implementing a completion popup for ShellView
 
31
 
 
32
    This class encapsulates the user interface logic for completion
 
33
    popups. The actual code to determine possible completions lives in
 
34
    tokenized_statement.py. 
 
35
    
 
36
    """
 
37
    
 
38
    def __init__(self, view):
 
39
        Popup.__init__(self)
 
40
        self.set_size_request(WIDTH, HEIGHT)
 
41
        
 
42
        self.__view = view
 
43
 
 
44
        sw = gtk.ScrolledWindow()
 
45
        self.add(sw)
 
46
 
 
47
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
48
        
 
49
        self.__tree_model = gtk.ListStore(str, str, object)
 
50
        self.__tree = gtk.TreeView(self.__tree_model)
 
51
        self.__tree.set_headers_visible(False)
 
52
        
 
53
        self.__tree.get_selection().connect('changed', self.__on_selection_changed)
 
54
        self.__tree.connect('row-activated', self.__on_row_activated)
 
55
 
 
56
        cell = gtk.CellRendererText()
 
57
        column = gtk.TreeViewColumn(None, cell, text=0)
 
58
        self.__tree.append_column(column)
 
59
 
 
60
        sw.add(self.__tree)
 
61
        sw.show_all()
 
62
 
 
63
        self.set_default_size(WIDTH, HEIGHT)
 
64
 
 
65
        # A small amount of background shows between the scrollbar and the list;
 
66
        # which looks ugly if it is the only gray thing in the window, so change
 
67
        # the window background to white
 
68
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(65535, 65535, 65535))
 
69
 
 
70
        self.__doc_popup= DocPopup(fixed_width=True, fixed_height=True, max_height=HEIGHT, can_focus=False)
 
71
 
 
72
        self._in_change = False
 
73
        self.spontaneous = False
 
74
        self.showing = False
 
75
 
 
76
    def __update_completions(self, spontaneous=False):
 
77
        buf = self.__view.get_buffer()
 
78
 
 
79
        self.__in_change = True
 
80
        self.__tree_model.clear()
 
81
        line, offset = buf.iter_to_pos(buf.get_iter_at_mark(buf.get_insert()), adjust=ADJUST_NONE)
 
82
        if line == None:
 
83
            completions = []
 
84
        else:
 
85
            if spontaneous:
 
86
                min_length = SPONTANEOUS_MIN_LENGTH
 
87
            else:
 
88
                min_length = 0
 
89
            completions = buf.worksheet.find_completions(line, offset, min_length)
 
90
        for display, completion, obj in completions:
 
91
            self.__tree_model.append([display, completion, obj])
 
92
 
 
93
        if len(completions) > 0:
 
94
            self.__tree.set_cursor(0)
 
95
        self.__in_change = False
 
96
        self.__update_doc_popup()
 
97
 
 
98
    def __update_position(self):
 
99
        buf = self.__view.get_buffer()
 
100
        
 
101
        self.position_at_location(self.__view,
 
102
                                  buf.get_iter_at_mark(buf.get_insert()))
 
103
 
 
104
    def __update_doc_popup(self):
 
105
        if not self.showing:
 
106
            self.__doc_popup.popdown()
 
107
            return
 
108
 
 
109
        model, iter = self.__tree.get_selection().get_selected()
 
110
        if not iter:
 
111
            self.__doc_popup.popdown()
 
112
            return
 
113
 
 
114
        obj = model.get_value(iter, 2)
 
115
 
 
116
        # Long term it would be nice to preview the value of the
 
117
        # object, but it's distracting to show the class docs on int
 
118
        # for every integer constant, etc, which is what the DocPopup
 
119
        # does currently.
 
120
        if (obj == None or is_data_object(obj)):
 
121
            self.__doc_popup.popdown()
 
122
            return
 
123
        
 
124
        self.__doc_popup.set_target(obj)
 
125
        self.__doc_popup.popup()
 
126
 
 
127
    def __insert_completion(self, iter):
 
128
        completion = self.__tree_model.get_value(iter, 1)
 
129
        obj = self.__tree_model.get_value(iter, 2)
 
130
 
 
131
        buf = self.__view.get_buffer()
 
132
        default_editable = self.__view.get_editable()
 
133
 
 
134
        buf.insert_interactive_at_cursor(completion, default_editable)
 
135
        if inspect.isclass(obj) or inspect.isroutine(obj):
 
136
            # Show the doc popup to give the user information about what arguments
 
137
            # are posssible/required
 
138
            self.__view.show_doc_popup()
 
139
 
 
140
            # Insert a () and put the cursor in the middle
 
141
            buf.insert_interactive_at_cursor('(', default_editable)
 
142
            insert = buf.get_iter_at_mark(buf.get_insert())
 
143
            mark_between_parens = buf.create_mark(None, insert, left_gravity=True)
 
144
            buf.insert_interactive_at_cursor(')', default_editable)
 
145
            iter = buf.get_iter_at_mark(mark_between_parens)
 
146
            self.__view.highlight_arg_region(iter, iter)
 
147
            buf.place_cursor(iter)
 
148
            buf.delete_mark(mark_between_parens)
 
149
 
 
150
    def __insert_selected(self):
 
151
        model, iter = self.__tree.get_selection().get_selected()
 
152
        self.__insert_completion(iter)
 
153
            
 
154
    def __on_selection_changed(self, selection):
 
155
        if not self.__in_change:
 
156
            self.__update_doc_popup()
 
157
 
 
158
    def __on_row_activated(self, view, path, column):
 
159
        self.__insert_completion(self.__tree_model.get_iter(path))
 
160
        self.popdown()
 
161
 
 
162
    def popup(self, spontaneous=False):
 
163
        """Pop up the completion popup.
 
164
 
 
165
        If there are no possibilities completion at the insert cursor
 
166
        location, the popup is not popped up. If there is exactly one
 
167
        possibility and the spontaneous parameter is not provided , then
 
168
        completion is done immediately to that one possibility.
 
169
 
 
170
        @param spontaneous set to True if we're popping this up as a result
 
171
           of editing, rather than because of an explicit key shortcut.
 
172
 
 
173
        """
 
174
        
 
175
        self.__update_completions(spontaneous=spontaneous)
 
176
        num_completions = len(self.__tree_model)
 
177
        if num_completions == 0:
 
178
            return
 
179
        elif num_completions == 1 and not spontaneous:
 
180
            self.__insert_selected()
 
181
            return
 
182
        
 
183
        self.__update_position()
 
184
 
 
185
        self.spontaneous = spontaneous
 
186
 
 
187
        if self.showing:
 
188
            return
 
189
 
 
190
        self.show()
 
191
        self.showing = True
 
192
 
 
193
        self.__doc_popup.position_next_to_window(self)
 
194
        self.__update_doc_popup()
 
195
 
 
196
        self.focus()
 
197
 
 
198
    def update(self):
 
199
        """Update the completion popup after the cursor is moved, or text is inserted.
 
200
 
 
201
        If there are no completion possibilities at the cursor when this is called,
 
202
        the popup is popped down.
 
203
 
 
204
        """
 
205
        
 
206
        if not self.showing:
 
207
            return
 
208
        
 
209
        self.__update_completions(spontaneous=self.spontaneous)
 
210
        if len(self.__tree_model) == 0:
 
211
            self.popdown()
 
212
            return
 
213
        
 
214
        self.__update_position()
 
215
        
 
216
    def popdown(self):
 
217
        """Hide the completion if it is currently showing"""
 
218
 
 
219
        if not self.showing:
 
220
            return
 
221
 
 
222
        self.showing = False
 
223
        self.focused = False
 
224
        
 
225
        if self.__doc_popup.showing:
 
226
            self.__doc_popup.popdown()
 
227
        
 
228
        self.hide()
 
229
 
 
230
    def on_key_press_event(self, event):
 
231
        """Do key press handling while the popup is active.
 
232
 
 
233
        Returns True if the key press is handled, False otherwise.
 
234
 
 
235
        """
 
236
        
 
237
        if event.keyval == gtk.keysyms.Escape:
 
238
            self.popdown()
 
239
            return True
 
240
        elif event.keyval in (gtk.keysyms.KP_Enter, gtk.keysyms.Return):
 
241
            self.__insert_selected()
 
242
            self.popdown()
 
243
            return True
 
244
        # These keys are forwarded to the popup to move the selected row
 
245
        elif event.keyval in (gtk.keysyms.Up, gtk.keysyms.KP_Up,
 
246
                              gtk.keysyms.Down, gtk.keysyms.KP_Down,
 
247
                              gtk.keysyms.Page_Up, gtk.keysyms.KP_Page_Up,
 
248
                              gtk.keysyms.Page_Down, gtk.keysyms.KP_Page_Down):
 
249
            self.event(event)
 
250
            return True
 
251
 
 
252
        return False