5 * Created by Toshi Nagata on 08/10/27.
6 * Copyright 2008 Toshi Nagata. All rights reserved.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation version 2 of the License.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
18 #include "wx/wxprec.h"
24 #include "ConsoleFrame.h"
28 #include "wx/colour.h"
32 #include "../MolLib/Ruby_bind/Molby_extern.h"
33 #include "../MolLib/MolLib.h"
34 #include "../MolLib/Missing.h"
37 #if defined(__WXMSW__)
42 BEGIN_EVENT_TABLE(ConsoleFrame, wxFrame)
43 EVT_UPDATE_UI(-1, ConsoleFrame::OnUpdateUI)
44 EVT_CLOSE(ConsoleFrame::OnCloseWindow)
45 EVT_MENU(wxID_CLOSE, ConsoleFrame::OnClose)
46 /* EVT_MENU(wxID_CUT, ConsoleFrame::OnCut)
47 EVT_MENU(wxID_COPY, ConsoleFrame::OnCopy)
48 EVT_MENU(wxID_PASTE, ConsoleFrame::OnPaste)
49 EVT_MENU(wxID_CLEAR, ConsoleFrame::OnClear) */
50 EVT_MENU(wxID_UNDO, ConsoleFrame::OnUndo)
51 EVT_MENU(wxID_REDO, ConsoleFrame::OnRedo)
54 ConsoleFrame::ConsoleFrame(wxFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size, long type):
55 wxFrame(parent, wxID_ANY, title, pos, size, type)
57 valueHistory = commandHistory = NULL;
58 nValueHistory = nCommandHistory = 0;
59 valueHistoryIndex = commandHistoryIndex = -1;
61 selectionFrom = selectionTo = -1;
64 ConsoleFrame::~ConsoleFrame()
67 for (i = 0; i < nValueHistory; i++)
68 free(valueHistory[i]);
70 for (i = 0; i < nCommandHistory; i++)
71 free(commandHistory[i]);
73 // wxGetApp().DocManager()->FileHistoryRemoveMenu(file_history_menu);
77 ConsoleFrame::OnCreate()
82 GetClientSize(&width, &height);
83 textCtrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxPoint(0, 0), wxSize(100, 100), wxTE_MULTILINE | wxTE_RICH | wxTE_PROCESS_ENTER
86 #if defined(__WXMSW__)
88 HWND hwnd = (HWND)(textCtrl->GetHWND());
92 /* Disable dual language mode */
93 dwOptions = SendMessage(hwnd, EM_GETLANGOPTIONS, 0, 0);
94 dwOptions &= ~0x02; /* 0x02 = IMF_AUTOFONT */
95 result = SendMessage(hwnd, EM_SETLANGOPTIONS, 0, (LPARAM)dwOptions);
96 printf("%ld\n", (long)result);
100 wxBoxSizer *consoleSizer = new wxBoxSizer(wxHORIZONTAL);
101 consoleSizer->Add(textCtrl, 1, wxEXPAND);
102 this->SetSizer(consoleSizer);
103 consoleSizer->SetSizeHints(this);
105 // Set the default font (fixed-pitch)
106 #if defined(__WXMSW__)
107 default_font = new wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
108 if (!default_font->SetFaceName(wxT("Consolas")))
109 default_font->SetFaceName(wxT("Courier"));
110 #elif defined(__WXMAC__)
111 default_font = new wxFont(11, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
112 default_font->SetFaceName(wxT("Monaco"));
114 default_font = new wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
116 current_attr = new wxTextAttr;
117 current_attr->SetFont(*default_font);
118 textCtrl->SetDefaultStyle(*current_attr);
120 // Connect textCtrl event handler
121 textCtrl->Connect(-1, wxEVT_KEY_DOWN, wxKeyEventHandler(ConsoleFrame::OnKeyDown), NULL, this);
122 textCtrl->Connect(-1, wxEVT_TEXT_ENTER, wxCommandEventHandler(ConsoleFrame::OnTextEnter), NULL, this);
123 textCtrl->Connect(-1, wxEVT_SET_FOCUS, wxFocusEventHandler(ConsoleFrame::OnSetFocus), NULL, this);
124 textCtrl->Connect(-1, wxEVT_KILL_FOCUS, wxFocusEventHandler(ConsoleFrame::OnKillFocus), NULL, this);
126 wxMenuBar *menu_bar = wxGetApp().CreateMenuBar(2, &file_history_menu, &edit_menu);
128 //// Associate the menu bar with the frame
129 SetMenuBar(menu_bar);
133 ConsoleFrame::CreateConsoleFrame(wxFrame *parent)
136 wxPoint origin(0, 0);
137 wxSize size(640, 200);
139 wxPoint origin(10, 24);
140 wxSize size(640, 200);
142 ConsoleFrame *frame = new ConsoleFrame(parent, _T("Molby Console"), origin, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
145 frame->SetClientSize(size);
151 GetLineIncludingPosition(wxTextCtrl *ctrl, int pos, int *start, int *end)
153 int pos1, pos2, posend;
163 posChar = ctrl->GetRange(pos1 - 1, pos1).GetChar(0);
169 posend = ctrl->GetLastPosition();
170 posChar = ctrl->GetRange(posend - 1, posend).GetChar(0);
175 while (pos2 < posend) {
176 posChar = ctrl->GetRange(pos2, pos2 + 1).GetChar(0);
177 if (posChar == '\n') {
192 ConsoleFrame::OnCloseWindow(wxCloseEvent &event)
194 // Do not delete this window; it may be reopened later
196 // Check if all windows are gone
197 wxGetApp().CheckIfAllWindowsAreGone(NULL);
201 ConsoleFrame::OnClose(wxCommandEvent &event)
207 ConsoleFrame::OnKillFocus(wxFocusEvent &event)
209 #if defined(__WXMSW__)
210 textCtrl->GetSelection(&selectionFrom, &selectionTo);
216 ConsoleFrame::OnSetFocus(wxFocusEvent &event)
218 #if defined(__WXMSW__)
219 if (selectionFrom >= 0 && selectionTo >= 0) {
220 textCtrl->SetSelection(selectionFrom, selectionTo);
226 // I do not understand why these functions should be written...
227 // Certainly there should be better way to implement these.
229 ConsoleFrame::OnUndo(wxCommandEvent &event)
231 if (wxWindow::FindFocus() == textCtrl)
237 ConsoleFrame::OnRedo(wxCommandEvent &event)
239 if (wxWindow::FindFocus() == textCtrl)
245 ConsoleFrame::OnUpdateUI(wxUpdateUIEvent& event)
247 int uid = event.GetId();
248 if (uid == wxID_CLOSE)
249 // Why this is not automatically done??
251 else if (uid == wxID_UNDO || uid == wxID_REDO) {
252 if (wxWindow::FindFocus() == textCtrl) {
253 if (uid == wxID_UNDO)
254 event.Enable(textCtrl->CanUndo());
256 event.Enable(textCtrl->CanRedo());
262 ConsoleFrame::OnEnterPressed()
264 int start, pos, end, veryend, lastpos;
267 if (::wxGetKeyState(WXK_ALT)) {
268 textCtrl->WriteText(wxT("\n> "));
272 pos = textCtrl->GetInsertionPoint();
273 lastpos = textCtrl->GetLastPosition();
277 if (!GetLineIncludingPosition(textCtrl, pos, &start, &end) || start == end) {
278 start = end = veryend = pos;
283 startChar = textCtrl->GetRange(start, start + 1).GetChar(0);
284 if (startChar == '%') {
287 } else if (startChar == '>') {
291 start = end = veryend = pos;
295 while (start < end && veryend < lastpos) {
297 if (!GetLineIncludingPosition(textCtrl, pos, &pos, &end) || pos == end) {
300 startChar = textCtrl->GetRange(pos, pos + 1).GetChar(0);
301 if (startChar != '>')
306 wxString string = textCtrl->GetRange(start, veryend);
307 int len = string.Len();
309 // Is there any non-whitespace characters?
312 for (i = 0; i < len; i++) {
314 if (ch != ' ' && ch != '\t' && ch != '\n' && ch != 'r')
318 // Input is not empty
319 if (veryend < lastpos) {
320 // Enter is pressed in the block not at the end
321 // -> Insert the text at the end
322 wxRegEx re1(wxT("[ \t\n]*$"));
323 re1.ReplaceFirst(&string, wxT(""));
324 wxRegEx re2(wxT("^[ \t\n]*"));
325 re2.ReplaceFirst(&string, wxT(""));
326 if (textCtrl->GetRange(lastpos - 1, lastpos).GetChar(0) != '\n')
327 textCtrl->AppendText(wxT("\n"));
328 MyAppCallback_showRubyPrompt();
329 MyAppCallback_setConsoleColor(3);
330 textCtrl->AppendText(string);
332 wxTextAttr scriptAttr(*wxBLUE, wxNullColour, *default_font);
333 textCtrl->SetStyle(start, veryend, scriptAttr);
335 string.Append(wxT("\n")); // To avoid choking Ruby interpreter
336 wxRegEx re3(wxT("\n>"));
337 re3.Replace(&string, wxT("\n"));
338 ch = textCtrl->GetRange(lastpos - 1, lastpos).GetChar(0);
340 textCtrl->AppendText(wxT("\n"));
342 MyAppCallback_setConsoleColor(0);
344 // Invoke ruby interpreter
348 Molecule *mol = MoleculeCallback_currentMolecule();
349 int lock_needed = (mol != NULL && mol->mutex != NULL);
352 script = strdup(string.mb_str(WX_DEFAULT_CONV));
353 val = Molby_evalRubyScriptOnMolecule(script, MoleculeCallback_currentMolecule(), NULL, &status);
356 script[strlen(script) - 1] = 0; /* Remove the last newline */
357 AssignArray(&commandHistory, &nCommandHistory, sizeof(char *), nCommandHistory, &script);
358 if (nCommandHistory >= MAX_HISTORY_LINES)
359 DeleteArray(&commandHistory, &nCommandHistory, sizeof(char *), 0, 1, NULL);
360 if (status != -1) { /* Status -1 is already handled */
362 MyAppCallback_setConsoleColor(1);
364 Molby_showError(status);
366 textCtrl->AppendText(wxT("-->"));
367 Molby_showRubyValue(val, &valueString);
368 AssignArray(&valueHistory, &nValueHistory, sizeof(char *), nValueHistory, &valueString);
369 if (nValueHistory >= MAX_HISTORY_LINES)
370 DeleteArray(&valueHistory, &nValueHistory, sizeof(char *), 0, 1, NULL);
372 MyAppCallback_setConsoleColor(0);
373 textCtrl->AppendText(wxT("\n"));
374 MyAppCallback_showRubyPrompt();
377 textCtrl->AppendText(wxT("\n"));
378 MyAppCallback_showRubyPrompt();
380 commandHistoryIndex = valueHistoryIndex = -1;
384 ConsoleFrame::ShowHistory(bool up, bool option)
387 if (commandHistoryIndex == -1 && valueHistoryIndex == -1) {
390 historyPos = textCtrl->GetLastPosition();
394 if (valueHistoryIndex == -1) {
395 if (nValueHistory == 0)
397 valueHistoryIndex = nValueHistory;
399 if (valueHistoryIndex <= 0)
400 return; /* Do nothing */
402 p = valueHistory[valueHistoryIndex];
404 if (valueHistoryIndex == -1)
405 return; /* Do nothing */
406 if (valueHistoryIndex == nValueHistory - 1) {
407 valueHistoryIndex = -1;
411 p = valueHistory[valueHistoryIndex];
416 if (commandHistoryIndex == -1) {
417 if (nCommandHistory == 0)
419 commandHistoryIndex = nCommandHistory;
421 if (commandHistoryIndex <= 0)
422 return; /* Do nothing */
423 commandHistoryIndex--;
424 p = commandHistory[commandHistoryIndex];
426 if (commandHistoryIndex == -1)
427 return; /* Do nothing */
428 if (commandHistoryIndex == nCommandHistory - 1) {
429 commandHistoryIndex = -1;
432 commandHistoryIndex++;
433 p = commandHistory[commandHistoryIndex];
437 textCtrl->Replace(historyPos, textCtrl->GetLastPosition(), wxT(""));
438 SetConsoleColor(option ? 1 : 3);
441 textCtrl->AppendText(wxString(p, WX_DEFAULT_CONV));
446 ConsoleFrame::OnKeyDown(wxKeyEvent &event)
448 int code = event.GetKeyCode();
451 // in wxCocoa, the key down event is fired even when the input method still has
452 // marked text. We need to avoid doing our own work when the input method is expecting
454 extern bool textCtrl_hasMarkedText();
455 if (textCtrl_hasMarkedText()) {
461 if (code == WXK_RETURN || code == WXK_NUMPAD_ENTER)
463 else if ((code == WXK_UP || code == WXK_DOWN) && (textCtrl->GetInsertionPoint() == textCtrl->GetLastPosition()))
464 ShowHistory(code == WXK_UP, event.GetModifiers() == wxMOD_ALT);
470 ConsoleFrame::OnTextEnter(wxCommandEvent &event)
476 ConsoleFrame::AppendConsoleMessage(const char *mes)
478 wxString string(mes, WX_DEFAULT_CONV);
479 textCtrl->AppendText(string);
480 commandHistoryIndex = valueHistoryIndex = -1;
485 ConsoleFrame::FlushConsoleMessage()
492 ConsoleFrame::SetConsoleColor(int color)
494 static const wxColour *col[4];
495 if (col[0] == NULL) {
501 current_attr->SetTextColour(*col[color % 4]);
502 current_attr->SetFont(*default_font);
503 textCtrl->SetDefaultStyle(wxTextAttr(*col[color % 4], wxNullColour, *default_font));
507 ConsoleFrame::EmptyBuffer(bool showRubyPrompt)
511 MyAppCallback_showRubyPrompt();
512 commandHistoryIndex = valueHistoryIndex = -1;
515 #pragma mark ====== Plain-C interface ======
518 MyAppCallback_showScriptMessage(const char *fmt, ...)
525 if (strchr(fmt, '%') == NULL) {
526 /* No format characters */
527 return wxGetApp().GetConsoleFrame()->AppendConsoleMessage(fmt);
528 } else if (strcmp(fmt, "%s") == 0) {
529 /* Direct output of one string */
530 p = va_arg(ap, char *);
531 return wxGetApp().GetConsoleFrame()->AppendConsoleMessage(p);
533 vasprintf(&p, fmt, ap);
535 retval = wxGetApp().GetConsoleFrame()->AppendConsoleMessage(p);
540 wxGetApp().GetConsoleFrame()->FlushConsoleMessage();
547 MyAppCallback_setConsoleColor(int color)
549 wxGetApp().GetConsoleFrame()->SetConsoleColor(color);
553 MyAppCallback_showRubyPrompt(void)
555 MyAppCallback_setConsoleColor(0);
556 MyAppCallback_showScriptMessage("%% ");