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.
tdeedu/kturtle/src/kturtle.cpp

1182 lines
40 KiB

/*
* KTurtle, Copyright (C) 2003-04 Cies Breijs <cies # kde ! nl>
This program is free software; you can redistribute it and/or
modify it under the terms of version 2 of the GNU General Public
License as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// BEGIN includes and defines
#include <stdlib.h>
#include <qbutton.h>
#include <qregexp.h>
#include <qpainter.h>
#include <qtooltip.h>
#include <qtimer.h>
#include <qwhatsthis.h>
#include <kapplication.h>
#include <kconfigdialog.h>
#include <kdebug.h>
#include <kedittoolbar.h>
#include <kfiledialog.h>
#include <kimageio.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kmenubar.h>
#include <kprinter.h>
#include <ksavefile.h>
#include <kstatusbar.h>
#include <ktexteditor/clipboardinterface.h>
#include <ktexteditor/cursorinterface.h>
#include <ktexteditor/editorchooser.h>
#include <ktexteditor/editinterface.h>
#include <ktexteditor/highlightinginterface.h>
#include <ktexteditor/printinterface.h>
#include <ktexteditor/selectioninterface.h>
#include <ktexteditor/undointerface.h>
#include <ktexteditor/viewcursorinterface.h>
#include <ktexteditor/encodinginterface.h>
#include "lexer.h"
#include "settings.h"
#include "translate.h"
#include "kturtle.h"
// StatusBar field IDs
#define IDS_STATUS 0
#define IDS_LINECOLUMN 2
#define IDS_INS 3
#define IDS_LANG 4
// END
// BEGIN constructor and destructor
MainWindow::MainWindow(KTextEditor::Document *document) : editor(0)
{
// the initial values
CurrentFile = KURL(); // fill with empty URL
setCaption( i18n("Untitled") );
picker = 0; // for the colorpickerdialog
executing = false;
b_fullscreen = false;
// set the shell's ui resource file
if (!document)
{
if ( !(document = KTextEditor::EditorChooser::createDocument(0,"KTextEditor::Document") ) )
{
KMessageBox::error( this, i18n("A KDE text-editor component could not be found;\n"
"please check your KDE installation.") );
kapp->exit(1);
}
// docList.append(doc);
}
doc = document;
setupCanvas();
setupEditor();
setupStatusBar();
setupActions();
createShellGUI(true);
setMinimumSize(200,200);
// init with more usefull size, stolen from kwite (they stole it from konq)
if ( !initialGeometrySet() && !kapp->config()->hasGroup("MainWindow Settings") ) resize(640, 480);
KConfig *config = kapp->config();
readConfig(config);
}
MainWindow::~MainWindow()
{
delete editor->document();
}
// END
// BEGIN setup's (actions, editor, statusbar, canvas)
void MainWindow::setupActions()
{
KActionCollection *ac = actionCollection(); // abbreviation
// File actions
KStdAction::openNew(this, SLOT(slotNewFile()), ac);
openExAction = new KAction(i18n("Open Exa&mples..."), "bookmark_folder", CTRL+Key_E, this, SLOT(slotOpenExample()), ac, "open_examples");
KStdAction::open(this, SLOT(slotOpenFile()), ac);
m_recentFiles = KStdAction::openRecent(this, SLOT(slotOpenFile(const KURL&)), ac);
KStdAction::save(this, SLOT(slotSaveFile()), ac);
KStdAction::saveAs(this, SLOT(slotSaveAs()), ac);
new KAction(i18n("Save &Canvas..."), 0, 0, this, SLOT(slotSaveCanvas()), ac, "save_canvas");
speed = new KSelectAction(i18n("Execution Speed"), 0, ALT+Key_S, this, SLOT( slotChangeSpeed() ), ac, "speed");
QStringList speeds; speeds << i18n("Full Speed") << i18n("Slow") << i18n("Slower") << i18n("Slowest");
speed->setItems(speeds);
speed->setCurrentItem(0);
run = new KAction(i18n("&Execute Commands"), "gear", ALT+Key_Return, this, SLOT( slotExecute() ), ac, "run");
pause = new KToggleAction(i18n("Pause E&xecution"), "player_pause", Key_Pause, this, SLOT( slotPauseExecution() ), ac, "pause");
pause->setChecked(false);
pause->setEnabled(false);
stop = new KAction(i18n("Stop E&xecution"), "stop", Key_Escape, this, SLOT( slotAbortExecution() ), ac, "stop");
stop->setEnabled(false);
KStdAction::print(this, SLOT(slotPrint()), ac);
KStdAction::quit(this, SLOT(close()), ac);
// Edit actions
KStdAction::undo(this, SLOT(slotUndo()), ac);
KStdAction::redo(this, SLOT(slotRedo()), ac);
KStdAction::cut(this, SLOT(slotCut()), ac);
KStdAction::copy(this, SLOT(slotCopy()), ac);
KStdAction::paste(this, SLOT(slotPaste()), ac);
KStdAction::selectAll(this, SLOT(slotSelectAll()), ac);
KStdAction::deselect(this, SLOT(slotClearSelection()), ac);
new KToggleAction(i18n("Toggle Insert"), Key_Insert, this, SLOT(slotToggleInsert()), ac, "set_insert");
KStdAction::find(this, SLOT(slotFind()), ac);
KStdAction::findNext(this, SLOT(slotFindNext()), ac);
KStdAction::findPrev(this, SLOT(slotFindPrevious()), ac);
KStdAction::replace(this, SLOT(slotReplace()), ac);
// View actions
new KToggleAction(i18n("Show &Line Numbers"), 0, Key_F11, this, SLOT(slotToggleLineNumbers()), ac, "line_numbers");
m_fullscreen = KStdAction::fullScreen(this, SLOT(slotToggleFullscreen()), ac, this, "full_screen");
m_fullscreen->setChecked(b_fullscreen);
// Tools actions
colorpicker = new KToggleAction(i18n("&Color Picker"), "colorize", ALT+Key_C, this, SLOT(slotColorPicker()), ac, "color_picker");
new KAction(i18n("&Indent"), "indent", CTRL+Key_I, this, SLOT(slotIndent()), ac, "edit_indent");
new KAction(i18n("&Unindent"), "unindent", CTRL+SHIFT+Key_I, this, SLOT(slotUnIndent()), ac, "edit_unindent");
new KAction(i18n("Cl&ean Indentation"), 0, 0, this, SLOT(slotCleanIndent()), ac, "edit_cleanIndent");
new KAction(i18n("Co&mment"), 0, CTRL+Key_D, this, SLOT(slotComment()), ac, "edit_comment");
new KAction(i18n("Unc&omment"), 0, CTRL+SHIFT+Key_D, this, SLOT(slotUnComment()), ac, "edit_uncomment");
// Settings actions
KStdAction::preferences( this, SLOT(slotSettings()), ac );
new KAction(i18n("&Configure Editor..."), "configure", 0, this, SLOT(slotEditor()), ac, "set_confdlg");
// Help actions
ContextHelp = new KAction(0, 0, Key_F2, this, SLOT(slotContextHelp()), ac, "context_help");
slotContextHelpUpdate(); // this sets the label of this action
// other
setXMLFile("kturtleui.rc");
setupGUI();
}
void MainWindow::setupEditor()
{
editorDock = new QDockWindow(this);
editorDock->setNewLine(true);
editorDock->setFixedExtentWidth(250);
editorDock->setFixedExtentHeight(150);
editorDock->setResizeEnabled(true);
editorDock->setFrameShape(QFrame::ToolBarPanel);
QWhatsThis::add( editorDock, i18n( "This is the code editor, here you type the Logo commands to instruct the turtle. You can also open an existing Logo program with File->Open Examples... or File->Open." ) );
moveDockWindow(editorDock, Qt::DockLeft);
editor = doc->createView (editorDock, 0L);
// editorInterface is the editor interface which allows us to access the text in the part
editorInterface = dynamic_cast<KTextEditor::EditInterface*>(doc);
dynamic_cast<KTextEditor::EncodingInterface*>(doc)->setEncoding("UTF-8");
editorDock->setWidget(editor);
// default the highlightstyle to "logo" using the needed i18n
kdDebug(0)<<"The HighlightStyle for the Code Editor: "<<Settings::logoLanguage()<<endl;
slotSetHighlightstyle( Settings::logoLanguage() );
// allow the cursor position to be indicated in the statusbar
connect( editor, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorStatusBar()) );
// and update the context help menu item
connect( editor, SIGNAL(cursorPositionChanged()), this, SLOT(slotContextHelpUpdate()) );
translate = new Translate();
}
void MainWindow::setupStatusBar()
{
statusBar()->insertItem("", IDS_STATUS, 1, false);
statusBar()->setItemAlignment(IDS_STATUS, AlignLeft);
statusBar()->insertItem("", IDS_LANG, 0, true);
statusBar()->insertItem("", IDS_LINECOLUMN, 0, true);
statusBar()->insertItem("", IDS_INS, 0, true);
// fill the statusbar
slotStatusBar(i18n("Welcome to KTurtle..."), IDS_STATUS); // the message part
slotStatusBar(i18n("Line: %1 Column: %2").arg(1).arg(1), IDS_LINECOLUMN);
slotStatusBar(i18n("INS"), IDS_INS);
}
void MainWindow::setupCanvas()
{
baseWidget = new QWidget(this);
setCentralWidget(baseWidget);
baseLayout = new QGridLayout(baseWidget, 0, 0);
canvasView = new Canvas(baseWidget);
baseLayout->addWidget(canvasView, 0, 0, AlignCenter);
baseLayout->setRowStretch(0, 1); // this apperntly fixes a pre-usefull scrollbars bug
baseLayout->setColStretch(0, 1);
QWhatsThis::add( canvasView, i18n("This is the canvas, here the turtle draws a picture.") );
canvasView->show();
connect( canvasView, SIGNAL( CanvasResized() ), this, SLOT( slotUpdateCanvas() ) );
}
// END
// BEGIN staturbar related functions
void MainWindow::slotStatusBar(QString text, int id)
{
text = " " + text + " "; // help the layout
statusBar()->changeItem(text, id);
}
void MainWindow::slotCursorStatusBar()
{
uint cursorLine;
uint cursorCol;
dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&cursorLine, &cursorCol);
QString linenumber = i18n(" Line: %1 Column: %2 ").arg(cursorLine + 1).arg(cursorCol + 1);
statusBar()->changeItem(linenumber, IDS_LINECOLUMN);
}
// END
// BEGIN file menu related fuctions (new, open, save()as, image, print, quit)
void MainWindow::slotNewFile()
{
if ( !editor->document()->isModified() && CurrentFile.isEmpty() ) return; // do nothing when nothing can be done
if ( editor->document()->isModified() )
{
int result = KMessageBox::warningContinueCancel( this,
i18n("The program you are currently working on is not saved. "
"By continuing you may lose the changes you have made."),
i18n("Unsaved File"), i18n("&Discard Changes") );
if (result != KMessageBox::Continue) return;
}
editorInterface->clear(); // clear the editor
canvasView->slotClear(); // clear the view
editor->document()->setModified(false);
CurrentFile = KURL();
setCaption( i18n("Untitled") );
slotStatusBar(i18n("New file... Happy coding!"), IDS_STATUS);
}
void MainWindow::slotOpenFile(const KURL &urlRef)
{
KURL url = urlRef;
if ( url.isEmpty() )
{
url = KFileDialog::getOpenURL( QString(":logo_dir"), QString("*.logo|") + i18n("Logo Files"), this, i18n("Open Logo File") );
}
loadFile(url);
}
void MainWindow::slotOpenExample()
{
KURL url;
url.setPath( locate("data", "kturtle/examples/" + Settings::logoLanguage() + "/") );
url = KFileDialog::getOpenURL( url.path(), QString("*.logo|") + i18n("Logo Examples Files"), this, i18n("Open Logo Example File") );
loadFile(url);
}
void MainWindow::loadFile(const KURL &url)
{
if ( !url.isEmpty() )
{
QFile file( url.path() );
if ( file.open(IO_ReadOnly) )
{
if ( editor->document()->isModified() )
{
int result = KMessageBox::warningContinueCancel( this,
i18n("The program you are currently working on is not saved. "
"By continuing you may lose the changes you have made."),
i18n("Unsaved File"), i18n("&Discard Changes") );
if (result != KMessageBox::Continue)
{
slotStatusBar(i18n("Opening aborted, nothing opened."), IDS_STATUS);
return;
}
}
QTextStream stream(&file);
stream.setEncoding(QTextStream::UnicodeUTF8);
editorInterface->setText( stream.read() );
file.close();
m_recentFiles->addURL(url);
setCaption( url.fileName() );
slotStatusBar(i18n("Opened file: %1").arg( url.fileName() ), IDS_STATUS);
editor->document()->setModified(false);
CurrentFile = url;
return;
}
else
{
KMessageBox::error( this,
i18n("KTurtle was unable to open: \n%1.").arg( url.prettyURL() ),
i18n("Open Error") );
slotStatusBar(i18n("Opening aborted because of error."), IDS_STATUS);
return;
}
}
slotStatusBar(i18n("Opening aborted."), IDS_STATUS); // fallback
}
void MainWindow::slotSaveFile()
{
writeFile(CurrentFile);
}
void MainWindow::slotSaveAs()
{
KURL url;
while (true)
{
url = KFileDialog::getSaveURL( QString(":logo_dir"), QString("*.logo|") + i18n("Logo Files"), this, i18n("Save As") );
if ( url.isEmpty() ) // cancelled the save dialog
{
slotStatusBar(i18n("Saving aborted."), IDS_STATUS);
return;
}
if ( QFile( url.path() ).exists() )
{
int result = KMessageBox::warningContinueCancel ( this,
i18n("A program named \"%1\" already exists in this folder. "
"Do you want to overwrite it?").arg( url.fileName() ),
i18n("Overwrite?"), i18n("&Overwrite") );
if (result != KMessageBox::Continue) return;
}
break;
}
writeFile(url);
}
void MainWindow::writeFile(const KURL &url)
{
if ( url.isEmpty() ) slotSaveAs();
else
{
editor->document()->saveAs(url); // use the KateParts method for saving
loadFile(url); // reload the file as utf8 otherwise display weird chars
setCaption( url.fileName() );
slotStatusBar(i18n("Saved to: %1").arg( url.fileName() ), IDS_STATUS);
m_recentFiles->addURL(url);
editor->document()->setModified(false);
CurrentFile = url;
}
}
void MainWindow::slotSaveCanvas()
{
KURL url;
while (true)
{
url = KFileDialog::getSaveURL( QString(":logo_dir"), QString("*.png|") +
i18n("Pictures"), this, i18n("Save Canvas as Picture") );
if ( url.isEmpty() ) return; // when cancelled the KFiledialog?
if ( QFile( url.path() ).exists() )
{
int result = KMessageBox::warningContinueCancel( this,
i18n("A picture named \"%1\" already in this folder. "
"Do you want to overwrite it?").arg( url.fileName() ),
i18n("Overwrite?"), i18n("&Overwrite") );
if (result != KMessageBox::Continue) return;
}
break;
}
QString type( KImageIO::type( url.path() ) );
if ( type.isNull() ) type = "PNG";
bool ok = false;
QPixmap* pixmap = canvasView->canvas2Pixmap();
if ( url.isLocalFile() )
{
KSaveFile saveFile( url.path() );
if ( saveFile.status() == 0 )
{
if ( pixmap->save( saveFile.file(), type.latin1() ) ) ok = saveFile.close();
}
}
if (!ok)
{
kdWarning() << "KTurtle was unable to save the canvas drawing" << endl;
KMessageBox::error(this,
i18n("KTurtle was unable to save the image to: \n%1.").arg( url.prettyURL() ),
i18n("Unable to Save Image") );
slotStatusBar(i18n("Could not save image."), IDS_STATUS);
return;
}
slotStatusBar(i18n("Saved canvas to: %1").arg( url.fileName() ), IDS_STATUS);
}
void MainWindow::slotPrint()
{
int result = KMessageBox::questionYesNoCancel( this,
i18n("Do you want to print the Logo code or the canvas?"),
i18n("What to Print?"), i18n("Print &Logo Code"), i18n("Print &Canvas") );
if (result == KMessageBox::Yes)
{
dynamic_cast<KTextEditor::PrintInterface*>(doc)->printDialog();
return;
}
if (result == KMessageBox::No)
{
KPrinter printer;
if ( printer.setup(this) )
{
QPainter painter(&printer);
QPixmap *CanvasPic = canvasView->canvas2Pixmap();
painter.drawPixmap(0, 0, *CanvasPic);
}
return;
}
slotStatusBar(i18n("Printing aborted."), IDS_STATUS);
}
bool MainWindow::queryClose()
{
if ( editor->document()->isModified() )
{
slotStatusBar(i18n("Quitting KTurtle..."), IDS_STATUS);
// make sure the dialog looks good with new -- unnamed -- files.
int result = KMessageBox::warningYesNoCancel( this,
i18n("The program you are currently working on is not saved. "
"By quitting KTurtle you may lose the changes you have made."),
i18n("Unsaved File"), i18n("&Save"), i18n("Discard Changes && &Quit") );
if (result == KMessageBox::Cancel)
{
slotStatusBar(i18n("Quitting aborted."), IDS_STATUS);
return false;
}
else if (result == KMessageBox::Yes)
{
slotSaveFile();
if ( CurrentFile.isEmpty() )
{
// when saveAs get cancelled or X-ed it should not quit
slotStatusBar(i18n("Quitting aborted."), IDS_STATUS);
return false;
}
}
}
KConfig *config = kapp->config();
config->setGroup("General Options");
m_recentFiles->saveEntries(config, "Recent Files");
config->sync();
return true;
}
// END
// BEGIN run related functions
void MainWindow::slotExecute()
{
if (b_fullscreen) // check if execution should be execution in 'fullscreen' mode
{
editorDock->hide();
statusBar()->hide();
menuBar()->hide();
toolBar()->hide();
}
run->setEnabled(false); // set action indications
pause->setEnabled(true);
stop->setEnabled(true);
// start paring
slotStatusBar(i18n("Parsing commands..."), IDS_STATUS);
kdDebug(0)<<"############## PARSING STARTED ##############"<<endl;
kapp->processEvents();
errMsg = new ErrorMessage(this); // create an empty errorDialog
QString txt = editorInterface->text() + "\x0a\x0a"; // parser expects input to have 2 delimiting newlines
QTextIStream in(&txt); // create the stream
Parser parser(in); // pass the reference to the stream to the parse object
connect(&parser, SIGNAL( ErrorMsg(Token&, const QString&, uint) ),
errMsg, SLOT( slotAddError(Token&, const QString&, uint) ) );
connect( errMsg, SIGNAL(setSelection(uint, uint, uint, uint) ),
this, SLOT(slotSetSelection(uint, uint, uint, uint) ) );
parser.parse(); // and GO!
TreeNode* root = parser.getTree(); // when finished parsing get the nodeTree
kdDebug(0)<<"############## PARSING FINISHED ##############"<<endl;
kdDebug(0)<<"TreeNode::showTree():"<<endl;
root->showTree(root); // show nodeTree, this is a DEBUG OPTION (but nice)
// start execution
slotStatusBar(i18n("Executing commands..."), IDS_STATUS);
kdDebug(0)<<"############## EXECUTION STARTED ##############"<<endl;
exe = new Executer(root); // make Executer object, 'exe', and pass it the nodeTree
connect(this, SIGNAL( changeSpeed(int) ), exe, SLOT(slotChangeSpeed(int) ) );
connect(this, SIGNAL( unpauseExecution() ), exe, SLOT( slotStopPausing() ) );
connect( exe, SIGNAL( setSelection(uint, uint, uint, uint) ),
this, SLOT ( slotSetSelection(uint, uint, uint, uint) ) );
connect( exe, SIGNAL( ErrorMsg(Token&, const QString&, uint) ),
errMsg, SLOT ( slotAddError(Token&, const QString&, uint) ) );
connect( exe, SIGNAL( InputDialog(QString&) ), this, SLOT( slotInputDialog(QString&) ) );
connect( exe, SIGNAL( MessageDialog(QString) ), this, SLOT( slotMessageDialog(QString) ) );
// Connect the signals form Executer to the slots from Canvas:
connect( exe, SIGNAL( Clear() ), canvasView, SLOT( slotClear() ) );
connect( exe, SIGNAL( Go(double, double) ), canvasView, SLOT( slotGo(double, double) ) );
connect( exe, SIGNAL( GoX(double) ), canvasView, SLOT( slotGoX(double) ) );
connect( exe, SIGNAL( GoY(double) ), canvasView, SLOT( slotGoY(double) ) );
connect( exe, SIGNAL( Forward(double) ), canvasView, SLOT( slotForward(double) ) );
connect( exe, SIGNAL( Backward(double) ), canvasView, SLOT( slotBackward(double) ) );
connect( exe, SIGNAL( Direction(double) ), canvasView, SLOT( slotDirection(double) ) );
connect( exe, SIGNAL( TurnLeft(double) ), canvasView, SLOT( slotTurnLeft(double) ) );
connect( exe, SIGNAL( TurnRight(double) ), canvasView, SLOT( slotTurnRight(double) ) );
connect( exe, SIGNAL( Center() ), canvasView, SLOT( slotCenter() ) );
connect( exe, SIGNAL( SetPenWidth(int) ), canvasView, SLOT( slotSetPenWidth(int) ) );
connect( exe, SIGNAL( PenUp() ), canvasView, SLOT( slotPenUp() ) );
connect( exe, SIGNAL( PenDown() ), canvasView, SLOT( slotPenDown() ) );
connect( exe, SIGNAL( SetFgColor(int, int, int) ), canvasView, SLOT( slotSetFgColor(int, int, int) ) );
connect( exe, SIGNAL( SetBgColor(int, int, int) ), canvasView, SLOT( slotSetBgColor(int, int, int) ) );
connect( exe, SIGNAL( ResizeCanvas(int, int) ), canvasView, SLOT( slotResizeCanvas(int, int) ) );
connect( exe, SIGNAL( SpriteShow() ), canvasView, SLOT( slotSpriteShow() ) );
connect( exe, SIGNAL( SpriteHide() ), canvasView, SLOT( slotSpriteHide() ) );
connect( exe, SIGNAL( SpritePress() ), canvasView, SLOT( slotSpritePress() ) );
connect( exe, SIGNAL( SpriteChange(int) ), canvasView, SLOT( slotSpriteChange(int) ) );
connect( exe, SIGNAL( Print(QString) ), canvasView, SLOT( slotPrint(QString) ) );
connect( exe, SIGNAL( FontType(QString, QString) ), canvasView, SLOT( slotFontType(QString, QString) ) );
connect( exe, SIGNAL( FontSize(int) ), canvasView, SLOT( slotFontSize(int) ) );
connect( exe, SIGNAL( WrapOn() ), canvasView, SLOT( slotWrapOn() ) );
connect( exe, SIGNAL( WrapOff() ), canvasView, SLOT( slotWrapOff() ) );
connect( exe, SIGNAL( Reset() ), canvasView, SLOT( slotReset() ) );
// START EXECUTION on the selected speed, and use the feedbacked boolean value
slotChangeSpeed();
if ( exe->run() ) slotStatusBar(i18n("Done."), IDS_STATUS);
else slotStatusBar(i18n("Execution aborted."), IDS_STATUS);
finishExecution();
delete exe; // clean-up
if ( errMsg->containsErrors() ) errMsg->display(); // if errors show them
}
void MainWindow::slotPauseExecution()
{
if ( pause->isChecked() )
{
exe->pause();
slotStatusBar(i18n("Execution paused."), IDS_STATUS);
}
else
{
emit unpauseExecution();
slotStatusBar(i18n("Executing commands..."), IDS_STATUS);
}
}
void MainWindow::slotAbortExecution()
{
// To abort the executor, this can for instance be handy when the
// executer got stuck in a user made endless loops...
// to keep the program responding to interrupts like this, all the
// loops have possible breaks. This activates them breaks:
exe->abort();
}
void MainWindow::finishExecution()
{
kdDebug(0)<<"############## EXECUTION FINISHED ##############"<<endl;
executing = false;
pause->setEnabled(false);
pause->setChecked(false);
run->setEnabled(true);
stop->setEnabled(false);
// if execution finished on FullSpeed the selection does not match the last executed command -> clear-it
if (speed->currentItem() == 0) slotClearSelection();
// if coming from fullscreen-mode show the editor, menu- and statusbar
if (b_fullscreen) QTimer::singleShot( 1000, this, SLOT( slotFinishedFullScreenExecution() ) );
}
void MainWindow::slotChangeSpeed()
{
emit changeSpeed( speed->currentItem() );
}
// slots for logo functions that need to use the MainWindow class:
void MainWindow::slotInputDialog(QString& value)
{
value = KInputDialog::getText(i18n("Input"), value);
}
void MainWindow::slotMessageDialog(QString text)
{
KMessageBox::information( this, text, i18n("Message") );
}
// END
// BEGIN editor connections (undo, redo, cut, copy, paste, cursor, selections, find, replace, linenumbers etc.)
void MainWindow::slotEditor()
{
KAction *a = editor->actionCollection()->action("set_confdlg");
a->activate();
}
void MainWindow::slotSetHighlightstyle(QString langCode)
{
KTextEditor::HighlightingInterface *hli = dynamic_cast<KTextEditor::HighlightingInterface*>(doc);
for (uint i = 0; i < hli->hlModeCount(); i++)
{
if (hli->hlModeName(i) == langCode)
{
hli->setHlMode(i);
return;
}
}
// and the fallback:
for (uint i = 0; i < hli->hlModeCount(); i++)
{
if(hli->hlModeName(i) == "en_US") hli->setHlMode(i);
}
}
void MainWindow::slotUndo()
{
dynamic_cast<KTextEditor::UndoInterface*>(doc)->undo();
}
void MainWindow::slotRedo()
{
dynamic_cast<KTextEditor::UndoInterface*>(doc)->redo();
}
void MainWindow::slotCut()
{
dynamic_cast<KTextEditor::ClipboardInterface*>(editor)->cut();
}
void MainWindow::slotCopy()
{
dynamic_cast<KTextEditor::ClipboardInterface*>(editor)->copy();
}
void MainWindow::slotPaste()
{
dynamic_cast<KTextEditor::ClipboardInterface*>(editor)->paste();
}
void MainWindow::slotSelectAll()
{
dynamic_cast<KTextEditor::SelectionInterface*>(doc)->selectAll();
}
void MainWindow::slotClearSelection()
{
dynamic_cast<KTextEditor::SelectionInterface*>(doc)->clearSelection();
}
void MainWindow::slotFind()
{
KAction *a = editor->actionCollection()->action("edit_find");
a->activate();
}
void MainWindow::slotFindNext()
{
KAction *a = editor->actionCollection()->action("edit_find_next");
a->activate();
}
void MainWindow::slotFindPrevious()
{
KAction *a = editor->actionCollection()->action("edit_find_prev");
a->activate();
}
void MainWindow::slotReplace()
{
KAction* a = editor->actionCollection()->action("edit_replace");
a->activate();
}
void MainWindow::slotToggleInsert()
{
KToggleAction *a = dynamic_cast<KToggleAction*>(editor->actionCollection()->action("set_insert"));
a->activate();
if (a) statusBar()->changeItem(a->isChecked() ? i18n(" OVR ") : i18n(" INS "), IDS_INS);
}
void MainWindow::slotIndent()
{
KAction *a = editor->actionCollection()->action("tools_indent");
a->activate();
}
void MainWindow::slotUnIndent()
{
KAction *a = editor->actionCollection()->action("tools_unindent");
a->activate();
}
void MainWindow::slotCleanIndent()
{
KAction *a = editor->actionCollection()->action("tools_cleanIndent");
a->activate();
}
void MainWindow::slotComment()
{
KAction *a = editor->actionCollection()->action("tools_comment");
a->activate();
}
void MainWindow::slotUnComment()
{
KAction *a = editor->actionCollection()->action("tools_uncomment");
a->activate();
}
void MainWindow::slotToggleLineNumbers()
{
KToggleAction *a = dynamic_cast<KToggleAction*>( editor->actionCollection()->action("view_line_numbers") );
a->activate();
}
void MainWindow::slotInsertText(QString str)
{
uint StartLine, StartCol, EndLine, EndCol;
dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&StartLine, &StartCol);
dynamic_cast<KTextEditor::EditInterface*>(doc)->insertText(StartLine, StartCol, str);
dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&EndLine, &EndCol);
dynamic_cast<KTextEditor::SelectionInterface*>(doc)->setSelection(StartLine, StartCol, EndLine, EndCol);
}
void MainWindow::slotSetCursorPos(uint row, uint col)
{
dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->setCursorPositionReal(row - 1, col);
// kdDebug(0)<<"Cursor set to: ("<<row-1<<", "<<col<<")"<<endl;
}
void MainWindow::slotSetSelection(uint StartLine, uint StartCol, uint EndLine, uint EndCol)
{
dynamic_cast<KTextEditor::SelectionInterface*>(doc)->setSelection(StartLine - 1, StartCol - 1, EndLine - 1, EndCol - 1);
// kdDebug(0)<<"Selection set to: ("<<StartLine<<", "<<StartCol<<", "<<EndLine<<", "<<EndCol<<")"<<endl;
}
// END
// BEGIN fullscreen functions
void MainWindow::slotToggleFullscreen()
{
if (!b_fullscreen) showFullScreen(); // both calls will generate event triggering updateFullScreen()
else if ( isFullScreen() ) showNormal();
}
bool MainWindow::event(QEvent* e)
{
// executes updateFullScreen() after a ShowFullScreen or ShowNormal event got triggered
if (e->type() == QEvent::ShowFullScreen || e->type() == QEvent::ShowNormal) updateFullScreen();
return KMainWindow::event(e);
}
void MainWindow::updateFullScreen()
{
if (isFullScreen() == b_fullscreen) return;
b_fullscreen = isFullScreen();
if (m_fullscreen) m_fullscreen->setChecked(b_fullscreen);
}
void MainWindow::slotFinishedFullScreenExecution()
{
restartOrBackDialog = new RestartOrBack(this); // we have to make some to delete some
if ( errMsg->containsErrors() ) slotBackToFullScreen(); // straight back to edit if there where errors
else
{
connect( restartOrBackDialog, SIGNAL( user1Clicked() ), this, SLOT( slotRestartFullScreen() ) );
connect( restartOrBackDialog, SIGNAL( user2Clicked() ), this, SLOT( slotBackToFullScreen() ) );
connect( restartOrBackDialog, SIGNAL( finished() ), this, SLOT( slotBackToFullScreen() ) );
restartOrBackDialog->show();
restartOrBackDialog->move(50, 50);
}
}
void MainWindow::slotBackToFullScreen()
{
delete restartOrBackDialog;
editorDock->show();
statusBar()->show();
menuBar()->show();
toolBar()->show();
}
void MainWindow::slotRestartFullScreen()
{
delete restartOrBackDialog;
slotExecute();
}
// END
// BEGIN configuration related functions
void MainWindow::slotSettings()
{
// Check if there is already a dialog, if so bring it to the foreground.
if ( KConfigDialog::showDialog("settings") ) return;
// Create a new dialog with the same name as the above checking code.
KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self() );
// connect the help
connect( dialog, SIGNAL( helpClicked() ), this, SLOT( slotSettingsHelp() ) );
// making the filling for the 'General' settings dept.
general = new QWidget();
QGridLayout *generalLayout = new QGridLayout( general, 1, 1, 11, 6, "generalLayout");
WidthHeightBox = new QGroupBox( i18n("Initial Canvas Size"), general );
WidthHeightBox->setColumnLayout(0, Qt::Vertical );
WidthHeightBox->layout()->setSpacing( 6 );
WidthHeightBox->layout()->setMargin( 11 );
QVBoxLayout *WidthHeightBoxLayout = new QVBoxLayout( WidthHeightBox->layout() );
WidthHeightBoxLayout->setAlignment( Qt::AlignTop );
QHBoxLayout *layout3 = new QHBoxLayout( 0, 0, 6, "layout3");
QVBoxLayout *layout2 = new QVBoxLayout( 0, 0, 6, "layout2");
QVBoxLayout *layout1 = new QVBoxLayout( 0, 0, 6, "layout1");
kcfg_CanvasWidth = new KIntNumInput( WidthHeightBox, "kcfg_CanvasWidth" );
kcfg_CanvasWidth->setValue( 400 );
kcfg_CanvasWidth->setMinValue( 1 );
kcfg_CanvasWidth->setReferencePoint( 1 );
layout1->addWidget( kcfg_CanvasWidth );
kcfg_CanvasHeight = new KIntNumInput( WidthHeightBox, "kcfg_CanvasHeight" );
kcfg_CanvasHeight->setValue( 300 );
kcfg_CanvasHeight->setMinValue( 1 );
kcfg_CanvasHeight->setReferencePoint( 1 );
layout1->addWidget( kcfg_CanvasHeight );
WidthLabel = new QLabel( kcfg_CanvasWidth, i18n("Canvas &width:"), WidthHeightBox );
layout2->addWidget( WidthLabel );
HeightLabel = new QLabel( kcfg_CanvasHeight, i18n("Ca&nvas height:"), WidthHeightBox );
layout2->addWidget( HeightLabel );
layout3->addLayout( layout2 );
layout3->addLayout( layout1 );
WidthHeightBoxLayout->addLayout( layout3 );
QLabel* WidthHeightLabel = new QLabel(i18n("You need to restart before these settings have effect"), WidthHeightBox);
WidthHeightBoxLayout->addWidget( WidthHeightLabel );
generalLayout->addWidget( WidthHeightBox, 0, 0 );
general->resize( QSize(234, 109).expandedTo(minimumSizeHint()) );
dialog->addPage( general, i18n("General"), "package_settings", i18n("General Settings") );
// making the filling for the 'Language' settings dept.
QWidget *language = new QWidget();
QGridLayout *languageLayout = new QGridLayout( language, 1, 1, 11, 6, "Form1Layout");
QGroupBox *groupBox1 = new QGroupBox( language, "groupBox1" );
groupBox1->setColumnLayout(0, Qt::Vertical );
groupBox1->layout()->setSpacing( 6 );
groupBox1->layout()->setMargin( 11 );
QGridLayout *groupBox1Layout = new QGridLayout( groupBox1->layout() );
groupBox1Layout->setAlignment( Qt::AlignTop );
QVBoxLayout *layout4 = new QVBoxLayout( 0, 0, 6, "layout4");
kcfg_LanguageComboBox = new KComboBox(groupBox1, "kcfg_LanguageComboBox");
kcfg_LanguageComboBox->setEditable(false);
QStringList LogoLanguageList = Settings::logoLanguageList();
// Add the full language names to the items
for ( QStringList::Iterator it = LogoLanguageList.begin(); it != LogoLanguageList.end(); ++it ) {
*it = KGlobal::locale()->twoAlphaToLanguageName( (*it).left(2) ) + " (" + *it + ")";
}
kcfg_LanguageComboBox->insertStringList(LogoLanguageList);
LanguageLabel = new QLabel(kcfg_LanguageComboBox, i18n("&Select the language for the Logo commands:"), groupBox1);
layout4->addWidget( LanguageLabel );
layout4->addWidget( kcfg_LanguageComboBox );
LanguageLabel->setBuddy( kcfg_LanguageComboBox );
groupBox1Layout->addLayout( layout4, 0, 0 );
languageLayout->addWidget( groupBox1, 0, 0 );
language->resize( QSize(373, 80).expandedTo(minimumSizeHint()) );
dialog->addPage( language, i18n("Language"), "locale", i18n("Language Settings") );
// When the user clicks OK or Apply we want to update our settings.
connect( dialog, SIGNAL( settingsChanged() ), this, SLOT( slotUpdateSettings() ) );
// Display the dialog is there where errors.
dialog->setInitialSize( QSize(550, 300) );
dialog->show();
}
void MainWindow::slotUpdateSettings()
{
// get the selected language as a language code
QString selectedLogoLanguage = kcfg_LanguageComboBox->currentText().section( "(", -1, -1 ).remove(")");
// update the settings
Settings::setLogoLanguage( selectedLogoLanguage );
Settings::setLanguageComboBox( kcfg_LanguageComboBox->currentItem() );
Settings::writeConfig();
// set the HLstyle
slotSetHighlightstyle( selectedLogoLanguage );
// set the statusbar to display the language as just updated
// TODO maybe this language name can be more pretty by not using ".left(2)", ie "American English" would than be possible... [if this is possible this should be fixed at more places.]
KConfig entry(locate("locale", "all_languages"));
entry.setGroup(Settings::logoLanguage().left(2));
slotStatusBar(i18n("Command language: %1").arg( entry.readEntry("Name") ), IDS_LANG);
delete translate; // to update the currently used language
translate = new Translate();
}
void MainWindow::readConfig(KConfig *config)
{
config->setGroup("General Options");
m_recentFiles->loadEntries(config, "Recent Files");
KConfig entry(locate("locale", "all_languages"));
entry.setGroup(Settings::logoLanguage().left(2));
slotStatusBar(i18n("Command language: %1").arg( entry.readEntry("Name") ), IDS_LANG);
}
void MainWindow::slotSettingsHelp()
{
kapp->invokeHelp("settings-configure", "", "");
}
// END
// BEGIN help related functions
void MainWindow::slotContextHelp()
{
// somehow the 'anchor' parameter of invokeHelp is not working correcctly (yet)
// this is/was appearently a bug in KHTML that Waldo Bastian kindly and quikly fixed.
// Ooh... and we also want a DCOPmethod to close the sidebar since it over-informs...
// Ooh3... we want fancy help (using KHTML) @ errormessage dialog
// Ooh4... And we might also have to keep track of the KHelpCenter instance we open so
// we will not end up with loads of them
//
// IDEA!!: put all the keyword in a i18n("...") this will make translating them a lot easier!!!
// MAYBE THIS IS ALSO POSSIBLE FOR THE INTERPRETER!!!!
// this should be discussed with translators (and please think of the highlight-themes too (since
// right now thay can probably be translated with a simple perl script
kdDebug(0)<<"help requested on this text: "<<helpKeyword<<endl;
QString helpWord;
if ( helpKeyword == i18n("<no keyword>") )
{
KMessageBox::information( this, i18n("There is currently no text under the cursor to get help on."), i18n("Nothing Under Cursor") );
return;
}
else if ( helpKeyword == i18n("<number>") ) helpWord = "number";
else if ( helpKeyword == i18n("<string>") ) helpWord = "string";
else if ( helpKeyword == i18n("<assignment>") ) helpWord = "assignment";
else if ( helpKeyword == i18n("<question>") ) helpWord = "questions";
else if ( helpKeyword == i18n("<name>") ) helpWord = "name";
else if ( helpKeyword == i18n("<comment>") ) helpWord = "comment";
else
{
// make lowercase
helpWord = helpKeyword.lower();
// if the key is an alias translate that alias to a key
if ( !translate->alias2key(helpKeyword).isEmpty() ) helpWord = translate->alias2key(helpKeyword);
else if ( !translate->name2key (helpKeyword).isEmpty() ) helpWord = translate->name2key (helpKeyword);
// at this point helpKeyword should contain a valid
// section/anchor-id that can be used found in the doc
// if not... who cares :)... well let's put an debugMsg for that occasion:
else kdDebug(0)<<"Error in MainWindow::slotContextHelp: could not translate \""<<helpKeyword<<"\""<<endl;
}
kdDebug(0)<<"trying to open a help page using this keyword: "<<helpWord<<endl;
kapp->invokeHelp(helpWord, "", "");
QString help2statusBar;
if ( helpKeyword.startsWith("<") ) help2statusBar = helpKeyword;
else help2statusBar = i18n("\"%1\"").arg(helpKeyword);
slotStatusBar(i18n("Displaying help on %1").arg(help2statusBar), IDS_STATUS);
}
void MainWindow::slotContextHelpUpdate()
{
uint row, col;
dynamic_cast<KTextEditor::ViewCursorInterface*>(editor)->cursorPositionReal(&row, &col);
QString line = dynamic_cast<KTextEditor::EditInterface*>(doc)->textLine(row);
// two shortcuts so we dont do all the CPU intensive regexp stuff when it not needed
if ( line.stripWhiteSpace().startsWith("#") )
{
helpKeyword = i18n("<comment>");
ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) );
return;
}
if ( line.stripWhiteSpace().isEmpty() || line.mid(col-1,2).stripWhiteSpace().isEmpty() )
{
helpKeyword = i18n("<no keyword>");
ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) );
return;
}
int start, length, pos;
pos = 0;
if ( line.contains('"') )
{
QRegExp delimitedStrings("(\"[^\"\\\\\\r\\n]*(\\\\.[^\"\\\\\\r\\n]*)*\")");
while ( delimitedStrings.search(line, pos) != -1 )
{
// kdDebug(0)<<"stringsearch: >>"<<pos<<"<<"<<endl;
start = delimitedStrings.search(line, pos);
length = delimitedStrings.matchedLength(); // the length of the last matched string, or -1 if there was no match
if ( col >= (uint)start && col < (uint)(start+length) )
{
helpKeyword = i18n("<string>");
ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) );
return;
}
pos += (length <= 0 ? 1 : length);
}
}
// except for "strings" this regexp effectively separates logo code in 'words' (in a very broad meaning)
QRegExp splitter("(([^ ,+\\-*/()=<>[!]|(?!==|<=|>=|!=))*)|(([ ,+\\-*/()=<>[!]|==|<=|>=|!=))");
pos = 0;
while (splitter.search(line, pos) != -1)
{
start = splitter.search(line, pos);
length = splitter.matchedLength();
if ( col < (uint)start ) break;
if ( col >= (uint)start && col < (uint)(start+length) )
{
QString cursorWord = line.mid( (uint)start, (uint)length );
kdDebug(0)<<"cursorWord: '"<<cursorWord<<"'"<<endl;
kdDebug(0)<<"(translate->name2key( cursorWord.lower() )): '"<<translate->name2key( cursorWord.lower() )<<"'"<<endl;
kdDebug(0)<<"(translate->alias2key( cursorWord.lower() )): '"<<translate->alias2key( cursorWord.lower() )<<"'"<<endl;
if ( cursorWord.stripWhiteSpace().isEmpty() ) helpKeyword = i18n("<no keyword>");
else if ( !translate->name2key( cursorWord.lower() ).isEmpty() ) helpKeyword = cursorWord;
else if ( !translate->alias2key( cursorWord.lower() ).isEmpty() ) helpKeyword = cursorWord;
else if ( cursorWord.find( QRegExp("[\\d.]+") ) == 0 ) helpKeyword = i18n("<number>");
else if ( cursorWord.find( QRegExp("[+\\-*/\\(\\)]") ) == 0 ) helpKeyword = i18n("<math>");
else if ( cursorWord == QString("=") ) helpKeyword = i18n("<assignment>");
else if ( cursorWord.find( QRegExp("==|<|>|<=|>=|!=") ) == 0 ) helpKeyword = i18n("<question>");
// if we come here we either have an ID of some sort or an error
// all we can do is try to catch some errors (TODO) and then...
else helpKeyword = i18n("<name>");
ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) );
return;
}
pos += (length <= 0 ? 1 : length); // the pos had to be increased with at least one
}
// we allready cached some in the beginning of this method; yet its still needed as fall-through
helpKeyword = i18n("<no keyword>");
ContextHelp->setText( i18n("Help on: %1").arg(helpKeyword) );
}
// END
// BEGIN misc. functions
void MainWindow::slotColorPicker()
{
// in the constructor picker is initialised as 0
// if picker is 0 when this funktion is called a colorpickerdialog is created and connected
if (picker == 0)
{
picker = new ColorPicker(this);
if(picker == 0) return; // safety
connect( picker, SIGNAL( visible(bool) ), colorpicker, SLOT( setChecked(bool) ) );
connect( picker, SIGNAL( ColorCode(QString) ), this, SLOT( slotInsertText(QString) ) );
}
// if picker is not 0, there is a colorpickerdialog which only needs to be shown OR hidden
if ( picker->isHidden() )
{
picker->show();
colorpicker->setChecked(true);
}
else
{
picker->hide();
colorpicker->setChecked(false);
}
}
void MainWindow::slotUpdateCanvas()
{
// fixes a non updateing bug
// I tried doing this by connecting Canvas's resized to baseWidget's update...
// but i had no luck :( ... this worked though :)
canvasView->hide();
canvasView->show();
}
// END
#include "kturtle.moc"