1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: steshell.cpp
3 // Purpose: wxSTEditorShell
4 // Author: John Labenski
5 // Modified by:
6 // Created: 11/05/2002
7 // RCS-ID:
8 // Copyright: (c) John Labenski
9 // Licence: wxWidgets licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "precomp.h"
13
14 #include "wx/stedit/stedit.h"
15 #include "wx/stedit/steshell.h"
16
17 //-----------------------------------------------------------------------------
18 // wxSTEditorShell
19 //-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxSTEditorShell,wxSTEditor)20 IMPLEMENT_DYNAMIC_CLASS(wxSTEditorShell, wxSTEditor)
21
22 BEGIN_EVENT_TABLE(wxSTEditorShell, wxSTEditor)
23 EVT_KEY_DOWN ( wxSTEditorShell::OnKeyDown)
24 EVT_STC_UPDATEUI (wxID_ANY, wxSTEditorShell::OnSTCUpdateUI)
25 END_EVENT_TABLE()
26
27 void wxSTEditorShell::Init()
28 {
29 m_line_history_index = 0;
30 m_max_history_lines = 100;
31
32 m_max_lines = 10000; // arbitrary, seems reasonable
33 m_overflow_lines = 2000;
34
35 m_writeable_count = 0;
36 }
37
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)38 bool wxSTEditorShell::Create(wxWindow *parent, wxWindowID id,
39 const wxPoint& pos, const wxSize& size,
40 long style, const wxString& name)
41 {
42 if (!wxSTEditor::Create(parent, id, pos, size, style, name))
43 return false;
44
45 // set this up in case they don't want to bother with the preferences
46 SetMarginWidth(STE_MARGIN_NUMBER, 0);
47 SetMarginWidth(STE_MARGIN_FOLD, 0);
48 SetMarginWidth(PROMPT_MARGIN, 16);
49
50 SetMarginType(PROMPT_MARGIN, wxSTC_MARGIN_SYMBOL);
51 SetMarginMask(PROMPT_MARGIN, 1<<PROMPT_MARKER);
52 // after creation you can change this to whatever prompt you prefer
53 MarkerDefine(PROMPT_MARKER, wxSTC_MARK_ARROWS, *wxBLACK, wxColour(255,255,0));
54 return true;
55 }
56
~wxSTEditorShell()57 wxSTEditorShell::~wxSTEditorShell()
58 {
59 }
60
AppendText(const wxString & text)61 void wxSTEditorShell::AppendText(const wxString &text)
62 {
63 BeginWriteable(); // make it writeable
64
65 wxSTEditor::AppendText(text); // write the text
66 SetMaxLines(m_max_lines, m_overflow_lines); // check for line count overflow
67 GotoPos(GetLength()); // put cursor at end
68 EmptyUndoBuffer(); // don't let them undo what you wrote!
69 // but they can undo their own typing
70
71 EndWriteable(); // end the writeable state
72 }
73
SetPromptText(const wxString & text)74 void wxSTEditorShell::SetPromptText(const wxString& text)
75 {
76 BeginWriteable();
77 int length = GetLength();
78 int prompt_line = GetPromptLine();
79 int start_pos = PositionFromLine(prompt_line);
80 SetTargetStart(start_pos);
81 SetTargetEnd(length);
82 ReplaceTarget(text);
83 GotoPos(GetLength());
84 EndWriteable();
85 }
86
GetPromptText()87 wxString wxSTEditorShell::GetPromptText()
88 {
89 int prompt_line = GetPromptLine();
90 int start_pos = PositionFromLine(prompt_line);
91 int end_pos = GetLength();
92 wxString text(GetTextRange(start_pos, end_pos));
93 return text;
94 }
95
BeginWriteable(bool make_writeable)96 void wxSTEditorShell::BeginWriteable(bool make_writeable)
97 {
98 m_writeable_count++;
99 if (make_writeable && GetReadOnly())
100 SetReadOnly(false);
101 }
EndWriteable(bool check_ro)102 void wxSTEditorShell::EndWriteable(bool check_ro)
103 {
104 if (m_writeable_count > 0)
105 m_writeable_count--;
106
107 if (check_ro && (m_writeable_count == 0))
108 CheckReadOnly(true);
109 }
110
GetPromptLine()111 int wxSTEditorShell::GetPromptLine()
112 {
113 int total_lines = GetLineCount();
114 return MarkerPrevious(total_lines+1, (1<<PROMPT_MARKER));
115
116 // single line entry, return text on last line FIXME - double check this
117 // Scintilla doesn't complain if you enter a line greater than the length to get last prompt
118 //int marker = MarkerGet(total_lines);
119 //if (((marker & (1<<PROMPT_MARKER)) != 0)
120 //{
121 // text = GetLineText(total_lines); //.Strip(wxString::both);
122 //}
123 //else
124 //{
125 // int marker_line = MarkerPrevious(total_lines+1, (1<<PROMPT_MARKER));
126 //}
127 }
128
CaretOnPromptLine(STE_CaretPos_Type option)129 bool wxSTEditorShell::CaretOnPromptLine(STE_CaretPos_Type option)
130 {
131 int prompt_line = GetPromptLine();
132 bool on_last = (GetCurrentLine() >= prompt_line);
133
134 //wxPrintf(wxT("Caret on last line total %d current %d onlast %d\n"), total_lines, GetCurrentLine(), (int)on_last);
135
136 if (!on_last && (option != STE_CARET_MOVE_NONE))
137 {
138 if ((option & STE_CARET_MOVE_LASTLINE) != 0)
139 GotoLine(prompt_line);
140 else if ((option & STE_CARET_MOVE_ENDTEXT) != 0)
141 GotoPos(GetLength());
142 }
143
144 return GetCurrentLine() >= prompt_line;
145 }
146
CheckReadOnly(bool set)147 bool wxSTEditorShell::CheckReadOnly(bool set)
148 {
149 bool make_ro = !CaretOnPromptLine(STE_CARET_MOVE_NONE);
150
151 if (!make_ro)
152 {
153 // also check selection and make ro so they can't cut text not on last line
154 int prompt_line = GetPromptLine();
155 make_ro |= ((LineFromPosition(GetSelectionStart()) < prompt_line) ||
156 (LineFromPosition(GetSelectionEnd()) < prompt_line));
157 }
158
159 if (set && (make_ro != GetReadOnly()))
160 SetReadOnly(make_ro);
161
162 return make_ro;
163 }
164
CheckPrompt(bool set)165 bool wxSTEditorShell::CheckPrompt(bool set)
166 {
167 int total_lines = GetLineCount();
168 total_lines = wxMax(0, total_lines-1);
169 bool has_prompt = (MarkerGet(total_lines) & (1<<PROMPT_MARKER)) != 0;
170
171 if (set && !has_prompt)
172 {
173 MarkerAdd(total_lines, PROMPT_MARKER);
174 return true;
175 }
176
177 return has_prompt;
178 }
179
OnSTCUpdateUI(wxStyledTextEvent & event)180 void wxSTEditorShell::OnSTCUpdateUI(wxStyledTextEvent &event)
181 {
182 event.Skip();
183 if (m_writeable_count == 0)
184 CheckReadOnly(true);
185 }
186
GetNextHistoryLine(bool forwards,const wxString & line)187 wxString wxSTEditorShell::GetNextHistoryLine(bool forwards, const wxString &line)
188 {
189 int count = (int)m_lineHistoryArray.GetCount();
190
191 // no history, just return ""
192 if (count == 0)
193 return wxEmptyString;
194
195 // return current one if it's different
196 if ((m_line_history_index >= 0) && (m_line_history_index < count) &&
197 (line != m_lineHistoryArray[m_line_history_index]))
198 return m_lineHistoryArray[m_line_history_index];
199
200 if (forwards)
201 {
202 if (m_line_history_index >= count - 1)
203 {
204 m_line_history_index = (int)(count - 1); // fix it up
205 return wxEmptyString;
206 }
207
208 m_line_history_index++;
209 }
210 else // reverse
211 {
212 if (m_line_history_index < 1) // already checked for empty array
213 {
214 m_line_history_index = 0; // fix it up
215 return wxEmptyString;
216 }
217
218 m_line_history_index--;
219 }
220
221 return m_lineHistoryArray[m_line_history_index];
222 }
223
AddHistoryLine(const wxString & string,bool set_index_to_last)224 void wxSTEditorShell::AddHistoryLine(const wxString& string, bool set_index_to_last)
225 {
226 size_t count = m_lineHistoryArray.GetCount();
227
228 // don't add same line twice
229 if ((count > 0) && (string == m_lineHistoryArray[count-1]))
230 return;
231
232 m_lineHistoryArray.Add(string);
233 if (set_index_to_last)
234 m_line_history_index = (int)(m_lineHistoryArray.GetCount() - 1);
235
236 SetMaxHistoryLines(GetMaxHistoryLines()); // remove any extra
237 }
238
SetMaxHistoryLines(int max_lines)239 void wxSTEditorShell::SetMaxHistoryLines(int max_lines)
240 {
241 m_max_history_lines = max_lines;
242
243 int extra = int(m_lineHistoryArray.GetCount()) - m_max_history_lines;
244 if ((m_max_history_lines >= 0) && (extra > 0))
245 m_lineHistoryArray.RemoveAt(0, extra);
246
247 m_line_history_index = wxMin(m_line_history_index, int(m_lineHistoryArray.GetCount())-1);
248 }
249
SetMaxLines(int max_lines,int overflow_lines)250 bool wxSTEditorShell::SetMaxLines(int max_lines, int overflow_lines)
251 {
252 m_max_lines = max_lines;
253 m_overflow_lines = overflow_lines;
254 if (m_max_lines < 0) return false;
255
256 int total_lines = GetLineCount();
257 total_lines = wxMax(0, total_lines-1);
258
259 // delete lines when more than m_max_lines, you'll eventually crash otherwise
260 if (total_lines > m_max_lines + m_overflow_lines)
261 {
262 BeginWriteable();
263
264 int marker = MarkerGet(total_lines - m_max_lines);
265
266 SetTargetStart(0);
267 SetTargetEnd(PositionFromLine(total_lines - m_max_lines));
268 ReplaceTarget(wxEmptyString);
269
270 // wipe marker that has moved up if there shouldn't be a marker
271 if ((marker & (1<<PROMPT_MARKER)) == 0)
272 MarkerDelete(0, PROMPT_MARKER);
273
274 EndWriteable();
275 return true;
276 }
277
278 return false;
279 }
280
OnKeyDown(wxKeyEvent & event)281 void wxSTEditorShell::OnKeyDown(wxKeyEvent &event)
282 {
283 // don't steal any keys from the autocomplete dropdown
284 if (AutoCompActive())
285 {
286 event.Skip(true);
287 return;
288 }
289
290 event.Skip(false);
291 CheckReadOnly(true);
292
293 switch (event.GetKeyCode())
294 {
295 case WXK_UP : case WXK_NUMPAD_UP :
296 {
297 // you can scroll up through multiline entry
298 int current_line = GetCurrentLine();
299 int prompt_line = GetPromptLine();
300 if ((current_line < prompt_line) || (current_line > prompt_line))
301 break;
302
303 // up/down arrows go through the history buffer
304 wxString promptText = GetPromptText();
305 SetPromptText(GetNextHistoryLine(false, promptText));
306 return;
307 }
308 case WXK_DOWN : case WXK_NUMPAD_DOWN :
309 {
310 // you can scroll down through multiline entry
311 int total_lines = GetLineCount();
312 total_lines = wxMax(0, total_lines - 1);
313 int current_line = GetCurrentLine();
314 if (current_line < total_lines)
315 break;
316
317 // up/down arrows go through the history buffer
318 wxString promptText = GetPromptText();
319 SetPromptText(GetNextHistoryLine(true, promptText));
320 return;
321 }
322 case WXK_LEFT : case WXK_NUMPAD_LEFT :
323 {
324 int current_line = GetCurrentLine();
325 int prompt_line = GetPromptLine();
326 if (current_line >= prompt_line)
327 {
328 int caret_pos = 0;
329 GetCurLine(&caret_pos);
330 if (caret_pos < 1)
331 return;
332 }
333 break;
334 }
335
336 case WXK_PAGEUP : case WXK_NUMPAD_PAGEUP :
337 case WXK_PAGEDOWN : case WXK_NUMPAD_PAGEDOWN :
338 case WXK_END : case WXK_NUMPAD_END :
339 case WXK_HOME : case WXK_NUMPAD_HOME :
340 case WXK_RIGHT : case WXK_NUMPAD_RIGHT :
341
342 case WXK_SHIFT :
343 case WXK_CONTROL :
344 case WXK_ALT :
345 {
346 // default processing for these keys
347 event.Skip();
348 return;
349 }
350
351 case WXK_RETURN : case WXK_NUMPAD_ENTER :
352 {
353 // put cursor at end if not already on the last line
354 if (!CaretOnPromptLine(STE_CARET_MOVE_NONE))
355 {
356 GotoPos(GetLength());
357 return;
358 }
359
360 int current_line = GetCurrentLine();
361 int prompt_line = GetPromptLine();
362
363 // allow multiline entry for shift+enter
364 if ((current_line >= prompt_line) && event.ShiftDown())
365 {
366 event.Skip();
367 return;
368 }
369
370 wxString promptText = GetPromptText();
371
372 // goto the end of the line and store the line for the history
373 LineEnd();
374 if (promptText.Length())
375 AddHistoryLine(promptText, true);
376
377 // just send the event, the receiver can do what they like
378 SendEvent(wxEVT_STESHELL_ENTER, 0, GetState(), promptText);
379 return;
380 }
381 case WXK_BACK :
382 {
383 // go to the end of the last line if not on last line
384 if (!CaretOnPromptLine(STE_CARET_MOVE_NONE))
385 {
386 GotoPos(GetLength());
387 return;
388 }
389 // don't let them backspace into previous line
390 int caret_pos = 0;
391 GetCurLine(&caret_pos);
392 if (caret_pos < 1)
393 return;
394
395 break;
396 }
397 default : // move cursor to end if not already there
398 {
399 // reset history to start at most recent again
400 m_line_history_index = (int)(m_lineHistoryArray.GetCount() - 1);
401
402 CaretOnPromptLine(STE_CARET_MOVE_ENDTEXT);
403 break;
404 }
405 }
406
407 event.Skip();
408 }
409