You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

468 lines
14KB

  1. /*
  2. Copyright (C) 2012 Rory Walsh
  3. Cabbage is free software; you can redistribute it
  4. and/or modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. Cabbage is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with Csound; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  14. 02111-1307 USA
  15. */
  16. #include "CodeEditor.h"
  17. //==============================================================================
  18. CsoundCodeEditor::CsoundCodeEditor(String type, CodeDocument &document, CodeTokeniser *codeTokeniser)
  19. : CodeEditorComponent(document, codeTokeniser), type(type), textChanged(false)
  20. {
  21. document.addListener(this);
  22. setColour(CodeEditorComponent::backgroundColourId, Colour::fromRGB(20, 20, 20));
  23. setColour(CodeEditorComponent::lineNumberBackgroundId, Colours::black);
  24. setColour(CodeEditorComponent::highlightColourId, Colours::green.withAlpha(.6f));
  25. setColour(CaretComponent::caretColourId, Colours::white);
  26. setColour(TextEditor::backgroundColourId, Colours::black);
  27. setColour(TextEditor::textColourId, Colours::white);
  28. setLineNumbersShown(true);
  29. setColour(CodeEditorComponent::highlightColourId, Colours::cornflowerblue);
  30. setColour(CodeEditorComponent::lineNumberTextId, Colours::whitesmoke);
  31. setLineNumbersShown(true);
  32. setFont(Font(String("Courier New"), 14, 1));
  33. }
  34. CsoundCodeEditor::~CsoundCodeEditor()
  35. {
  36. }
  37. void CsoundCodeEditor::highlightLine(String line){
  38. String temp = getDocument().getAllContent();
  39. Range<int> range;
  40. moveCaretTo(CodeDocument::Position (getDocument(), temp.indexOf(line)+line.length()), false);
  41. moveCaretTo(CodeDocument::Position (getDocument(), temp.indexOf(line)), true);
  42. //range.setStart(temp.indexOf(line)+line.length());
  43. //range.setEnd(temp.indexOf(line));
  44. //range.setStart(1);
  45. //range.setEnd(100);
  46. //setHighlightedRegion(range);
  47. }
  48. bool CsoundCodeEditor::keyPressed (const KeyPress& key)
  49. {
  50. //Logger::writeToLog(String(key.getKeyCode()));
  51. if (key.getTextDescription().contains("cursor up") || key.getTextDescription().contains("cursor down")
  52. || key.getTextDescription().contains("cursor left") || key.getTextDescription().contains("cursor right"))
  53. handleEscapeKey();
  54. if (! TextEditorKeyMapper<CodeEditorComponent>::invokeKeyFunction (*this, key))
  55. {
  56. if (key == KeyPress::returnKey)
  57. handleReturnKey();
  58. else if (key == KeyPress::escapeKey)
  59. handleEscapeKey();
  60. //else if (key == KeyPress ('[', ModifierKeys::commandModifier, 0)) unindentSelection();
  61. //else if (key == KeyPress (']', ModifierKeys::commandModifier, 0)) indentSelection();
  62. else if (key.getTextCharacter() >= ' ')
  63. insertTextAtCaret (String::charToString (key.getTextCharacter()));
  64. //insertMultiTextAtCaret(String::charToString (key.getTextCharacter()));
  65. else if(key.getKeyCode() == 268435488)
  66. handleTabKey("backwards");
  67. else if(key == KeyPress::tabKey)
  68. handleTabKey("forwards");
  69. else
  70. return false;
  71. }
  72. //handleUpdateNowIfNeeded();
  73. return true;
  74. }
  75. void CsoundCodeEditor::handleReturnKey (){
  76. if(type=="csound"){
  77. insertNewLine("\n");
  78. sendActionMessage("make popup invisible");
  79. }
  80. }
  81. void CsoundCodeEditor::insertText(String text){
  82. pos1 = getCaretPos();
  83. getDocument().insertText(pos1, text);
  84. }
  85. void CsoundCodeEditor::insertNewLine(String text){
  86. pos1 = getCaretPos();
  87. StringArray csdArray;
  88. csdArray.addLines(getAllText());
  89. String curLine = csdArray[pos1.getLineNumber()];
  90. int numberOfTabs=0;
  91. String tabs;
  92. while(curLine.substring(numberOfTabs, numberOfTabs+1).equalsIgnoreCase("\t")){
  93. tabs.append("\t", 8);
  94. numberOfTabs++;
  95. }
  96. Logger::writeToLog("Number of tabs:"+String(numberOfTabs));
  97. getDocument().insertText(pos1, text+tabs);
  98. }
  99. void CsoundCodeEditor::insertMultiTextAtCaret (String text)
  100. {
  101. sendActionMessage("make popup invisible");
  102. StringArray csdArray;
  103. csdArray.addLines(getAllText());
  104. String curLine;
  105. CodeDocument::Position newPos, indexPos;
  106. newPos = getCaretPos();//getSelectionStartCaretPos();
  107. int currentLine = getCaretPos().getLineNumber();
  108. int index = newPos.getIndexInLine();
  109. Logger::writeToLog(String(index));
  110. StringArray selectedText;
  111. selectedText.addLines(getTextInRange(this->getHighlightedRegion()));
  112. for(int i=0;i<selectedText.size();i++){
  113. curLine = newPos.getLineText();
  114. Logger::writeToLog(String(curLine.length()));
  115. /* need to check for tabs and add four spaces!!*/
  116. for(int y=curLine.length();y<index+2;y++){
  117. getDocument().insertText(CodeDocument::Position(getDocument(), newPos.getLineNumber(), curLine.length()), " ");
  118. newPos = newPos.movedBy(1);
  119. //curLine = csdArray[currentLine+i];
  120. }
  121. getDocument().insertText(newPos, text);
  122. newPos = newPos.movedByLines(1);
  123. }
  124. sendActionMessage("make popup invisible");
  125. }
  126. void CsoundCodeEditor::handleTabKey(String direction)
  127. {
  128. /*multi line action, get highlited text, find the position of
  129. * it within the text editor, remove it from editor and reinsert it with
  130. * formatting
  131. */
  132. StringArray selectedText, csdArray;
  133. selectedText.addLines(getSelectedText());
  134. csdArray.addLines(getAllText());
  135. String csdText;
  136. String currentLine;
  137. if(direction.equalsIgnoreCase("forwards")){
  138. //single line tab
  139. if(selectedText.size()<1){
  140. insertTabAtCaret();
  141. return;
  142. }
  143. else{
  144. //multiline tab
  145. int indexOfText = getAllText().indexOf(getSelectedText());
  146. csdText = getAllText().replace(getSelectedText(), "");
  147. for(int i=0;i<selectedText.size();i++)
  148. selectedText.set(i, "\t"+selectedText[i]);
  149. csdText = csdText.replaceSection(indexOfText, 0, selectedText.joinIntoString("\n"));
  150. }
  151. }
  152. else if(direction.equalsIgnoreCase("backwards"))
  153. //single line back tab
  154. if(selectedText.size()<1){
  155. pos1 = getCaretPos();
  156. //Logger::writeToLog(csdArray[pos1.getLineNumber()]);
  157. currentLine = csdArray[pos1.getLineNumber()];
  158. if(csdArray[pos1.getLineNumber()].substring(0, 1).contains("\t")){
  159. csdArray.set(pos1.getLineNumber(), currentLine.substring(1));
  160. csdText = csdArray.joinIntoString("\n");
  161. }
  162. else
  163. return;
  164. }
  165. //multiline back tab
  166. else{
  167. //multiline tab
  168. int indexOfText = getAllText().indexOf(getSelectedText());
  169. csdText = getAllText().replace(getSelectedText(), "");
  170. for(int i=0;i<selectedText.size();i++)
  171. if(selectedText[i].substring(0, 1).equalsIgnoreCase("\t"))
  172. selectedText.set(i, selectedText[i].substring(1));
  173. csdText = csdText.replaceSection(indexOfText, 0, selectedText.joinIntoString("\n"));
  174. }
  175. //Logger::writeToLog(newTextArray.joinIntoString("\n"));
  176. setAllText(csdText);
  177. if(selectedText.size()>0)
  178. highlightLine(selectedText.joinIntoString("\n"));
  179. else
  180. moveCaretTo(CodeDocument::Position (getDocument(), getAllText().indexOf(currentLine.substring(1))), false);
  181. sendActionMessage("make popup invisible");
  182. }
  183. void CsoundCodeEditor::toggleComments()
  184. {
  185. StringArray selectedText;
  186. selectedText.addLines(getSelectedText());
  187. StringArray csdArray;
  188. csdArray.addLines(this->getAllText());
  189. String lastLine;
  190. for(int i=0;i<csdArray.size();i++)
  191. for(int y=0;y<selectedText.size();y++)
  192. if(selectedText[y]==csdArray[i]){
  193. if(!csdArray[i].equalsIgnoreCase("")){
  194. if(selectedText[y].substring(0, 1).equalsIgnoreCase(";"))
  195. csdArray.set(i, selectedText[y].substring(1));
  196. else
  197. csdArray.set(i, ";"+selectedText[y]);
  198. lastLine = selectedText[y].substring(1);
  199. }
  200. }
  201. this->setAllText(csdArray.joinIntoString("\n"));
  202. moveCaretTo(CodeDocument::Position (getDocument(), getAllText().indexOf(lastLine)+lastLine.length()), false);
  203. }
  204. //=================== addPopupMenuItems =======================
  205. void CsoundCodeEditor::addPopupMenuItems (PopupMenu &menuToAddTo, const MouseEvent *mouseClickEvent)
  206. {
  207. menuToAddTo.addItem(1, "Cut");
  208. menuToAddTo.addItem(1, "Copy");
  209. menuToAddTo.addItem(1, "Paste");
  210. menuToAddTo.addItem(1, "Select All");
  211. menuToAddTo.addSeparator();
  212. menuToAddTo.addItem(1, "Undo");
  213. menuToAddTo.addItem(1, "Redo");
  214. menuToAddTo.addItem(10, "Add to repo");
  215. PopupMenu m;
  216. int repoIndex = 100;
  217. ScopedPointer<XmlElement> xmlElement;
  218. xmlElement = appProperties->getUserSettings()->getXmlValue("CopeRepoXmlData");
  219. if(xmlElement)
  220. forEachXmlChildElement (*xmlElement, e)
  221. {
  222. m.addItem(repoIndex, e->getTagName());
  223. repoEntries.add(e->getTagName());
  224. repoIndex++;
  225. }
  226. xmlElement =nullptr;
  227. menuToAddTo.addSubMenu("Insert from repo", m);
  228. };
  229. Rectangle<int> CsoundCodeEditor::getCaretPoisition()
  230. {
  231. pos1 = getCaretPos();
  232. return getCharacterBounds(pos1);
  233. }
  234. void CsoundCodeEditor::performPopupMenuAction (int menuItemID){
  235. if(menuItemID==1000){
  236. pos1 = getDocument().findWordBreakBefore(getCaretPos());
  237. String line = getDocument().getLine(pos1.getLineNumber());
  238. Logger::writeToLog(line);
  239. line = getTextInRange(this->getHighlightedRegion());
  240. Logger::writeToLog(line);
  241. sendActionMessage(line);
  242. }
  243. //add to repo
  244. else if(menuItemID==10){
  245. addToRepository();
  246. }
  247. //insert from repo
  248. else if(menuItemID>=100){
  249. ScopedPointer<XmlElement> xmlElement;
  250. xmlElement = appProperties->getUserSettings()->getXmlValue("CopeRepoXmlData");
  251. forEachXmlChildElement (*xmlElement, e)
  252. {
  253. if(e->getTagName()==repoEntries[menuItemID-100])
  254. insertText(e->getAllSubText());
  255. }
  256. xmlElement = nullptr;
  257. }
  258. };
  259. void CsoundCodeEditor::addRepoToSettings()
  260. {
  261. }
  262. void CsoundCodeEditor::updateCaretPosition()
  263. {
  264. /*
  265. Logger::writeToLog("Updating caret position");
  266. int columnEdit = 1;
  267. if(columnEdit==1){
  268. StringArray selectedText;
  269. selectedText.addLines(getTextInRange(this->getHighlightedRegion()));
  270. Rectangle<int> newCaretPosition(getCharacterBounds(getSelectionStartCaretPosition()));
  271. newCaretPosition.setHeight(getCharacterBounds (getCaretPos()).getHeight()*selectedText.size());
  272. caret->setCaretPosition (newCaretPosition);
  273. }
  274. else*/
  275. setCaretPos(getCharacterBounds (getCaretPos()));
  276. }
  277. void CsoundCodeEditor::addToRepository()
  278. {
  279. AlertWindow alert("Add to Repository", "Enter a name and hit 'escape'", AlertWindow::NoIcon, this->getTopLevelComponent());
  280. //CabbageLookAndFeel basicLookAndFeel;
  281. //alert.setLookAndFeel(&basicLookAndFeel);
  282. alert.setColour(TextEditor::textColourId, Colours::white);
  283. alert.setColour(TextEditor::highlightedTextColourId, Colours::lime);
  284. alert.addTextEditor("textEditor", "enter name", "");
  285. alert.getTextEditor("textEditor")->setColour(TextEditor::textColourId, Colours::lime);
  286. alert.runModalLoop();
  287. Logger::writeToLog(alert.getTextEditorContents("textEditor"));
  288. String repoEntryName = alert.getTextEditorContents("textEditor");
  289. if(repoEntryName!="enter name"){
  290. ScopedPointer<XmlElement> repoXml;
  291. ScopedPointer<XmlElement> newEntryXml;
  292. repoXml = appProperties->getUserSettings()->getXmlValue("CopeRepoXmlData");
  293. newEntryXml = new XmlElement(repoEntryName);
  294. newEntryXml->addTextElement(getSelectedText());
  295. if(newEntryXml)
  296. repoXml->addChildElement(newEntryXml);
  297. appProperties->getUserSettings()->setValue("CopeRepoXmlData", repoXml);
  298. repoXml = nullptr;
  299. newEntryXml = nullptr;
  300. }
  301. }
  302. String CsoundCodeEditor::getLineText(){
  303. StringArray csdLines;
  304. csdLines.addLines(getDocument().getAllContent());
  305. pos1 = getDocument().findWordBreakBefore(getCaretPos());
  306. Logger::writeToLog(csdLines[pos1.getLineNumber()]);
  307. return csdLines[pos1.getLineNumber()];
  308. }
  309. String CsoundCodeEditor::getAllText(){
  310. return getDocument().getAllContent();
  311. }
  312. void CsoundCodeEditor::setAllText(String text){
  313. getDocument().replaceAllContent(text);
  314. }
  315. String CsoundCodeEditor::getTempChannelInstr(){
  316. String channel = "event_i \"i\", 999.999, 0, .1\n";
  317. channel << "instr 999\n";
  318. channel << getLineText() << "\n";
  319. channel << "endin\n";
  320. Logger::writeToLog(channel);
  321. return channel;
  322. }
  323. String CsoundCodeEditor::getSelectedText(){
  324. String selectedText = getTextInRange(this->getHighlightedRegion());
  325. Logger::writeToLog(selectedText);
  326. return selectedText;
  327. }
  328. StringArray CsoundCodeEditor::getSelectedTextArray(){
  329. StringArray tempArray;
  330. String selectedText = getTextInRange(this->getHighlightedRegion());
  331. tempArray.addLines(selectedText);
  332. Logger::writeToLog(selectedText);
  333. return tempArray;
  334. }
  335. String CsoundCodeEditor::getInstrumentText(){
  336. StringArray csdLines;
  337. csdLines.addLines(getDocument().getAllContent());
  338. pos1 = getDocument().findWordBreakBefore(getCaretPos());
  339. int startOfInstrDef, endOfInstrDef;
  340. int index = pos1.getLineNumber();
  341. while(index>0){
  342. if(csdLines[index].contains("instr")){
  343. startOfInstrDef = index-1;
  344. break;
  345. }
  346. else
  347. index--;
  348. }
  349. index = pos1.getLineNumber();
  350. while(index>0){
  351. if(csdLines[index].contains("endin")){
  352. endOfInstrDef = index;
  353. break;
  354. }
  355. else
  356. index++;
  357. }
  358. String selectedText="";
  359. for(int i = startOfInstrDef;i<=endOfInstrDef;i++)
  360. selectedText += csdLines[i]+"\n";
  361. Logger::writeToLog(selectedText);
  362. return selectedText;
  363. }
  364. void CsoundCodeEditor::codeDocumentTextInserted(const juce::String &,int)
  365. {
  366. textChanged = true;
  367. pos1 = getDocument().findWordBreakBefore(getCaretPos());
  368. String lineFromCsd = getDocument().getLine(pos1.getLineNumber());
  369. if(CabbageUtils::getPreference(appProperties, "EnablePopupDisplay"))
  370. {
  371. String opcodeHelpString;
  372. StringArray syntaxTokens, csdLineTokens;
  373. csdLineTokens.clear();
  374. csdLineTokens.addTokens(lineFromCsd, " ,\t", "");
  375. for(int i=0;i<opcodeStrings.size();i++){
  376. opcodeHelpString = opcodeStrings[i];
  377. syntaxTokens.clear();
  378. syntaxTokens.addTokens(opcodeHelpString, ";", "\"");
  379. if(syntaxTokens.size()>3)
  380. for(int x=0;x<csdLineTokens.size();x++){
  381. //Logger::writeToLog(csdLineTokens[x]);
  382. if(syntaxTokens[0].removeCharacters("\"")==csdLineTokens[x].trim()){
  383. if(syntaxTokens[0].length()>3){
  384. //Logger::writeToLog(syntaxTokens[0]);
  385. sendActionMessage("popupDisplay"+syntaxTokens[2]);
  386. opcodeTokens = syntaxTokens;
  387. x=csdLineTokens.size();
  388. i=opcodeStrings.size();
  389. }
  390. }
  391. }
  392. }
  393. }
  394. }
  395. void CsoundCodeEditor::codeDocumentTextDeleted(int,int){
  396. textChanged = true;
  397. sendActionMessage("make popup invisible");
  398. }