summaryrefslogtreecommitdiffstats
path: root/logo/src/xlogo
diff options
context:
space:
mode:
authorMarko Živković <[email protected]>2014-06-11 10:10:33 +0000
committerMarko Živković <[email protected]>2014-06-11 10:10:33 +0000
commit1107aa0763e3d7554408c401d2a1dbed11a94c51 (patch)
tree7074264bc7b63f2ee5ee14a39458380fcce1904b /logo/src/xlogo
Add initial directories and files
git-svn-id: https://svn.code.sf.net/p/xlogo4schools/svn/trunk@1 3b0d7934-f7ef-4143-9606-b51f2e2281fd
Diffstat (limited to 'logo/src/xlogo')
-rw-r--r--logo/src/xlogo/AppSettings.java151
-rw-r--r--logo/src/xlogo/Application.java1875
-rw-r--r--logo/src/xlogo/Logo.java183
-rw-r--r--logo/src/xlogo/MemoryChecker.java163
-rw-r--r--logo/src/xlogo/MenuListener.java234
-rw-r--r--logo/src/xlogo/Popup.java161
-rw-r--r--logo/src/xlogo/Sound_Player.java281
-rw-r--r--logo/src/xlogo/StyledDocument/DocumentLogo.java390
-rw-r--r--logo/src/xlogo/StyledDocument/DocumentLogoCommande.java62
-rw-r--r--logo/src/xlogo/StyledDocument/DocumentLogoHistorique.java170
-rw-r--r--logo/src/xlogo/gpl/gpl-ar.html376
-rw-r--r--logo/src/xlogo/gpl/gpl-de.html767
-rw-r--r--logo/src/xlogo/gpl/gpl-el.html175
-rw-r--r--logo/src/xlogo/gpl/gpl-en.html499
-rw-r--r--logo/src/xlogo/gpl/gpl-eo.html714
-rw-r--r--logo/src/xlogo/gpl/gpl-es.html773
-rw-r--r--logo/src/xlogo/gpl/gpl-fr.html580
-rw-r--r--logo/src/xlogo/gpl/gpl-gl.html140
-rw-r--r--logo/src/xlogo/gpl/gpl-hu.html428
-rw-r--r--logo/src/xlogo/gpl/gpl-pt.html10
-rw-r--r--logo/src/xlogo/gpl/x4s_info.html42
-rw-r--r--logo/src/xlogo/gui/AImprimer.java90
-rw-r--r--logo/src/xlogo/gui/Editor.java396
-rw-r--r--logo/src/xlogo/gui/EditorTextArea.java58
-rw-r--r--logo/src/xlogo/gui/EditorTextPane.java83
-rw-r--r--logo/src/xlogo/gui/EditorTextZone.java303
-rw-r--r--logo/src/xlogo/gui/HistoryPanel.java416
-rw-r--r--logo/src/xlogo/gui/Lis.java78
-rw-r--r--logo/src/xlogo/gui/MyTextAreaDialog.java59
-rw-r--r--logo/src/xlogo/gui/MyToolBar.java126
-rw-r--r--logo/src/xlogo/gui/ReplaceFrame.java265
-rw-r--r--logo/src/xlogo/gui/SearchFrame.java189
-rw-r--r--logo/src/xlogo/gui/Searchable.java36
-rw-r--r--logo/src/xlogo/gui/Traduc.java216
-rw-r--r--logo/src/xlogo/gui/ZoneCommande.java271
-rw-r--r--logo/src/xlogo/gui/ZoneEdition.java196
-rw-r--r--logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java232
-rw-r--r--logo/src/xlogo/gui/components/ProcedureSearch.java309
-rw-r--r--logo/src/xlogo/gui/components/TurtleComboBox.java169
-rw-r--r--logo/src/xlogo/gui/components/X4SComponent.java81
-rw-r--r--logo/src/xlogo/gui/components/X4SFrame.java40
-rw-r--r--logo/src/xlogo/gui/components/X4SGui.java112
-rw-r--r--logo/src/xlogo/gui/components/fileslist/11_alerts_and_states_error.pngbin0 -> 1285 bytes
-rw-r--r--logo/src/xlogo/gui/components/fileslist/1_navigation_accept.pngbin0 -> 1197 bytes
-rw-r--r--logo/src/xlogo/gui/components/fileslist/5_content_discard.pngbin0 -> 1359 bytes
-rw-r--r--logo/src/xlogo/gui/components/fileslist/5_content_edit.pngbin0 -> 1506 bytes
-rw-r--r--logo/src/xlogo/gui/components/fileslist/5_content_remove.pngbin0 -> 1202 bytes
-rw-r--r--logo/src/xlogo/gui/components/fileslist/FilesList.java665
-rw-r--r--logo/src/xlogo/gui/components/fileslist/FilesListItem.java542
-rw-r--r--logo/src/xlogo/gui/components/fileslist/IFilesListItem.java60
-rw-r--r--logo/src/xlogo/gui/components/fileslist/close_icon2.pngbin0 -> 22621 bytes
-rw-r--r--logo/src/xlogo/gui/components/fileslist/new_content.pngbin0 -> 1099 bytes
-rw-r--r--logo/src/xlogo/gui/preferences/AbstractPanelColor.java143
-rw-r--r--logo/src/xlogo/gui/preferences/PanelColor.java45
-rw-r--r--logo/src/xlogo/gui/translation/BottomPanel.java89
-rw-r--r--logo/src/xlogo/gui/translation/FirstPanel.java237
-rw-r--r--logo/src/xlogo/gui/translation/MyTable.java375
-rw-r--r--logo/src/xlogo/gui/translation/TopPanel.java63
-rw-r--r--logo/src/xlogo/gui/translation/TranslateXLogo.java178
-rw-r--r--logo/src/xlogo/gui/welcome/WelcomeScreen.java518
-rw-r--r--logo/src/xlogo/gui/welcome/WorkspaceSettings.java183
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java269
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java197
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java230
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java288
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java144
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java537
-rw-r--r--logo/src/xlogo/interfaces/BasicFileContainer.java129
-rw-r--r--logo/src/xlogo/interfaces/BroadcasterErrorFileContainer.java40
-rw-r--r--logo/src/xlogo/interfaces/ErrorDetector.java104
-rw-r--r--logo/src/xlogo/interfaces/MessageBroadcaster.java39
-rw-r--r--logo/src/xlogo/interfaces/ProcedureMapper.java54
-rw-r--r--logo/src/xlogo/interfaces/X4SModeSwitcher.java82
-rw-r--r--logo/src/xlogo/kernel/Affichage.java207
-rw-r--r--logo/src/xlogo/kernel/DrawPanel.java2917
-rw-r--r--logo/src/xlogo/kernel/InstructionBuffer.java206
-rw-r--r--logo/src/xlogo/kernel/Interprete.java981
-rw-r--r--logo/src/xlogo/kernel/Kernel.java211
-rw-r--r--logo/src/xlogo/kernel/LaunchPrimitive.java4751
-rw-r--r--logo/src/xlogo/kernel/LogoError.java89
-rw-r--r--logo/src/xlogo/kernel/LoopFillPolygon.java48
-rw-r--r--logo/src/xlogo/kernel/LoopFor.java94
-rw-r--r--logo/src/xlogo/kernel/LoopForEach.java69
-rw-r--r--logo/src/xlogo/kernel/LoopProperties.java149
-rw-r--r--logo/src/xlogo/kernel/LoopRepeat.java43
-rw-r--r--logo/src/xlogo/kernel/LoopWhile.java44
-rw-r--r--logo/src/xlogo/kernel/MP3Player.java86
-rw-r--r--logo/src/xlogo/kernel/MyCalculator.java1221
-rw-r--r--logo/src/xlogo/kernel/MyFlow.java110
-rw-r--r--logo/src/xlogo/kernel/MyFlowReader.java81
-rw-r--r--logo/src/xlogo/kernel/MyFlowWriter.java74
-rw-r--r--logo/src/xlogo/kernel/Primitive.java573
-rw-r--r--logo/src/xlogo/kernel/Turtle.java434
-rw-r--r--logo/src/xlogo/kernel/genericPrimitive308
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoException.java69
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoList.java93
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoNumber.java76
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoParser.java289
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoPrimitive.java74
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java56
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoTree.java96
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoType.java129
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoTypeNull.java56
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoVariable.java68
-rw-r--r--logo/src/xlogo/kernel/grammar/LogoWord.java68
-rw-r--r--logo/src/xlogo/kernel/gui/GuiButton.java92
-rw-r--r--logo/src/xlogo/kernel/gui/GuiComponent.java95
-rw-r--r--logo/src/xlogo/kernel/gui/GuiMap.java72
-rw-r--r--logo/src/xlogo/kernel/gui/GuiMenu.java101
-rw-r--r--logo/src/xlogo/kernel/network/ChatFrame.java160
-rw-r--r--logo/src/xlogo/kernel/network/NetworkClientChat.java115
-rw-r--r--logo/src/xlogo/kernel/network/NetworkClientExecute.java114
-rw-r--r--logo/src/xlogo/kernel/network/NetworkClientSend.java100
-rw-r--r--logo/src/xlogo/kernel/network/NetworkServer.java194
-rw-r--r--logo/src/xlogo/kernel/perspective/Conic.java214
-rw-r--r--logo/src/xlogo/kernel/perspective/Element3D.java115
-rw-r--r--logo/src/xlogo/kernel/perspective/ElementLine.java374
-rw-r--r--logo/src/xlogo/kernel/perspective/ElementPoint.java129
-rw-r--r--logo/src/xlogo/kernel/perspective/ElementPolygon.java179
-rw-r--r--logo/src/xlogo/kernel/perspective/FogDialog.java256
-rw-r--r--logo/src/xlogo/kernel/perspective/LightDialog.java378
-rw-r--r--logo/src/xlogo/kernel/perspective/MyFog.java134
-rw-r--r--logo/src/xlogo/kernel/perspective/MyLight.java190
-rw-r--r--logo/src/xlogo/kernel/perspective/Viewer3D.java493
-rw-r--r--logo/src/xlogo/kernel/perspective/World3D.java318
-rw-r--r--logo/src/xlogo/kernel/userspace/ErrorManager.java201
-rw-r--r--logo/src/xlogo/kernel/userspace/GlobalVariableTable.java73
-rw-r--r--logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java106
-rw-r--r--logo/src/xlogo/kernel/userspace/PropertyListTable.java165
-rw-r--r--logo/src/xlogo/kernel/userspace/UserSpace.java790
-rw-r--r--logo/src/xlogo/kernel/userspace/context/ContextManager.java301
-rw-r--r--logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java43
-rw-r--r--logo/src/xlogo/kernel/userspace/context/LogoContext.java312
-rw-r--r--logo/src/xlogo/kernel/userspace/context/NetworkContext.java206
-rw-r--r--logo/src/xlogo/kernel/userspace/context/RecordContext.java146
-rw-r--r--logo/src/xlogo/kernel/userspace/context/UserContext.java191
-rw-r--r--logo/src/xlogo/kernel/userspace/files/LogoFile.java755
-rw-r--r--logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java39
-rw-r--r--logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java560
-rw-r--r--logo/src/xlogo/kernel/userspace/files/RecordFile.java234
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java59
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java36
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/Procedure.java763
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java80
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java557
-rw-r--r--logo/src/xlogo/messages/Message.java52
-rw-r--r--logo/src/xlogo/messages/MessageKeys.java180
-rw-r--r--logo/src/xlogo/messages/Messenger.java33
-rw-r--r--logo/src/xlogo/messages/async/AbstractAsyncMessenger.java141
-rw-r--r--logo/src/xlogo/messages/async/AsyncMediumAdapter.java35
-rw-r--r--logo/src/xlogo/messages/async/AsyncMessage.java51
-rw-r--r--logo/src/xlogo/messages/async/AsyncMessenger.java35
-rw-r--r--logo/src/xlogo/messages/async/dialog/DialogMessage.java75
-rw-r--r--logo/src/xlogo/messages/async/dialog/DialogMessenger.java74
-rw-r--r--logo/src/xlogo/messages/async/history/HistoryMessage.java79
-rw-r--r--logo/src/xlogo/messages/async/history/HistoryMessenger.java85
-rw-r--r--logo/src/xlogo/messages/async/history/HistoryWriter.java33
-rw-r--r--logo/src/xlogo/storage/Storable.java267
-rw-r--r--logo/src/xlogo/storage/StorableDocument.java198
-rw-r--r--logo/src/xlogo/storage/StorableObject.java148
-rw-r--r--logo/src/xlogo/storage/WSManager.java443
-rw-r--r--logo/src/xlogo/storage/global/GlobalConfig.java545
-rw-r--r--logo/src/xlogo/storage/user/DrawQuality.java55
-rw-r--r--logo/src/xlogo/storage/user/LookAndFeel.java32
-rw-r--r--logo/src/xlogo/storage/user/PenShape.java52
-rw-r--r--logo/src/xlogo/storage/user/UserConfig.java918
-rw-r--r--logo/src/xlogo/storage/workspace/ContestConfig.java59
-rw-r--r--logo/src/xlogo/storage/workspace/Language.java111
-rw-r--r--logo/src/xlogo/storage/workspace/NumberOfBackups.java57
-rw-r--r--logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java151
-rw-r--r--logo/src/xlogo/storage/workspace/WorkspaceConfig.java688
-rw-r--r--logo/src/xlogo/utils/ExtensionFichier.java75
-rw-r--r--logo/src/xlogo/utils/Icon_x4s.pngbin0 -> 3795 bytes
-rw-r--r--logo/src/xlogo/utils/Logo_xlogo4schools.pngbin0 -> 4583 bytes
-rw-r--r--logo/src/xlogo/utils/Utils.java481
-rw-r--r--logo/src/xlogo/utils/WebPage.java66
-rw-r--r--logo/src/xlogo/utils/WriteImage.java122
-rw-r--r--logo/src/xlogo/utils/animation.pngbin0 -> 3427 bytes
-rw-r--r--logo/src/xlogo/utils/background.pngbin0 -> 13385 bytes
-rw-r--r--logo/src/xlogo/utils/chercher.pngbin0 -> 1784 bytes
-rw-r--r--logo/src/xlogo/utils/close_icon.pngbin0 -> 1029 bytes
-rw-r--r--logo/src/xlogo/utils/down_arrow.pngbin0 -> 979 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau0.pngbin0 -> 293 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau1.pngbin0 -> 1167 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau10.pngbin0 -> 373 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau11.pngbin0 -> 992 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau12.pngbin0 -> 217 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau13.pngbin0 -> 1528 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau2.pngbin0 -> 1783 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau3.pngbin0 -> 1523 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau4.pngbin0 -> 1630 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau5.pngbin0 -> 1178 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau6.pngbin0 -> 193 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau7.pngbin0 -> 5606 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau8.pngbin0 -> 1945 bytes
-rw-r--r--logo/src/xlogo/utils/drapeau9.pngbin0 -> 589 bytes
-rw-r--r--logo/src/xlogo/utils/editcopy.pngbin0 -> 1289 bytes
-rw-r--r--logo/src/xlogo/utils/editcut.pngbin0 -> 330 bytes
-rw-r--r--logo/src/xlogo/utils/editpaste.pngbin0 -> 2159 bytes
-rw-r--r--logo/src/xlogo/utils/error.pngbin0 -> 1469 bytes
-rw-r--r--logo/src/xlogo/utils/fileprint.pngbin0 -> 1610 bytes
-rw-r--r--logo/src/xlogo/utils/fog.pngbin0 -> 7561 bytes
-rw-r--r--logo/src/xlogo/utils/gnu_gpl.pngbin0 -> 23180 bytes
-rw-r--r--logo/src/xlogo/utils/icone.pngbin0 -> 16894 bytes
-rw-r--r--logo/src/xlogo/utils/info_icon.pngbin0 -> 4057 bytes
-rw-r--r--logo/src/xlogo/utils/light0.pngbin0 -> 11493 bytes
-rw-r--r--logo/src/xlogo/utils/light1.pngbin0 -> 11494 bytes
-rw-r--r--logo/src/xlogo/utils/light2.pngbin0 -> 11455 bytes
-rw-r--r--logo/src/xlogo/utils/light3.pngbin0 -> 11422 bytes
-rw-r--r--logo/src/xlogo/utils/menubtn.pngbin0 -> 1254 bytes
-rw-r--r--logo/src/xlogo/utils/play.pngbin0 -> 3877 bytes
-rw-r--r--logo/src/xlogo/utils/preview0.pngbin0 -> 1078 bytes
-rw-r--r--logo/src/xlogo/utils/preview1.pngbin0 -> 2552 bytes
-rw-r--r--logo/src/xlogo/utils/preview2.pngbin0 -> 3228 bytes
-rw-r--r--logo/src/xlogo/utils/preview3.pngbin0 -> 1471 bytes
-rw-r--r--logo/src/xlogo/utils/preview4.pngbin0 -> 3416 bytes
-rw-r--r--logo/src/xlogo/utils/preview5.pngbin0 -> 2996 bytes
-rw-r--r--logo/src/xlogo/utils/preview6.pngbin0 -> 1470 bytes
-rw-r--r--logo/src/xlogo/utils/quit.pngbin0 -> 760 bytes
-rw-r--r--logo/src/xlogo/utils/redo.pngbin0 -> 1601 bytes
-rw-r--r--logo/src/xlogo/utils/remove_256.pngbin0 -> 1227 bytes
-rw-r--r--logo/src/xlogo/utils/screenshot.pngbin0 -> 7472 bytes
-rw-r--r--logo/src/xlogo/utils/stop.pngbin0 -> 1056 bytes
-rw-r--r--logo/src/xlogo/utils/tortue0.pngbin0 -> 1369 bytes
-rw-r--r--logo/src/xlogo/utils/tortue1.pngbin0 -> 5105 bytes
-rw-r--r--logo/src/xlogo/utils/tortue2.pngbin0 -> 4305 bytes
-rw-r--r--logo/src/xlogo/utils/tortue3.pngbin0 -> 2014 bytes
-rw-r--r--logo/src/xlogo/utils/tortue4.pngbin0 -> 5255 bytes
-rw-r--r--logo/src/xlogo/utils/tortue5.pngbin0 -> 3993 bytes
-rw-r--r--logo/src/xlogo/utils/tortue6.pngbin0 -> 2456 bytes
-rw-r--r--logo/src/xlogo/utils/undo.pngbin0 -> 1489 bytes
-rw-r--r--logo/src/xlogo/utils/up_arrow.pngbin0 -> 986 bytes
-rw-r--r--logo/src/xlogo/utils/zoomin.pngbin0 -> 1628 bytes
-rw-r--r--logo/src/xlogo/utils/zoomout.pngbin0 -> 1588 bytes
234 files changed, 45661 insertions, 0 deletions
diff --git a/logo/src/xlogo/AppSettings.java b/logo/src/xlogo/AppSettings.java
new file mode 100644
index 0000000..60cc1e7
--- /dev/null
+++ b/logo/src/xlogo/AppSettings.java
@@ -0,0 +1,151 @@
+package xlogo;
+
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+
+import xlogo.storage.workspace.Language;
+import xlogo.storage.workspace.SyntaxHighlightConfig;
+
+/**
+ * This singleton class shall eliminate the accesses to Logo.messages.getString(),
+ * provide a unique way to access current settings and to stay updated through change events.
+ * @since 10th June 2014 - not yet consistently used throughout the application
+ * @author Marko
+ *
+ */
+public class AppSettings
+{
+ private static AppSettings instance;
+
+ public static AppSettings getInstance()
+ {
+ if (instance == null)
+ instance = new AppSettings();
+ return instance;
+ }
+
+ /* * * * * * *
+ * LANGUAGE
+ * * * * * * */
+
+ private Language language;
+
+ public Language getLanguage()
+ {
+ return language;
+ }
+
+ public void setLanguage(Language language)
+ {
+ if (language == this.language)
+ return;
+ this.language = language;
+ Logo.generateLanguage(language);
+ notifyLanguageChanged();
+ }
+
+ /**
+ * Translate the key into the current language.
+ * Shortcut for Logo.messages#getString()
+ * @param key
+ * @return
+ */
+ public String translate(String key)
+ {
+ return Logo.messages.getString(key);
+ }
+
+ private ArrayList<ActionListener> languageChangeListeners = new ArrayList<ActionListener>();
+
+ public void addLanguageChangeListener(ActionListener listener)
+ {
+ languageChangeListeners.add(listener);
+ }
+
+ public void removeLanguageChangeListener(ActionListener listener)
+ {
+ languageChangeListeners.remove(listener);
+ }
+
+ private void notifyLanguageChanged()
+ {
+ ActionEvent event = new ActionEvent(this, 0, "languageChange");
+ for (ActionListener listener : languageChangeListeners)
+ listener.actionPerformed(event);
+ }
+
+ /* * * * * * *
+ * FONT
+ * * * * * * */
+
+ private Font font;
+
+ public Font getFont()
+ {
+ return font;
+ }
+
+ public void setFont(Font font)
+ {
+ this.font = font;
+ notifyFontChanged();
+ }
+
+ private ArrayList<ActionListener> fontChangeListeners = new ArrayList<ActionListener>();
+
+ public void addFontChangeListener(ActionListener listener)
+ {
+ fontChangeListeners.add(listener);
+ }
+
+ public void removeFontChangeListener(ActionListener listener)
+ {
+ fontChangeListeners.remove(listener);
+ }
+
+ private void notifyFontChanged()
+ {
+ ActionEvent event = new ActionEvent(this, 0, "fontChange");
+ for (ActionListener listener : fontChangeListeners)
+ listener.actionPerformed(event);
+ }
+
+ /* * * * * * *
+ * SYNTAX HIGHLIGHTING STYLE
+ * * * * * * */
+
+ private SyntaxHighlightConfig syntaxHighlightingStyles;
+
+ public SyntaxHighlightConfig getSyntaxHighlightStyles()
+ {
+ return syntaxHighlightingStyles;
+ }
+
+ public void setSyntaxHighlightingStyles(SyntaxHighlightConfig syntaxHighlighStyles)
+ {
+ this.syntaxHighlightingStyles = syntaxHighlighStyles;
+ notifySyntaxHighlightStyleChanged();
+ }
+
+ private ArrayList<ActionListener> syntaxHighlightStyleChangeListeners = new ArrayList<ActionListener>();
+
+ public void addSyntaxHighlightStyleChangeListener(ActionListener listener)
+ {
+ syntaxHighlightStyleChangeListeners.add(listener);
+ }
+
+ public void removeSyntaxHighlightStyleChangeListener(ActionListener listener)
+ {
+ syntaxHighlightStyleChangeListeners.remove(listener);
+ }
+
+ private void notifySyntaxHighlightStyleChanged()
+ {
+ ActionEvent event = new ActionEvent(this, 0, "fontChange");
+ for (ActionListener listener : syntaxHighlightStyleChangeListeners)
+ listener.actionPerformed(event);
+ }
+
+}
diff --git a/logo/src/xlogo/Application.java b/logo/src/xlogo/Application.java
new file mode 100644
index 0000000..9a60655
--- /dev/null
+++ b/logo/src/xlogo/Application.java
@@ -0,0 +1,1875 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * a lot of modifications, extensions, refactorings might been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.swing.*;
+import javax.swing.UIManager.LookAndFeelInfo;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Stack;
+
+import xlogo.interfaces.BasicFileContainer.FileContainerChangeListener;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector.ErrorListener;
+import xlogo.interfaces.ProcedureMapper.ProcedureMapListener;
+import xlogo.interfaces.X4SModeSwitcher.ModeChangeListener;
+import xlogo.kernel.DrawPanel;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+import xlogo.utils.WriteImage;
+import xlogo.gui.components.ProcedureSearch;
+import xlogo.gui.components.TurtleComboBox;
+import xlogo.gui.components.ProcedureSearch.ProcedureSearchRequestListener;
+import xlogo.gui.components.fileslist.FilesList;
+import xlogo.gui.*;
+import xlogo.kernel.Affichage;
+import xlogo.kernel.Kernel;
+import xlogo.kernel.network.NetworkServer;
+import xlogo.kernel.perspective.Viewer3D;
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.AsyncMediumAdapter;
+import xlogo.messages.async.AsyncMessage;
+import xlogo.messages.async.AsyncMessenger;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.messages.async.history.HistoryMessenger;
+
+/**
+ * @author Marko
+ * @author Lo�c Le Coq
+ */
+public class Application
+{
+ private static final int BG_COLOR = 0xB3BCEA;
+ public static final String appName = "XLogo4Schools";
+
+ private static Stack<String> pile_historique = new Stack<String>();
+ private int index_historique = 0;
+
+ private MenuListener menulistener;
+
+ public boolean error = false;
+ boolean stop = false;
+
+ public Affichage affichage = null;
+ private Sound_Player son = new Sound_Player(this);
+ private Touche touche = new Touche();
+ private Popup jpop;
+
+ // pref Box
+ /**
+ * To display 3D View
+ */
+ private Viewer3D viewer3d = null;
+
+ // Interpreter and drawer
+ private Kernel kernel;
+
+ private UserSpace userSpace;
+ private UserConfig uc;
+
+ /*
+ * My Layout
+ */
+
+ private JFrame mainFrame = new JFrame();
+ private JPanel filesAndProcedures = new JPanel();
+ private JPanel commandOrEditor;
+ private FilesList filesList;
+ private ProcedureSearch procedureSearch;
+
+ // drawingOrEditor@Drawing
+ private JPanel commandCard = new JPanel();
+ private ZoneCommande commandLine = new ZoneCommande(this);
+ private JLabel recordTimerLabel = new JLabel();
+ private JButton stopButton = new JButton();
+ private JButton menuButton = new JButton();
+ public JSplitPane drawingAndHistory = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+
+ private JPanel drawingAndExtras = new JPanel();
+ private HistoryPanel history = new HistoryPanel(this);
+
+ public JScrollPane scrollArea;
+ private DrawPanel drawPanel;
+ private JPanel extrasPanel = new JPanel();
+ private JSlider speedSlider = new JSlider(JSlider.VERTICAL);
+
+ // drawingOrEditor@Editor
+ public Editor editeur = new Editor(this);
+
+ // Extras Menu
+ private JPopupMenu extras = new JPopupMenu();
+
+ private static final String COMMAND_CARD_ID = "command_card";
+ private static final String EDITOR_CARD_ID = "editor_card";
+
+
+
+ /** Builds the main frame */
+ public Application()
+ {
+ uc = WSManager.getUserConfig();
+
+ userSpace = new UserSpace();
+ kernel = new Kernel(this, userSpace);
+ initMessenger();
+ kernel.initInterprete();
+
+ menulistener = new MenuListener(this);
+ jpop = new Popup(menulistener, commandLine);
+
+ try
+ {
+ initGUI();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ setText();
+
+ initListeners();
+
+ showWelcomeMessage();
+ }
+
+ /**
+ * Hello Username
+ * You defined a, b, c, d, ...
+ */
+ public void showWelcomeMessage() {
+ HistoryMessenger.getInstance().dispatchComment(Logo.messages.getString(MessageKeys.APP_HELLO) + " " + uc.getUserName() + "!\n");
+
+ StringBuilder sb = new StringBuilder();
+ for (String procedureName : userSpace.getAllProcedureNames())
+ {
+ sb.append(procedureName);
+ sb.append(", ");
+ }
+ if (sb.length() == 0)
+ return;
+
+ sb.delete(sb.length() - 2, sb.length());
+
+ HistoryMessenger.getInstance().dispatchComment(
+ Logo.messages.getString("definir") + " "
+ + sb.toString() + ".\n");
+ }
+
+ public JFrame getFrame()
+ {
+ return mainFrame;
+ }
+
+ private void initMessenger()
+ {
+
+ DialogMessenger.getInstance().setMedium(new AsyncMediumAdapter<AsyncMessage<JFrame>, JFrame>(){
+ public boolean isReady()
+ {
+ return getFrame().isDisplayable();
+ }
+ public JFrame getMedium()
+ {
+ return getFrame();
+ }
+ public void addMediumReadyListener(final AsyncMessenger messenger)
+ {
+ getFrame().addWindowStateListener(new WindowStateListener(){
+
+ @Override
+ public void windowStateChanged(WindowEvent e)
+ {
+ if (getFrame().isDisplayable())
+ messenger.onMediumReady();
+ }
+ });
+ }
+ });
+
+
+ }
+
+ private void initGUI()
+ {
+ setTheme();
+ initFrame();
+ JPanel contentPane = (JPanel) mainFrame.getContentPane();
+ JPanel toplevel = new JPanel();
+ toplevel.setLayout(new BorderLayout());
+ layoutFilesAndProcedures();
+ toplevel.add(filesAndProcedures, BorderLayout.WEST);
+ commandOrEditor = new JPanel(new CardLayout());
+ toplevel.add(commandOrEditor, BorderLayout.CENTER);
+
+ commandOrEditor.add(commandCard, COMMAND_CARD_ID);
+ commandOrEditor.add(editeur.getComponent(), EDITOR_CARD_ID);
+ //commandOrEditor.setPreferredSize(new Dimension(d.width-200, d.height));
+ showCommandCard();
+
+ scrollArea = new JScrollPane();
+ DrawPanel.dessin=new BufferedImage(uc.getImageWidth(),uc.getImageHeight(),BufferedImage.TYPE_INT_RGB);
+ drawPanel = new DrawPanel(this);
+
+ layoutCommandCard();
+ layoutDrawingAndExtras();
+ layoutDrawingArea();
+ layoutExtras();
+ initMenu();
+
+ contentPane.add(toplevel);
+ getFrame().setVisible(true);
+ initDrawingZone();
+ }
+
+ private void initFrame()
+ {
+ Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
+ mainFrame.setSize(new Dimension(d.width, d.height * 9 / 10));
+ mainFrame.setTitle("XLogo4Schools" + " - " + uc.getUserName());
+ mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ mainFrame.setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("Icon_x4s.png")));
+ mainFrame.addWindowListener(new WindowListener(){
+ public void windowOpened(WindowEvent e){}
+ public void windowIconified(WindowEvent e){}
+ public void windowDeiconified(WindowEvent e){}
+ public void windowActivated(WindowEvent e){}
+ public void windowDeactivated(WindowEvent e){}
+ public void windowClosing(WindowEvent e)
+ {
+ closeWindow();
+ }
+ public void windowClosed(WindowEvent e){}
+ });
+ }
+
+ /**
+ * @author Marko Zivkovic - new impl
+ */
+ public void closeWindow()
+ {
+ WSManager storageManager = WSManager.getInstance();
+ try
+ {
+ if (!uc.isVirtual())
+ {
+ String openFile = userSpace.getOpenFileName();
+ if (openFile != null)
+ {
+ userSpace.writeFileText(openFile, editeur.getText());
+ userSpace.storeFile(openFile);
+ }
+ storageManager.getUserConfigInstance().store();
+ }
+ System.exit(0);
+ }
+ catch (Exception e1)
+ {
+ String message = Logo.messages.getString(MessageKeys.US_COULD_NOT_STORE_RECENT_DATA) + "\n\n"
+ + e1.toString() + "\n\n"
+ + Logo.messages.getString("quitter?");
+
+ String[] choix = { Logo.messages.getString(MessageKeys.DIALOG_YES), Logo.messages.getString(MessageKeys.DIALOG_NO) };
+ int retval = JOptionPane.showOptionDialog(
+ mainFrame,
+ message,
+ Logo.messages.getString("general.error.title"),
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.ERROR_MESSAGE,
+ null, choix, choix[0]);
+
+ if (retval == JOptionPane.OK_OPTION)
+ System.exit(0);
+ }
+ }
+
+ private void initDrawingZone()
+ {
+ // on centre la tortue
+ // Centering turtle
+ Dimension dim=scrollArea.getViewport().getViewRect().getSize();
+ Point p=new Point(Math.abs(
+ uc.getImageWidth()/2-dim.width/2),
+ Math.abs(uc.getImageHeight()/2-dim.height/2));
+ scrollArea.getViewport().setViewPosition(p);
+
+
+ MediaTracker tracker=new MediaTracker(getFrame());
+ tracker.addImage(DrawPanel.dessin,0);
+ try{tracker.waitForID(0);}
+ catch(InterruptedException e){}
+ drawPanel.getGraphics().drawImage(DrawPanel.dessin,0,0,mainFrame);
+ scrollArea.validate();
+
+ setCommandLine(false);
+ genere_primitive();
+ uc.setHeure_demarrage(Calendar.getInstance().getTimeInMillis());
+ setCommandLine(true);
+ focus_Commande();
+ resizeDrawingZone();
+ }
+
+ private void setTheme()
+ {
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ try
+ {
+ for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
+ {
+ if ("Nimbus".equals(info.getName()))
+ {
+ UIManager.setLookAndFeel(info.getClassName());
+ UIManager.put("defaultFont", font);
+ //UIManager.put("defaultFont", new Font(Font.SANS_SERIF, 0, 18));
+ return;
+ }
+ }
+
+ switch (uc.getLooknfeel())
+ {
+ case JAVA:
+ //MetalLookAndFeel.setCurrentTheme(uc.getColorTheme().getTheme());
+ UIManager.setLookAndFeel(new MetalLookAndFeel());
+ break;
+ default:
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+ catch (Exception ignore){}
+ }
+ UIManager.put("defaultFont", font);
+ }
+
+ private void layoutFilesAndProcedures()
+ {
+
+ filesList = new FilesList(userSpace);
+ procedureSearch = new ProcedureSearch(userSpace);
+ filesAndProcedures = new JPanel(new GridBagLayout());
+
+ // Note : the following JPanel saved my day ...
+ // thanks to this The files list is finally position at the top :-)
+ JPanel layoutHelper = new JPanel(new BorderLayout());
+ layoutHelper.add(filesList, BorderLayout.NORTH);
+
+ JScrollPane scroller = new JScrollPane(layoutHelper);
+ scroller.getViewport().setBackground(new Color(BG_COLOR));
+ layoutHelper.setBackground(new Color(BG_COLOR));
+
+ GridBagConstraints c = new GridBagConstraints();
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weighty = 0;
+ c.gridx = 0;
+ c.gridy = 0;
+ c.anchor = GridBagConstraints.NORTHWEST;
+ filesAndProcedures.add(procedureSearch, c);
+
+ c.gridx = 0;
+ c.gridy = 1;
+
+ c.weighty = 1;
+ c.weightx = 1;
+
+ c.fill = GridBagConstraints.BOTH;
+
+ c.anchor = GridBagConstraints.NORTHWEST;
+
+ filesAndProcedures.add(scroller, c);
+
+ }
+
+ private void layoutCommandCard()
+ {
+
+ int preferredHeight = WSManager.getWorkspaceConfig().getFont().getSize() * 15 / 10;
+
+ stopButton.setIcon(createImageIcon("stop.png", 20, 20));
+ menuButton.setIcon(createImageIcon("menubtn.png", 20, 20));
+
+ stopButton.setMaximumSize(new Dimension(preferredHeight, preferredHeight));
+ menuButton.setMaximumSize(new Dimension(preferredHeight, preferredHeight));
+ commandCard.setBackground(Color.white);
+ Font clockFont = new Font(null, Font.BOLD, 20);
+
+ recordTimerLabel.setOpaque(true);
+ recordTimerLabel.setBackground(Color.white);
+ recordTimerLabel.setFont(clockFont);
+ recordTimerLabel.setAlignmentY(JLabel.CENTER_ALIGNMENT);
+
+ commandCard.add(recordTimerLabel);
+ commandCard.add(commandLine);
+ commandCard.add(drawingAndHistory);
+ commandLine.setAlignmentX(JFrame.CENTER_ALIGNMENT);
+
+ drawingAndHistory.setTopComponent(drawingAndExtras);
+ drawingAndHistory.setBottomComponent(history);
+ drawingAndHistory.setResizeWeight(0.8);
+
+ GroupLayout commandCardLayout = new GroupLayout(commandCard);
+ commandCard.setLayout(commandCardLayout);
+ commandLine.setAlignmentY(JComponent.CENTER_ALIGNMENT);
+
+ commandCardLayout.setAutoCreateContainerGaps(false);
+ commandCardLayout.setAutoCreateGaps(false);
+
+ commandCardLayout.setVerticalGroup(
+ commandCardLayout.createSequentialGroup()
+ .addGroup(commandCardLayout.createParallelGroup()
+ .addComponent(commandLine)// TODO GAP?
+ .addComponent(recordTimerLabel)
+ .addGap(2)
+ .addComponent(stopButton)
+ .addGap(2)
+ .addComponent(menuButton))
+ .addComponent(drawingAndHistory));
+
+ commandCardLayout.setHorizontalGroup(
+ commandCardLayout.createParallelGroup()
+ .addGroup(commandCardLayout.createSequentialGroup()
+ .addComponent(commandLine)
+ .addComponent(recordTimerLabel)
+ .addComponent(stopButton)
+ .addComponent(menuButton))
+ .addComponent(drawingAndHistory));
+ }
+
+ private void layoutDrawingAndExtras()
+ {
+ history.setMinimumSize(new Dimension(600, 40));
+ drawingAndExtras.setBackground(new Color(BG_COLOR));
+
+ drawingAndExtras.add(scrollArea);
+ drawingAndExtras.add(extrasPanel);
+
+ GroupLayout drawingAndExtrasLayout = new GroupLayout(drawingAndExtras);
+ drawingAndExtras.setLayout(drawingAndExtrasLayout);
+
+ drawingAndExtrasLayout.setAutoCreateContainerGaps(false);
+ drawingAndExtrasLayout.setAutoCreateGaps(false);
+
+ drawingAndExtrasLayout.setVerticalGroup(
+ drawingAndExtrasLayout.createParallelGroup()
+ .addComponent(scrollArea)
+ .addComponent(extrasPanel));
+
+ drawingAndExtrasLayout.setHorizontalGroup(
+ drawingAndExtrasLayout.createSequentialGroup()
+ .addComponent(scrollArea)
+ .addComponent(extrasPanel));
+ }
+
+ private void layoutDrawingArea()
+ {
+ drawPanel.setSize(new Dimension(
+ (int) (uc.getImageWidth() * DrawPanel.zoom),
+ (int) (uc.getImageHeight() * DrawPanel.zoom)));
+ scrollArea.getViewport().add(drawPanel);
+ scrollArea.getHorizontalScrollBar().setBlockIncrement(5);
+ scrollArea.getVerticalScrollBar().setBlockIncrement(5);
+ scrollArea.setPreferredSize(drawPanel.getPreferredSize());
+ }
+
+ private void layoutExtras()
+ {
+ extrasPanel.setBackground(new Color(BG_COLOR));
+ extrasPanel.setLayout(new BoxLayout(extrasPanel, BoxLayout.Y_AXIS));
+ speedSlider.setMaximumSize(new Dimension(20,200));
+ speedSlider.setValue(speedSlider.getMaximum() - uc.getTurtleSpeed());
+
+ final TurtleComboBox turtleCombo = new TurtleComboBox();
+ extrasPanel.add(speedSlider);
+ extrasPanel.add(turtleCombo);
+ turtleCombo.setBackground(new Color(BG_COLOR));
+
+ turtleCombo.getComboBox().setSelectedIndex(uc.getActiveTurtle());
+
+
+ turtleCombo.getComboBox().addActionListener(new ActionListener(){
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ int i = turtleCombo.getComboBox().getSelectedIndex();
+ System.out.println(i);
+ uc.setActiveTurtle(i);
+ getKernel().getActiveTurtle().setShape(uc.getActiveTurtle());
+ getKernel().change_image_tortue("tortue" + i + ".png");
+ }
+ });
+
+ }
+
+ public void showCommandCard()
+ {
+ CardLayout cardLayout = (CardLayout) commandOrEditor.getLayout();
+ cardLayout.show(commandOrEditor, COMMAND_CARD_ID);
+ }
+
+ public void showEditorCard()
+ {
+ CardLayout cardLayout = (CardLayout) commandOrEditor.getLayout();
+ cardLayout.show(commandOrEditor, EDITOR_CARD_ID);
+ }
+
+ private JMenuItem startContestMenuItem;
+ private JMenuItem stopContestMenuItem;
+ private JMenuItem importMenuItem;
+ private JMenuItem exportMenuItem;
+ private JMenuItem saveImage;
+
+ public void initMenu()
+ {
+ //Create the popup menu.
+ extras = new JPopupMenu();
+
+ if (!uc.isVirtual()) // Contest Mode not available in virtual mode
+ {
+ startContestMenuItem = new JMenuItem(Logo.messages.getString(MessageKeys.CONTEST_MODE_START));
+ stopContestMenuItem = new JMenuItem(Logo.messages.getString(MessageKeys.CONTEST_MODE_STOP));
+ extras.add(startContestMenuItem,0);
+
+ startContestMenuItem.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e)
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ int nOfFiles = wc.getNOfContestFiles();
+ int nOfBonusFiles = wc.getNOfContestBonusFiles();
+
+ String[] contestFileNames = new String[nOfFiles + nOfBonusFiles];
+
+ String nameBase = Logo.messages.getString("contest.mode.filename") + " ";
+ for (int i = 0; i < nOfFiles; i++)
+ contestFileNames[i] = nameBase + (i + 1);
+
+ nameBase = Logo.messages.getString("contest.mode.bonus.filename") + " ";
+
+ for (int i = 0; i < nOfBonusFiles; i++)
+ contestFileNames[nOfFiles + i] = nameBase + (i + 1);
+ try
+ {
+ userSpace.startRecordMode(contestFileNames);
+ }
+ catch (IOException e1)
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.could.not.create") + "\n" + e.toString());
+ return;
+ }
+ }
+ });
+ stopContestMenuItem.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e)
+ {
+ userSpace.stopRecordMode();
+ }
+ });
+ }
+
+ importMenuItem = new JMenuItem(Logo.messages.getString(MessageKeys.US_IMPORT));
+ extras.add(importMenuItem);
+ importMenuItem.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e)
+ {
+ final JFileChooser fc = new JFileChooser();
+ fc.setFileSelectionMode(JFileChooser.OPEN_DIALOG);
+ fc.setFileFilter(new FileFilter(){
+
+ @Override
+ public String getDescription()
+ {
+ return GlobalConfig.LOGO_FILE_EXTENSION;
+ }
+
+ @Override
+ public boolean accept(File f)
+ {
+ return f.isDirectory() || f.getName().endsWith(GlobalConfig.LOGO_FILE_EXTENSION);
+ }
+ });;
+
+ int returnVal = fc.showOpenDialog(getFrame());
+
+ if(returnVal == JFileChooser.APPROVE_OPTION)
+ {
+ File file = fc.getSelectedFile();
+ try
+ {
+ userSpace.importFile(file);
+ }
+ catch (IOException e1)
+ {
+ DialogMessenger.getInstance().dispatchError(MessageKeys.GENERAL_ERROR_TITLE, e1.toString());
+ }
+ }
+
+ }
+ });
+
+ exportMenuItem = new JMenuItem(Logo.messages.getString(MessageKeys.US_EXPORT));
+ extras.add(exportMenuItem);
+ exportMenuItem.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e)
+ {
+
+ String fileName = (String) JOptionPane.showInputDialog(getFrame(), Logo.messages.getString(MessageKeys.US_EXPORT_MSG) + "\n",
+ Logo.messages.getString(MessageKeys.US_EXPORT), JOptionPane.PLAIN_MESSAGE, null, userSpace.getFileNames(), "ham");
+
+ //If a string was returned, say so.
+ if (fileName == null || fileName.length() == 0)
+ return;
+
+ final JFileChooser fc = new JFileChooser();
+ fc.setFileSelectionMode(JFileChooser.SAVE_DIALOG);
+ fc.setFileFilter(new FileFilter(){
+
+ @Override
+ public String getDescription()
+ {
+ return GlobalConfig.LOGO_FILE_EXTENSION;
+ }
+
+ @Override
+ public boolean accept(File f)
+ {
+ return f.isDirectory() || f.getName().endsWith(GlobalConfig.LOGO_FILE_EXTENSION);
+ }
+ });
+ ;
+
+ int returnVal = fc.showOpenDialog(getFrame());
+
+ if (returnVal == JFileChooser.APPROVE_OPTION)
+ {
+ File file = fc.getSelectedFile();
+ try
+ {
+ userSpace.exportFile(fileName, file);
+ }
+ catch (IOException e1)
+ {
+ DialogMessenger.getInstance().dispatchError(MessageKeys.GENERAL_ERROR_TITLE,
+ "Could not export file : \n " + e1.toString());
+ }
+ }
+ }
+ });
+
+ saveImage = new JMenuItem(Logo.messages.getString(MessageKeys.US_SAVE_IMAGE));
+ extras.add(saveImage);
+ saveImage.addActionListener(new ActionListener(){
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ WriteImage writeImage = new WriteImage(getFrame(), getDrawPanel()
+ .getSelectionImage());
+ int value = writeImage.chooseFile();
+ if (value == JFileChooser.APPROVE_OPTION) {
+ writeImage.start();
+ }
+ }
+ });
+ }
+
+ private ImageIcon createImageIcon(String path, int width, int heigth) {
+ Image img = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(path));
+ return new ImageIcon(img.getScaledInstance(width, heigth, Image.SCALE_SMOOTH));
+ }
+
+ /*
+ * CHANGE LISTENERS
+ */
+
+ /**
+ * This glues together models and the GUI controllers.
+ */
+ private void initListeners()
+ {
+ commandLine.addMouseListener(new MouseAdapter(){
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ maybeShowPopup(e);
+ }
+ @Override
+ public void mouseReleased(MouseEvent e)
+ {
+ maybeShowPopup(e);
+ }
+
+ private void maybeShowPopup(MouseEvent e)
+ {
+ if (e.isPopupTrigger())
+ {
+ jpop.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+ });
+
+ commandLine.addKeyListener(touche);
+
+ speedSlider.addChangeListener(new ChangeListener()
+ {
+ public void stateChanged(ChangeEvent e)
+ {
+ JSlider source = (JSlider) e.getSource();
+ int value = source.getValue();
+ uc.setTurtleSpeed(source.getMaximum() - value);
+ }
+ });
+
+ stopButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent arg0)
+ {
+ error=true;
+ if (NetworkServer.isActive){
+ NetworkServer.stopServer();
+ }
+ setCommandLine(true);
+ commandLine.requestFocus();
+ }
+ });
+
+ menuButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ extras.show(menuButton, 0, 0);
+ }
+ });
+
+ userSpace.addProcedureMapListener(new ProcedureMapListener()
+ {
+ @Override
+ public void ownerRenamed(String oldName, String newName)
+ {
+ // ignore
+ }
+
+ @Override
+ public void undefined(final String fileName, final String procedure)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ onUndefined(fileName, procedure);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onUndefined(fileName, procedure);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ undefined(fileName, procedure);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onUndefined(String fileName, String procedure)
+ {
+ HistoryMessenger.getInstance().dispatchComment(
+ fileName + " : " +
+ Logo.messages.getString(MessageKeys.HIST_MSG_PROCEDURES_UNDEFINED) + " " +
+ procedure + ".\n");
+ }
+
+ @Override
+ public void undefined(final String fileName, final Collection<String> procedures)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ onUndefined(fileName, procedures);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onUndefined(fileName, procedures);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ undefined(fileName, procedures);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onUndefined(String fileName, Collection<String> procedures)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (String procedureName : procedures)
+ {
+ sb.append(procedureName);
+ sb.append(", ");
+ }
+ sb.delete(sb.length() - 2, sb.length() - 1);
+
+ HistoryMessenger.getInstance().dispatchComment(
+ fileName + " : " +
+ Logo.messages.getString(MessageKeys.HIST_MSG_PROCEDURES_UNDEFINED) + " "
+ + sb.toString() + ".\n");
+ }
+
+ @Override
+ public void defined(final String fileName, final String procedure)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ onDefined(fileName, procedure);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onDefined(fileName, procedure);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ defined(fileName, procedure);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onDefined(String fileName, String procedure)
+ {
+ HistoryMessenger.getInstance().dispatchComment(
+ fileName + " : " +
+ Logo.messages.getString("definir") + " " +
+ procedure + ".\n");
+ }
+
+ @Override
+ public void defined(final String fileName, final Collection<String> procedures)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ onDefined(fileName, procedures);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onDefined(fileName, procedures);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ defined(fileName, procedures);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onDefined(String fileName, Collection<String> procedures)
+ {
+ if (procedures.size() == 0)
+ return;
+
+ StringBuilder sb = new StringBuilder();
+ for (String procedureName : procedures)
+ {
+ sb.append(procedureName);
+ sb.append(", ");
+ }
+ sb.delete(sb.length() - 2, sb.length());
+
+ HistoryMessenger.getInstance().dispatchComment(
+ fileName + " : " +
+ Logo.messages.getString("definir") + " "
+ + sb.toString() + ".\n");
+ }
+ });
+
+ userSpace.addFileListener(new FileContainerChangeListener()
+ {
+ public void fileOpened(final String fileName)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ onFileOpened(fileName);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onFileOpened(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileOpened(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onFileOpened(String fileName)
+ {
+ showEditorCard();
+ editeur.setText(userSpace.readFile(fileName));
+ mainFrame.setTitle(appName + " - " + uc.getUserName() + " - " + fileName);
+ }
+
+ public void fileClosed(final String fileName)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ onFileClosed(fileName);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onFileClosed(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileClosed(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onFileClosed(String fileName)
+ {
+ try
+ {
+ if (userSpace.existsFile(fileName)) // It is possibly deleted, and the editor is therefore closed.
+ {
+ userSpace.writeFileText(fileName, editeur.getText());
+ userSpace.storeFile(fileName);
+ }
+ }
+ catch (IOException e)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString("general.error.title"),
+ Logo.messages.getString("ws.automatic.save.failed"));
+ }
+ showCommandCard();
+ commandLine.requestFocus();
+ //commandLine.requestFocus();
+ mainFrame.setTitle(appName + " - " + uc.getUserName());
+ }
+
+ @Override
+ public void fileAdded(String fileName) { }
+
+ @Override
+ public void fileRemoved(String fileName)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ showCommandCard();
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ showCommandCard();
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileRemoved(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void fileRenamed(String oldName, String newName) { }
+
+ @Override
+ public void editRightsChanged(boolean editEnabled) { }
+
+ });
+
+ userSpace.addErrorListener(new ErrorListener()
+ {
+ @Override
+ public void errorsDetected(final String fileName)
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ onErrorsDetected(fileName);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onErrorsDetected(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ errorsDetected(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onErrorsDetected(String fileName)
+ {
+ HistoryMessenger.getInstance().dispatchError(fileName + " " +
+ Logo.messages.getString(MessageKeys.FILE_CONTAINS_ERRORS) + "\n");
+
+ for(String msg : userSpace.getErrorMessages(fileName))
+ HistoryMessenger.getInstance().dispatchError(msg + "\n");
+ }
+
+ @Override
+ public void allErrorsCorrected(String fileName)
+ {
+ // ignore
+ }
+ });
+
+ userSpace.addModeChangedListener(new ModeChangeListener()
+ {
+ @Override
+ public void recordModeStopped()
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onRecordModeStopped();
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onRecordModeStopped();
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ recordModeStopped();
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onRecordModeStopped()
+ {
+ extras.remove(stopContestMenuItem);
+ extras.add(startContestMenuItem, 0);
+ importMenuItem.setEnabled(true);
+ exportMenuItem.setEnabled(true);
+ recordTimer.stop();
+ recordTimerLabel.setText(null);
+ }
+
+ @Override
+ public void recordModeStarted()
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onRecordModeStarted();
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onRecordModeStarted();
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ recordModeStarted();
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private Timer recordTimer;
+
+ private void onRecordModeStarted()
+ {
+ extras.remove(startContestMenuItem);
+ extras.add(stopContestMenuItem, 0);
+ importMenuItem.setEnabled(false);
+ exportMenuItem.setEnabled(false);
+ recordTimer = new Timer(1000, new ActionListener(){
+ private Date start = Calendar.getInstance().getTime();
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+
+ Date now = Calendar.getInstance().getTime();
+
+ long diff = now.getTime() - start.getTime();
+
+ recordTimerLabel.setText(UserConfig.getMinSec(diff));
+ }
+ });
+ recordTimer.start();
+ }
+
+ @Override
+ public void networkModeStopped()
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onNetworkModeStopped();
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onNetworkModeStopped();
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ networkModeStopped();
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onNetworkModeStopped()
+ {
+ filesList.setEnabled(true);
+ extras.setEnabled(true);
+ }
+
+ @Override
+ public void networkModeStarted()
+ {
+
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onNetworkModeStarted();
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onNetworkModeStarted();
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ networkModeStarted();
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onNetworkModeStarted()
+ {
+ filesList.setEnabled(false);
+ extras.setEnabled(false);
+ }
+ });
+
+ procedureSearch.addSearchRequestListener(new ProcedureSearchRequestListener(){
+ public void requestProcedureSearch(String procedureName)
+ {
+ displayProcedure(procedureName);
+ }
+ });
+ }
+
+ public void displayProcedure(String procedureName)
+ {
+ Procedure proc = userSpace.getExecutable(procedureName);
+ if (proc != null)
+ displayProcedure(proc);
+ else
+ {
+ HistoryMessenger.getInstance().dispatchError(
+ procedureName + " : " +
+ Logo.messages.getString(MessageKeys.EDITOR_DISPLAY_PROC_NOT_FOUND)
+ + "\n");
+ }
+ }
+
+ public void displayProcedure(Procedure proc)
+ {
+ String openFile = userSpace.getOpenFileName();
+
+ if (openFile != null)
+ {
+ try
+ {
+ userSpace.writeFileText(openFile, editeur.getText());
+ userSpace.storeFile(openFile);
+ userSpace.closeFile(openFile);
+ }
+ catch (IOException e){}
+ }
+
+ String fileName = proc.getOwnerName();
+ if (userSpace.existsFile(fileName))
+ {
+ userSpace.openFile(fileName);
+ editeur.displayProcedure(proc.getName());
+ }
+ }
+
+ // BELOW IS MOSTLY XLOGO LEGACY CODE
+
+ /**
+ * Called by the constructor or when language has been modified
+ */
+ public void setText()
+ {
+ stopButton.setToolTipText(Logo.messages.getString("interrompre_execution"));
+
+ // Texte interne à utiliser pour JFileChooser et JColorChooser
+ UIManager.put("FileChooser.cancelButtonText", Logo.messages.getString("pref.cancel"));
+ UIManager.put("FileChooser.cancelButtonToolTipText", Logo.messages.getString("pref.cancel"));
+ UIManager.put("FileChooser.fileNameLabelText", Logo.messages.getString("nom_du_fichier"));
+ UIManager.put("FileChooser.upFolderToolTipText", Logo.messages.getString("dossier_parent"));
+ UIManager.put("FileChooser.lookInLabelText", Logo.messages.getString("rechercher_dans"));
+
+ UIManager.put("FileChooser.newFolderToolTipText", Logo.messages.getString("nouveau_dossier"));
+ UIManager.put("FileChooser.homeFolderToolTipText", Logo.messages.getString("repertoire_accueil"));
+ UIManager.put("FileChooser.filesOfTypeLabelText", Logo.messages.getString("fichier_du_type"));
+ UIManager.put("FileChooser.helpButtonText", Logo.messages.getString("menu.help"));
+
+ history.updateText();
+ jpop.setText();
+ }
+
+ // Ce qu'il se passe en validant dans la zone de texte
+ /**
+ * When the user types "Enter" in the Command Line
+ * @author Lo�c Le Coq
+ */
+ public void commande_actionPerformed()
+ {
+ // System.out.println("commandeTotal :"+Runtime.getRuntime().totalMemory()/1024/1024+" Free "+Runtime.getRuntime().freeMemory()/1024/1024);
+ // Si une parenthese était sélectionnée, on désactive la
+ // décoloration
+ commandLine.setActive(false);
+ // System.out.println(commande.getCaret().isVisible());
+ if (stop)
+ stop = false;
+ String texte = commandLine.getText();
+ if (!texte.equals("") && commandLine.isEditable())
+ {
+ if (touche.tape)
+ {
+ touche.tape = false;
+ pile_historique.pop();
+ }
+ if (pile_historique.size() > 49)
+ pile_historique.remove(0);
+ pile_historique.push(texte); // On rajoute ce texte à l'historique
+ index_historique = pile_historique.size(); // on réajuste l'index
+ // de
+ // l'historique
+
+ HistoryMessenger.getInstance().dispatchMessage(texte + "\n");
+
+ // On enlève les éventuels commentaires
+ int a = texte.indexOf("#");
+
+ // LogoParser lp=new LogoParser(texte);
+ while (a != -1)
+ {
+ if (a == 0)
+ {
+ texte = "";
+ break;
+ }
+ else if (!texte.substring(a - 1, a).equals("\\"))
+ {
+ texte = texte.substring(0, a);
+ break;
+ }
+ a = texte.indexOf("#", a + 1);
+ }
+
+ if(userSpace.isRecordMode())
+ recordCommandLine(texte);
+
+ Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
+ startInterpretation(Utils.decoupe(texte));
+
+ // On efface la ligne de commande
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run()
+ {
+ commandLine.setText("");
+ }
+ });
+
+ }
+ }
+
+ /**
+ * @author Marko Zivkovic
+ * @param text
+ */
+ public void recordCommandLine(final String text)
+ {
+ new Thread(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ PrintWriter out = null;
+ File logoFile = uc.getCommandLineContestFile();
+ String line = UserConfig.getTimeStamp() + " : " + text;
+ try
+ {
+ out = new PrintWriter(new BufferedWriter(new FileWriter(logoFile, true)));
+ out.println(line);
+ }catch(Exception e)
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.could.not.store") + "\n" + e.toString());
+ }finally
+ {
+ if(out != null)
+ out.close();
+ }
+ }
+ }).run();
+ }
+
+ /**
+ * Launch the Thread Affichage with the instructions "st"
+ *
+ * @param st
+ * List of instructions
+ * @author Lo�c Le Coq
+ * @author Marko Zivkovic - renamed (it was affichage_Start)
+ */
+ public void startInterpretation(StringBuffer st)
+ {
+ affichage = new Affichage(this, st);
+ affichage.start();
+ }
+
+ /**
+ * Get Method for Sound Player
+ *
+ * @return The Sound Player
+ */
+ public Sound_Player getSon()
+ {
+ return son;
+ }
+
+ /**
+ * Resize the dawing area
+ */
+ public void resizeDrawingZone()
+ {
+ if (null != affichage)
+ {
+ affichage.setPause(true);
+ }
+ // resize the drawing image
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run()
+ {
+ DrawPanel.dessin = new BufferedImage(uc.getImageWidth(), uc.getImageHeight(),
+ BufferedImage.TYPE_INT_RGB);
+ // System.out.println("Total :"+Runtime.getRuntime().totalMemory()/1024+" max "+Runtime.getRuntime().maxMemory()/1024+" Free "+Runtime.getRuntime().freeMemory()/1024);
+ MediaTracker tracker = new MediaTracker(drawPanel);
+ tracker.addImage(DrawPanel.dessin, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException e)
+ {}
+ // ardoise1.getGraphics().drawImage(Ardoise.dessin,0,0,ardoise1);
+
+ drawPanel.setPreferredSize(new Dimension(uc.getImageWidth(), uc.getImageHeight()));
+ drawPanel.revalidate();
+ kernel.initGraphics();
+ // ardoise.repaint();
+ //calculateMargin(); TODOD maybe return this
+
+ Dimension d = scrollArea.getViewport().getViewRect().getSize();
+ Point p = new Point(
+ Math.abs((uc.getImageWidth() - d.width) / 2),
+ Math.abs((uc.getImageHeight() - d.height) / 2));
+ scrollArea.getViewport().setViewPosition(p);
+ if (null != affichage)
+ affichage.setPause(false);
+ }
+
+ });
+
+ }
+
+ /**
+ * Change Syntax Highlighting for the editor, the command line and the
+ * History zone
+ *
+ * @param c_comment
+ * Comment Color
+ * @param sty_comment
+ * Comment style
+ * @param c_primitive
+ * Primitive Color
+ * @param sty_primitive
+ * Primitive style
+ * @param c_parenthese
+ * Parenthesis Color
+ * @param sty_parenthese
+ * Parenthesis Style
+ * @param c_operande
+ * Operand Color
+ * @param sty_operande
+ * Operand style
+ */
+ public void changeSyntaxHighlightingStyle(int c_comment, int sty_comment, int c_primitive, int sty_primitive,
+ int c_parenthese, int sty_parenthese, int c_operande, int sty_operande)
+ {
+ WorkspaceConfig uc = WSManager.getWorkspaceConfig();
+ editeur.initStyles(uc.getCommentColor(), uc.getCommentStyle(), uc.getPrimitiveColor(),
+ uc.getPrimitiveStyle(), uc.getBraceColor(), uc.getBraceStyle(),
+ uc.getOperatorColor(), uc.getOperatorStyle());
+ commandLine.initStyles(uc.getCommentColor(), uc.getCommentStyle(), uc.getPrimitiveColor(),
+ uc.getPrimitiveStyle(), uc.getBraceColor(), uc.getBraceStyle(),
+ uc.getOperatorColor(), uc.getOperatorStyle());
+ history.getDsd().initStyles(uc.getCommentColor(), uc.getCommentStyle(),
+ uc.getPrimitiveColor(), uc.getPrimitiveStyle(), uc.getBraceColor(),
+ uc.getBraceStyle(), uc.getOperatorColor(), uc.getOperatorStyle());
+ }
+
+ /**
+ * Enable or disable Syntax Highlighting
+ */
+ public void setColoration(boolean b)
+ {
+ history.setColoration(b);
+ commandLine.setColoration(b);
+ }
+
+ /**
+ * Return the drawing area
+ *
+ * @return The drawing area
+ */
+ public DrawPanel getDrawPanel()
+ {
+ return drawPanel;
+ }
+
+ /**
+ * Set Focus on the command line
+ */
+ public void focus_Commande()
+ {
+ commandLine.requestFocus();
+ commandLine.getCaret().setVisible(true);
+ }
+
+ /**
+ * Notice if the command line is editable.
+ *
+ * @return true if Command Line is editable, false otherwise
+ */
+ public boolean commande_isEditable()
+ {
+ return commandLine.isEditable();
+ }
+
+ /**
+ * Set the text in the command Line
+ *
+ * @param txt
+ * The text to write
+ */
+ public void setCommandText(String txt)
+ {
+ commandLine.setText(txt);
+ }
+
+ /**
+ * Get History panel
+ *
+ * @return The HistoryPanel
+ */
+ public HistoryPanel getHistoryPanel()
+ {
+ return history;
+ }
+
+ /**
+ * Enable or disable the command line and the play button
+ */
+ public void setCommandLine(boolean b)
+ {
+ if (b)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ commandLine.setEditable(true);
+ commandLine.setBackground(Color.WHITE);
+ }
+ else
+ {
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run()
+ {
+ commandLine.setEditable(true);
+ // commande.requestFocus();
+ commandLine.setBackground(Color.WHITE);
+ }
+ });
+ }
+ }
+ else
+ {
+ commandLine.setEditable(false);
+ commandLine.setBackground(new Color(250, 232, 217));
+ }
+ }
+
+ /**
+ * This method copy the selected Text in the command line
+ */
+ protected void copy()
+ {
+ commandLine.copy();
+ }
+
+ /**
+ * This methos cut the selected Text in the command line
+ */
+ protected void cut()
+ {
+ commandLine.cut();
+ }
+
+ /**
+ * This methos paste the selected Text into the command line
+ */
+ protected void paste()
+ {
+ commandLine.paste();
+ }
+
+ /**
+ * This methos selects all the Text in the command line
+ */
+ protected void select_all()
+ {
+ commandLine.selectAll();
+ }
+
+ /**
+ * This method creates all primitives.
+ * In XLogo4Schools, this method does not initialize the startup files
+ * anymore, because we don't have startup files.
+ *
+ * @author Marko Zivkovic
+ */
+ protected void genere_primitive()
+ {
+ kernel.initPrimitive();
+ }
+
+ /**
+ * Set the last key pressed to the key corresponding to integer i
+ *
+ * @param i
+ * The key code
+ */
+ public void setCar(int i)
+ {
+ touche.setCar(i);
+ }
+
+ /**
+ * Returns an int that corresponds to the last key pressed.
+ *
+ * @return the int representing the last key pressed
+ */
+ public int getCar()
+ {
+ return touche.getCar();
+ }
+
+ /**
+ * This boolean indicates if the viewer3D is visible
+ *
+ * @return true or false
+ */
+ public boolean viewer3DVisible()
+ {
+ if (null != viewer3d)
+ return viewer3d.isVisible();
+ return false;
+ }
+
+ /**
+ * Initialize the 3D Viewer
+ */
+ public void initViewer3D()
+ {
+ if (null == viewer3d)
+ {
+ viewer3d = new Viewer3D(drawPanel.getWorld3D(), drawPanel.getBackgroundColor());
+ }
+
+ }
+
+ public Viewer3D getViewer3D()
+ {
+ return viewer3d;
+ }
+
+ /**
+ * Open the Viewer3D Frame
+ */
+ public void viewerOpen()
+ {
+ if (null == viewer3d)
+ {
+ viewer3d = new Viewer3D(drawPanel.getWorld3D(), drawPanel.getBackgroundColor());
+ }
+ else
+ {
+ viewer3d.setVisible(false);
+ }
+ viewer3d.insertBranch();
+ viewer3d.setVisible(true);
+ viewer3d.requestFocus();
+ }
+
+ /**
+ * Returns the current kernel
+ *
+ * @return The Kernel Object associated to main frame
+ */
+ public Kernel getKernel()
+ {
+ return kernel;
+ }
+
+ /**
+ *
+ * @author loic This class is the Controller for the Command Line<br>
+ * It looks for key event, Upper and Lower Arrow for History<br>
+ * And all other Characters
+ */
+ class Touche extends KeyAdapter
+ {
+ int car = -1;
+
+ private boolean tape = false;
+
+ public void setCar(int i)
+ {
+ car = i;
+ }
+
+ public int getCar()
+ {
+ return car;
+ }
+
+ public void keyPressed(KeyEvent e)
+ {
+ int ch = e.getKeyChar();
+ int code = e.getKeyCode();
+ if (commandLine.isEditable())
+ {
+ if (code == KeyEvent.VK_UP)
+ {
+ if (index_historique > 0)
+ {
+ if (index_historique == pile_historique.size())
+ {
+ tape = true;
+ pile_historique.push(commandLine.getText());
+ }
+ index_historique--;
+ commandLine.setText(pile_historique.get(index_historique));
+ }
+ else
+ index_historique = 0;
+ }
+ else if (code == KeyEvent.VK_DOWN)
+ {
+ if (index_historique < pile_historique.size() - 1)
+ {
+ index_historique++;
+ commandLine.setText(pile_historique.get(index_historique));
+ }
+ else
+ index_historique = pile_historique.size() - 1;
+ }
+ }
+ else
+ {
+ if (ch != 65535)
+ car = ch;
+ else
+ car = -code;
+ }
+ }
+ }
+}
diff --git a/logo/src/xlogo/Logo.java b/logo/src/xlogo/Logo.java
new file mode 100644
index 0000000..86406af
--- /dev/null
+++ b/logo/src/xlogo/Logo.java
@@ -0,0 +1,183 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * a lot of modifications, extensions, refactorings have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+package xlogo;
+
+import javax.media.j3d.VirtualUniverse;
+import javax.swing.UIManager;
+import javax.swing.SwingUtilities;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ResourceBundle;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.workspace.Language;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.gui.welcome.WelcomeScreen;
+import xlogo.kernel.Primitive;
+/**
+ * @author loic
+ *
+ *@author Marko - this class generates the language,
+ *displays the welcome screen for workspace and user selection,
+ *and then starts {@link Application} when the user space is entered.
+ */
+public class Logo {
+ /**
+ * This ResourceBundle contains all messages for XLogo (menu, errors...)
+ */
+ public static ResourceBundle messages=null;
+
+ public static String translationLanguage[]=new String[14];
+
+ /**
+ * Welcome screen is always displayed when the application is started.
+ * The user selected a workspace and a user to enter the application.
+ */
+ private WelcomeScreen welcomeScreen=null;
+
+ // private Language language;
+
+
+ /**
+ * The main methods
+ * @param args The file *.lgo to load on startup
+ */
+ public static void main(String[] args) {
+
+ try{
+ // Display the java3d version
+ java.util.Map<String,String> map=VirtualUniverse.getProperties();
+ System.out.println("Java3d :"+map.get("j3d.version"));
+ }
+ catch(Exception e){
+ System.out.println("Java3d problem");
+ e.printStackTrace();
+ }
+
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+
+ //Recuperer les fichiers de démarrage correspondant au double clic de souris
+ // ou au lancement en ligne de commande
+
+ GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance();
+
+ for(int i=0;i<args.length;i++){
+ gc.getPath().add(args[i]);
+ }
+
+ gc.getPath().add(0,"#####");
+ new Logo();
+ }
+
+ /**Builds Application with the valid Config*/
+ public Logo() {
+ Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage();
+ generateLanguage(lang);
+
+ showWelcomeScreen();
+ }
+
+ /**
+ * Display {@link xlogo.gui.welcome.WelcomeScreen} when starting the application.
+ * @author Marko Zivkovic
+ */
+ private void showWelcomeScreen() {
+
+ try{
+ SwingUtilities.invokeAndWait(new Runnable(){
+ public void run(){
+ welcomeScreen=new WelcomeScreen(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ enterApplication();
+ }
+ });
+ }
+ });
+ //UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel());
+ } catch(Exception e){
+ System.out.println("here " + e.toString()); //TODO error m
+ }
+
+ }
+
+ /**
+ *
+ */
+ public void enterApplication(){
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+
+ welcomeScreen.dispose();
+ generateLanguage(wc.getLanguage());
+
+ // Initialize frame
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ new Application();
+ }
+ });
+
+ }
+
+ /**
+ * Sets the selected language for all messages
+ * @param id The integer that represents the language
+ */
+ public static void generateLanguage(Language lang){ // fixe la langue utilisée pour les messages
+ messages=ResourceBundle.getBundle("langage",lang.getLocale());
+ translationLanguage[0]=Logo.messages.getString("pref.general.french");
+ translationLanguage[1]=Logo.messages.getString("pref.general.english");
+ translationLanguage[2]=Logo.messages.getString("pref.general.arabic");
+ translationLanguage[3]=Logo.messages.getString("pref.general.spanish");
+ translationLanguage[4]=Logo.messages.getString("pref.general.portuguese");
+ translationLanguage[5]=Logo.messages.getString("pref.general.esperanto");
+ translationLanguage[6]=Logo.messages.getString("pref.general.german");
+ translationLanguage[7]=Logo.messages.getString("pref.general.galician");
+ translationLanguage[8]=Logo.messages.getString("pref.general.asturian");
+ translationLanguage[9]=Logo.messages.getString("pref.general.greek");
+ translationLanguage[10]=Logo.messages.getString("pref.general.italian");
+ translationLanguage[11]=Logo.messages.getString("pref.general.catalan");
+ translationLanguage[12]=Logo.messages.getString("pref.general.hungarian");
+ translationLanguage[13]=Logo.messages.getString("pref.general.abz.german.english");
+
+ Primitive.buildPrimitiveTreemap(lang);
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/MemoryChecker.java b/logo/src/xlogo/MemoryChecker.java
new file mode 100644
index 0000000..f35f73a
--- /dev/null
+++ b/logo/src/xlogo/MemoryChecker.java
@@ -0,0 +1,163 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * a lot of modifications, extensions, refactorings have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ **/
+package xlogo;
+
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.global.GlobalConfig;
+
+/**
+ * This class is a thread that prevents from memory Overflow <br>
+ * Those problems could happen when a program loops indefinitely<br>
+ * Eg:<br>
+ * <br>
+ * <tt>
+ * To bad<br>
+ * fd 1 rt 1 <br>
+ * bad<br>
+ * this lines will explode memory<br>
+ * end <br>
+ * </tt>
+ *
+ * @author loic
+ *
+ * @author Marko Zivkovic - Extensive tests and provocation of the memory usage error message have shown
+ * that in XLogo4Schools, using a maximum 128MB heap, the memory threshold is never reached
+ * (Or very, very late in a gigantic recursion).
+ *
+ */
+public class MemoryChecker extends Thread
+{
+ /**
+ * The main frame
+ */
+ private Application cadre;
+ /**
+ * This boolean indicates if the thread has to continue.<br>
+ * If false, the thread will stop.
+ */
+ private boolean alive;
+
+ /**
+ * Constructs the Memory Checker for the main Frame
+ *
+ * @param cadre
+ * the main Frame
+ */
+ public MemoryChecker(Application cadre)
+ {
+ this.cadre = cadre;
+ }
+
+ private static int maxSleepTime = 10000;
+ private static int minSleepTime = 500;
+ private static int sleepRange = maxSleepTime - minSleepTime;
+
+ /**
+ * The Run Method for the Thread
+ */
+ public void run()
+ {
+ /*
+ * Marko : I reduced the amount of calculations done in every iteration.
+ * Before it included 1 subtraction and 2 divisions and a fetch from GlobalConfig,
+ * now only 1 subtraction is used to determine consumed > consumptionThreshold
+ * and another long comparison was added to regulate frequency of memory checks.
+ */
+ long consumptionThreshold = GlobalConfig.getMemoryThreshold();
+ long listenThreshold = (long) (0.8 * consumptionThreshold);
+
+
+ /*
+ * I increased default sleeping time to about 10 seconds, because a check every 1 second is too much overhead for doing nothing useful.
+ * However, as soon as the listenThreshold is reached, the sleepTime will start to decrease.
+ * Only once the listenThreshold is reached,
+ * the MemoryChecker becomes important to prevent an OutOfMemoryError and start suggesting the garbage collector to work.)
+ */
+ int sleepTime = maxSleepTime;
+
+ alive = true;
+
+ while (alive)
+ {
+ try
+ {
+ Thread.sleep(sleepTime);
+ }
+ catch (InterruptedException ignore)
+ {}
+ long free = Runtime.getRuntime().freeMemory();
+ long total = Runtime.getRuntime().totalMemory();
+ long consumed = (total - free);
+
+ if (consumed >= listenThreshold)
+ {
+ sleepTime = (int) (maxSleepTime - consumed / total * sleepRange);
+
+ /*
+ * Marko :
+ * In XLogo, when the error dialog was displayed, it usually kept displaying several times, disrupting any further use of the interpreter.
+ * The only out was to restart the application. This indicates that, although heavy memory usage was detected by this thread,
+ * the garbage collector did not yet start to free memory from dead objects.
+ * (Discussions about GC in the Internet indicate that the Java GC starts working as soon as a heap generation gets full.
+ * It is possible that the young generation is not full enough when 0.9*MaxMemory is reached, thus the message keeps appearing all the time,
+ * because memory is not sufficiently cleaned up.)
+ * Therefore, at least suggest the GC that it would be a good moment for garbage collection, and help recover. That's the best we can do.
+ */
+ System.gc();
+ }
+ else
+ sleepTime = maxSleepTime;
+
+ if (consumed > consumptionThreshold)
+ {
+ cadre.error = true;
+ alive = false;
+ DialogMessenger.getInstance().dispatchError(Logo.messages.getString("erreur"),
+ Logo.messages.getString("depassement_memoire"));
+ }
+ }
+ }
+
+ /**
+ * Causes the memory checker loop to break.
+ * @author Marko
+ */
+ public void kill()
+ {
+ alive = false;
+ }
+
+}
diff --git a/logo/src/xlogo/MenuListener.java b/logo/src/xlogo/MenuListener.java
new file mode 100644
index 0000000..a40fd99
--- /dev/null
+++ b/logo/src/xlogo/MenuListener.java
@@ -0,0 +1,234 @@
+/**
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/* Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+
+package xlogo;
+// TODO Use parts of this that might be useful, and then delete this file
+import java.awt.*;
+import java.io.*;
+
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.image.BufferedImage;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.workspace.Language;
+import xlogo.utils.WebPage;
+import xlogo.utils.Utils;
+import xlogo.gui.AImprimer;
+import xlogo.gui.MyTextAreaDialog;
+import xlogo.kernel.DrawPanel;
+
+/**
+ * This class is the Controller for the main frame.<br>
+ * All events are interpreted by this class
+ *
+ * @author loic
+ *
+ */
+public class MenuListener extends JDialog implements ActionListener {
+ private static final long serialVersionUID = 1L;
+ private double zoomfactor = 1.25;
+ protected static final String FILE_NEW = "new";
+ protected static final String FILE_OPEN = "open";
+ protected static final String FILE_SAVE_AS = "save_as";
+ protected static final String FILE_SAVE = "save";
+ protected static final String FILE_SAVE_IMAGE = "record_image";
+ protected static final String FILE_COPY_IMAGE = "copy_image";
+ protected static final String FILE_PRINT_IMAGE = "print_image";
+ protected static final String FILE_SAVE_TEXT = "save_text";
+ protected static final String FILE_QUIT = "quit";
+ protected static final String EDIT_SELECT_ALL = "select_all";
+ public static final String EDIT_COPY = "copy";
+ public static final String EDIT_CUT = "cut";
+ public static final String EDIT_PASTE = "paste";
+ protected static final String TOOLS_PEN_COLOR = "pen_color";
+ protected static final String TOOLS_SCREEN_COLOR = "screen_color";
+ protected static final String TOOLS_TRANSLATOR = "translator";
+ protected static final String TOOLS_OPTIONS = "preferences";
+ protected static final String TOOLS_ERASER = "eraser";
+ protected static final String HELP_LICENCE = "licence";
+ protected static final String HELP_TRANSLATED_LICENCE = "translated_licence";
+ protected static final String HELP_TRANSLATE_XLOGO = "translate_xlogo";
+ protected static final String HELP_ABOUT = "about";
+ public static final String PLAY = "play";
+ public static final String ZOOMIN = "zoomin";
+ public static final String ZOOMOUT = "zoomout";
+
+ private static final String WEB_SITE = "http://xlogo.tuxfamily.org";
+ private static final String MAIL = "[email protected]";
+ private Application cadre;
+
+ /**
+ * Attached the controller MenuListener to the main Frame
+ *
+ * @param cadre
+ * main Frame
+ */
+ public MenuListener(Application cadre) {
+ this.cadre = cadre;
+ }
+
+ /**
+ * This method dispatches all events from the main Frame and executes the
+ * task corresponding to the incoming event.
+ */
+ public void actionPerformed(ActionEvent e) {
+ Language lang = WSManager.getWorkspaceConfig().getLanguage();
+
+ String cmd = e.getActionCommand();
+ if (MenuListener.EDIT_COPY.equals(cmd)) { // Copier
+ cadre.copy();
+ } else if (MenuListener.EDIT_CUT.equals(cmd)) { // Couper
+ cadre.cut();
+ } else if (MenuListener.EDIT_PASTE.equals(cmd)) { // Coller
+ cadre.paste();
+ } else if (MenuListener.EDIT_SELECT_ALL.equals(cmd)) { // select all
+ cadre.select_all();
+ } else if (MenuListener.FILE_COPY_IMAGE.equals(cmd)) { // Copier l'image
+ // au
+ // presse-papier
+ Thread copie = new CopyImage();
+ copie.start();
+ } else if (MenuListener.FILE_PRINT_IMAGE.equals(cmd)) { // imprimer
+ // l'image
+ AImprimer can = new AImprimer(cadre.getDrawPanel()
+ .getSelectionImage());
+ Thread imprime = new Thread(can);
+ imprime.start();
+ } else if (MenuListener.HELP_ABOUT.equals(cmd)) { // Boite de dialogue A
+ // propos
+ String message = Logo.messages.getString("message_a_propos1")
+ + GlobalConfig.getVersion() + "\n\n"
+ + Logo.messages.getString("message_a_propos2") + " "
+ + MenuListener.WEB_SITE + "\n\n"
+ + Logo.messages.getString("message_a_propos3") + "\n "
+ + MenuListener.MAIL;
+ MyTextAreaDialog jt = new MyTextAreaDialog(message);
+ ImageIcon icone = new ImageIcon(
+ Utils.class.getResource("icone.png"));
+ JOptionPane.showMessageDialog(null, jt,
+ Logo.messages.getString("menu.help.about"),
+ JOptionPane.INFORMATION_MESSAGE, (Icon) icone);
+
+ } else if (MenuListener.ZOOMIN.equals(cmd)) {
+ cadre.getDrawPanel().zoom(zoomfactor * DrawPanel.zoom, true);
+ } else if (MenuListener.ZOOMOUT.equals(cmd)) {
+ cadre.getDrawPanel().zoom(1 / zoomfactor * DrawPanel.zoom, false);
+ }
+ else if (MenuListener.HELP_LICENCE.equals(cmd) | MenuListener.HELP_TRANSLATED_LICENCE.equals(cmd))
+ { // Affichage
+ // de la
+ // licence
+ JFrame frame = new JFrame(Logo.messages.getString("menu.help.licence"));
+ frame.setIconImage(Toolkit.getDefaultToolkit().createImage(WebPage.class.getResource("icone.png")));
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.setSize(500, 500);
+ WebPage editorPane = new WebPage();
+ editorPane.setEditable(false);
+ String path = "gpl/gpl-";
+ if (MenuListener.HELP_LICENCE.equals(cmd))
+ path += "en";
+ else
+ {
+ path += lang.getLanguageCode();
+ }
+ path += ".html";
+ java.net.URL helpURL = MenuListener.class.getResource(path);
+ if (helpURL != null)
+ {
+ try
+ {
+ editorPane.setPage(helpURL);
+ }
+ catch (IOException e1)
+ {
+ System.err.println("Attempted to read a bad URL: " + helpURL);
+ }
+ }
+ else
+ {
+ System.err.println("Couldn't find file: " + path);
+ }
+
+ // Put the editor pane in a scroll pane.
+ JScrollPane editorScrollPane = new JScrollPane(editorPane);
+ editorScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+ editorScrollPane.setPreferredSize(new Dimension(250, 145));
+ editorScrollPane.setMinimumSize(new Dimension(10, 10));
+ frame.getContentPane().add(editorScrollPane);
+ frame.setVisible(true);
+ }
+
+ }
+
+ /**
+ * This class is a thread that copy the Image selection into the clipboard
+ */
+ class CopyImage extends Thread implements Transferable {
+ private BufferedImage image = null;
+ Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
+
+ CopyImage() {
+ image = cadre.getDrawPanel().getSelectionImage();
+ }
+
+ public void run() {
+ clip.setContents(this, null);
+ }
+
+ public DataFlavor[] getTransferDataFlavors() {
+ return new DataFlavor[] { DataFlavor.imageFlavor };
+ }
+
+ public boolean isDataFlavorSupported(DataFlavor flavor) {
+ return DataFlavor.imageFlavor.equals(flavor);
+ }
+
+ public Object getTransferData(DataFlavor flavor)
+ throws UnsupportedFlavorException {
+ if (!isDataFlavorSupported(flavor)) {
+ throw new UnsupportedFlavorException(flavor);
+ }
+ return image;
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/Popup.java b/logo/src/xlogo/Popup.java
new file mode 100644
index 0000000..a2acc2d
--- /dev/null
+++ b/logo/src/xlogo/Popup.java
@@ -0,0 +1,161 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */package xlogo;
+import javax.swing.JMenuItem;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.BadLocationException;
+import javax.swing.JPopupMenu;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.Language;
+
+import java.awt.event.*;
+/**
+ * This class represents the JPopupMenu when the user clicks with the right button on the command line.<br>
+ * It displays the action cut, copy, paste and:<br>
+ * In esperanto mode, the special characters for esperanto are added.
+ * @author loic
+ *
+ */
+public class Popup extends JPopupMenu implements ActionListener {
+ private static final long serialVersionUID = 1L;
+ /**
+ * This array contains all special characters for esperanto
+ */
+ private String[] car=new String[12];
+ /**
+ * Items for esperanto special characters
+ */
+ private JMenuItem[] jpopcar=new JMenuItem[12];
+ /**
+ * Item for copy action
+ */
+ private JMenuItem jpopcopier = new JMenuItem();
+ /**
+ * Item for paste action
+ */
+ private JMenuItem jpopcoller = new JMenuItem();
+ /**
+ * Item for cut action
+ */
+ private JMenuItem jpopcouper = new JMenuItem();
+ /**
+ * Separator between items
+ */
+ private JPopupMenu.Separator separ=new JPopupMenu.Separator();
+ /**
+ * The text Component for the Jpopup Menu
+ */
+ private JTextComponent jt=null;
+ /**
+ * This Constructot attached the Jpopup to Text Component jt
+ * @param menulistener The Controller for action
+ * @param jt The Text Component
+ */
+ public Popup(ActionListener menulistener,JTextComponent jt){
+ this.jt=jt;
+ car[0]="\u0109";
+ car[1]="\u011d";
+ car[2]="\u0125";
+ car[3]="\u0135";
+ car[4]="\u015d";
+ car[5]="\u016d";
+ car[6]="\u0108";
+ car[7]="\u011c";
+ car[8]="\u0124";
+ car[9]="\u0134";
+ car[10]="\u015c";
+ car[11]="\u016c";
+ for(int i=0;i<car.length;i++) {
+ jpopcar[i]=new JMenuItem(car[i]);
+ jpopcar[i].addActionListener(this);
+ jpopcar[i].setActionCommand(car[i]);
+ }
+ add(jpopcouper);
+ add(jpopcopier);
+ add(jpopcoller);
+ jpopcouper.setActionCommand(MenuListener.EDIT_CUT);
+ jpopcouper.addActionListener(this);
+ jpopcoller.setActionCommand(MenuListener.EDIT_PASTE);
+ jpopcoller.addActionListener(this);
+ jpopcopier.setActionCommand(MenuListener.EDIT_COPY);
+ jpopcopier.addActionListener(this);
+ setText();
+ }
+ /**
+ * Called by constructor and when the language is modified.
+ */
+ void setText(){
+ jpopcoller.setText(Logo.messages.getString("menu.edition.paste"));
+ jpopcouper.setText(Logo.messages.getString("menu.edition.cut"));
+ jpopcopier.setText(Logo.messages.getString("menu.edition.copy"));
+ // Si le langage choisie est l'esperanto, on rajoute les caractères accentués spéciaux au menu
+ if (WSManager.getWorkspaceConfig().getLanguage()==Language.LANGUAGE_ESPERANTO) {
+ add(separ);
+ for (int i=0;i<jpopcar.length;i++) {
+ add(jpopcar[i]);
+ }
+ }
+ else if(this.getComponentCount()>3){
+ remove(separ);
+ for (int i=0;i<jpopcar.length;i++) remove(jpopcar[i]);
+ }
+ }
+ /**
+ * The action to dispatch when the user clicks on the popup menu
+ */
+ public void actionPerformed(ActionEvent e){
+ String cmd=e.getActionCommand();
+ if (cmd.equals(MenuListener.EDIT_COPY)) jt.copy();
+ else if (cmd.equals(MenuListener.EDIT_CUT)) jt.cut();
+ else if (cmd.equals(MenuListener.EDIT_PASTE)) jt.paste();
+ else{
+ for (int i=0;i<car.length;i++){
+ if (car[i].equals(cmd)) {
+ int pos=jt.getCaretPosition();
+ try{
+ String debut=jt.getText(0,pos);
+ int longueur=jt.getText().length();
+ String fin="";
+ if (longueur>pos) fin=jt.getText(pos+1,longueur-pos);
+ jt.setText(debut+car[i]+fin);
+ jt.setCaretPosition(pos+1);
+ }
+ catch(BadLocationException e1){}
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/logo/src/xlogo/Sound_Player.java b/logo/src/xlogo/Sound_Player.java
new file mode 100644
index 0000000..9940f23
--- /dev/null
+++ b/logo/src/xlogo/Sound_Player.java
@@ -0,0 +1,281 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+
+package xlogo;
+import java.util.StringTokenizer;
+
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MetaEventListener;
+import javax.sound.midi.MetaMessage;
+import javax.sound.midi.MidiEvent;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Sequence;
+import javax.sound.midi.Sequencer;
+import javax.sound.midi.ShortMessage;
+import javax.sound.midi.Synthesizer;
+import javax.sound.midi.Track;
+
+import xlogo.kernel.LogoError;
+
+/**
+ * This class is designed to play music in XLogo
+ * @author loic
+ *
+ */
+public class Sound_Player {
+ private Application cadre;
+ static final int[ ] offsets = { 0, 2, 4, 5, 7,9,11};
+ private String[] notes={Logo.messages.getString("note.do"),Logo.messages.getString("note.re"),Logo.messages.getString("note.mi"),Logo.messages.getString("note.fa"),Logo.messages.getString("note.sol"),Logo.messages.getString("note.la"),Logo.messages.getString("note.si")};
+ private Track track=null;
+ private int instrument=0; //instrument selectionne
+ private int ticks=0; // Temps écoulé depuis le début de la piste
+ private Synthesizer synthesizer;
+ private Sequencer sequencer;
+ private Sequence sequence;
+ /**
+ * Builds our Sound Player
+ * @param cadre The main Frame
+ */
+ Sound_Player(Application cadre){
+ this.cadre=cadre;
+ try{sequence = new Sequence(Sequence.PPQ, 16);
+ }
+ catch(InvalidMidiDataException e){}
+ }
+ /**
+ * Choose a specific instrument
+ * @param i The integer that represents this instrument
+ */
+ public void setInstrument(int i){
+ instrument=i;
+ }
+ /**
+ * Return the current instrument
+ * @return The integer that represents the current instrument
+ */
+ public int getInstrument(){
+ return instrument;
+ }
+ /**
+ * This methode parses the note sequence and write it into a MIDI Sequence
+ * @param li This List contains the sequence of note to play
+ * @throws LogoError If the sequence in't valid
+ */
+ public void cree_sequence(String li) throws LogoError{
+ // 16 ticks per quarter note.
+ try{
+
+ /*
+ * Cette méthode met les notes contenues dans li dans la piste Track
+ * Voici les notations utilisées:
+ * do-re-mi-fa-sol-la-si: Les notes do+ pour le dièse, do- pour le bémol
+ * :+,:++ etc Monte d'une octave, de deux octaves etc
+ * :-,:--, etc Descend d'une octave, de deux octaves etc
+ * 1 ou 0.5 ou 2 : Durée affectée à la série de notes
+ */
+ if (null==track) track = sequence.createTrack( ); // Begin with a new track
+ // Set the instrument on channel 0
+ ShortMessage sm = new ShortMessage( );
+ sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument, 0);
+
+ track.add(new MidiEvent(sm, 0));
+ int notelength = 64; // valeur d'une noire
+ int velocity = 64; // default to middle volume
+ int basekey = 60; // 60 is middle C. Adjusted up and down by octave
+ int key=0;
+ StringTokenizer st=new StringTokenizer(li);
+ String element="";
+ if (st.hasMoreTokens()) element=st.nextToken().toLowerCase();
+ while (!element.equals("")){
+ int i=isnotes(element);
+ if(i!=-1){
+ key=basekey+offsets[isnotes(element)];
+ if (st.hasMoreTokens()) element=st.nextToken().toLowerCase();
+ else element="";
+ if (element.equals("+")) { //dièse
+ key++;
+ if (st.hasMoreTokens()) element=st.nextToken().toLowerCase();
+ else element="";
+ }
+ else if (element.equals("-")) { //bémol
+ key--;
+ if (st.hasMoreTokens()) element=st.nextToken().toLowerCase();
+ else element="";
+ }
+ // System.out.println(ticks+" "+notelength+" "+velocity+" "+key);
+ addNote(ticks,notelength,key,velocity);
+ ticks+=notelength;
+ }
+ if (element.equals(":")){
+ while (st.hasMoreTokens()){
+ element=st.nextToken().toLowerCase();
+ if (element.equals("+")) {
+ basekey+=12; //Une octave en plus
+ if (!st.hasMoreTokens()) element="";
+ }
+ else if (element.equals("-")) {
+ basekey-=12; //Une octave en moins
+ if (!st.hasMoreTokens()) element="";
+ }
+ else break;
+ }
+ }
+ else {
+ try{
+ double longueur=Double.parseDouble(element);
+ notelength=(int)(longueur*64+0.5);
+ if (st.hasMoreTokens()) element=st.nextToken().toLowerCase();
+ else element="";
+ }
+ catch(NumberFormatException e){
+ if (!element.equals("")&&isnotes(element)==-1&&!element.equals(":"))
+ throw new LogoError(Logo.messages.getString("probleme_sequence"));
+ }
+ }
+ }
+ }
+ catch(InvalidMidiDataException e2){}
+ }
+ /**
+ * If element is a note return the index for the notes in the array list "notes"
+ * @param element The String to test
+ * @return The index for this notes, -1 if it doesn't exist
+ */
+ int isnotes(String element){ //teste si element est une note et renvoie alors son numéro sinon renvoie -1
+ for (int i=0;i<notes.length;i++){
+ if (notes[i].equals(element)) return i;
+ }
+ return -1;
+ }
+
+ // These are some MIDI constants from the spec. They aren't defined
+ public static final int END_OF_TRACK = 47;
+/**
+ * Play the Track
+ */
+ public void joue(){
+ if (null!=track){
+ try{
+ // Set up the Sequencer and Synthesizer objects
+ sequencer = MidiSystem.getSequencer( );
+ sequencer.open( );
+ synthesizer = MidiSystem.getSynthesizer( );
+ synthesizer.open( );
+ sequencer.getTransmitter( ).setReceiver(synthesizer.getReceiver( ));
+ // Specify the sequence to play, and the tempo to play it at
+ sequencer.setSequence(sequence);
+ sequencer.setTempoInBPM(240);
+ // Let us know when it is done playing
+ sequencer.addMetaEventListener(new MetaEventListener( ) {
+ public void meta(MetaMessage m) {
+ // A message of this type is automatically sent
+ // when we reach the end of the track
+ if (m.getType( ) == END_OF_TRACK) {
+ sequencer.close();
+ synthesizer.close();
+ }
+ }
+ });
+ // And start playing now.
+ sequencer.start( );
+ try{
+ long temps=sequence.getTickLength();
+ while (temps>0){ //On attends que la séquence soit jouée.
+ Thread.sleep(500);
+ temps-=32;
+ if (cadre.error) { //Marko: LogoError.lance && TODO lance was always false : check this
+ //On a appuyé sur le bouton stop
+ sequencer.close();
+ synthesizer.close();
+ break;
+ }
+ }
+ }
+ catch (InterruptedException e){}
+ }
+ catch(MidiUnavailableException e1){}
+ catch(InvalidMidiDataException e2){}
+ }
+ }
+ /**
+ * Delete the sequence in memory
+ */
+ public void efface_sequence(){
+ try{
+ sequence=new Sequence(Sequence.PPQ,16);
+ track=null;
+ ticks=0;
+ }
+ catch(InvalidMidiDataException e){}
+
+
+ }
+ /**
+ * set tick index
+ * @param i
+ */
+ public void setTicks(int i){
+ ticks=i;
+ }
+ /**
+ * return tick index
+ * @return Tick index
+ */
+ public int getTicks(){
+ return ticks;
+ }
+
+ /**
+ * A convenience method to add a note to the track on channel 0
+ * @param startTick the starting tick
+ * @param tickLength the tick length
+ * @param key the note
+ * @param velocity the volume
+ * @throws InvalidMidiDataException
+ */
+
+ public void addNote(int startTick,
+ int tickLength, int key, int velocity)
+ throws InvalidMidiDataException
+ {
+ ShortMessage on = new ShortMessage( );
+ on.setMessage(ShortMessage.NOTE_ON, 0, key, velocity);
+ ShortMessage off = new ShortMessage( );
+ off.setMessage(ShortMessage.NOTE_OFF, 0, key, velocity);
+ track.add(new MidiEvent(on, startTick));
+ track.add(new MidiEvent(off, startTick + tickLength));
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/StyledDocument/DocumentLogo.java b/logo/src/xlogo/StyledDocument/DocumentLogo.java
new file mode 100644
index 0000000..4fc0cc7
--- /dev/null
+++ b/logo/src/xlogo/StyledDocument/DocumentLogo.java
@@ -0,0 +1,390 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */package xlogo.StyledDocument;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+
+import java.awt.*;
+
+import javax.swing.text.*;
+
+import xlogo.kernel.Primitive;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.WorkspaceConfig;
+
+/**
+ * Title : XLogo Description : XLogo is an interpreter for the Logo programming
+ * language
+ *
+ * @author Loïc Le Coq
+ */
+
+/*
+ * Cette classe permet de définir la coloration syntaxique dans l'éditeur
+ * Coloration des primitives Coloration des nombres, variables ou mot Coloration
+ * des commentaires Correspondance entre parenthèses ou crochets
+ */
+public class DocumentLogo extends DefaultStyledDocument {
+ private static final long serialVersionUID = 1L;
+ private DefaultStyledDocument doc;
+ private Element rootElement;
+
+ private MutableAttributeSet parenthese;
+ private MutableAttributeSet normal;
+ private MutableAttributeSet keyword;
+ private MutableAttributeSet comment;
+ private MutableAttributeSet quote;
+ private boolean coloration_activee = WSManager.getWorkspaceConfig().isSyntaxHighlightingEnabled();
+ private boolean colore_parenthese = false;
+
+ public void setColoration(boolean b) {
+ coloration_activee = b;
+ }
+
+ public DocumentLogo() {
+ WorkspaceConfig uc = WSManager.getWorkspaceConfig();
+
+ doc = this;
+ rootElement = doc.getDefaultRootElement();
+ putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n");
+ initStyles(uc.getCommentColor(), uc.getCommentStyle(),
+ uc.getPrimitiveColor(), uc.getPrimitiveStyle(),
+ uc.getBraceColor(), uc.getBraceStyle(),
+ uc.getOperatorColor(), uc.getOperatorStyle());
+ }
+
+ public void initStyles(int c_comment, int sty_comment, int c_primitive,
+ int sty_primitive, int c_parenthese, int sty_parenthese,
+ int c_operande, int sty_operande) {
+ Font font = WSManager.getWorkspaceConfig().getFont();
+
+ normal = new SimpleAttributeSet();
+ StyleConstants.setFontFamily(normal, font.getFamily());
+ StyleConstants.setForeground(normal, Color.black);
+ StyleConstants.setFontSize(normal, font.getSize());
+
+ comment = new SimpleAttributeSet();
+ StyleConstants.setForeground(comment, new Color(c_comment));
+ setBoldItalic(sty_comment, comment);
+ StyleConstants.setFontSize(comment, font.getSize());
+
+ keyword = new SimpleAttributeSet();
+ StyleConstants.setForeground(keyword, new Color(c_primitive));
+ setBoldItalic(sty_primitive, keyword);
+ StyleConstants.setFontSize(keyword, font.getSize());
+
+ quote = new SimpleAttributeSet();
+ StyleConstants.setForeground(quote, new Color(c_operande));
+ setBoldItalic(sty_operande, quote);
+ StyleConstants.setFontSize(quote, font.getSize());
+
+ parenthese = new SimpleAttributeSet();
+ StyleConstants.setForeground(parenthese, new Color(c_parenthese));
+ setBoldItalic(sty_parenthese, parenthese);
+ StyleConstants.setFontSize(parenthese, font.getSize());
+ }
+
+ void setBoldItalic(int id, MutableAttributeSet sty) {
+ switch (id) {
+ case 0: // aucun style
+ StyleConstants.setBold(sty, false);
+ StyleConstants.setItalic(sty, false);
+ StyleConstants.setUnderline(sty, false);
+ break;
+ case 1: // Gras
+ StyleConstants.setItalic(sty, false);
+ StyleConstants.setBold(sty, true);
+ StyleConstants.setUnderline(sty, false);
+ break;
+ case 2: // italique
+ StyleConstants.setBold(sty, false);
+ StyleConstants.setItalic(sty, true);
+ StyleConstants.setUnderline(sty, false);
+ break;
+ case 3: // Souligné
+ StyleConstants.setBold(sty, false);
+ StyleConstants.setItalic(sty, false);
+ StyleConstants.setUnderline(sty, true);
+ break;
+ }
+
+ }
+
+ /*
+ * Override to apply syntax highlighting after the document has been updated
+ */
+ public void insertString(int offset, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str.equals("\t"))
+ str = " ";
+ else if (str.equals("[")) {
+ if (offset > 0) {
+ String element = doc.getText(offset - 1, 1);
+ if (!element.equals(" ") && !element.equals("\\"))
+ str = " [";
+ }
+ } else if (str.equals("(")) {
+ if (offset > 0) {
+ String element = doc.getText(offset - 1, 1);
+ if ("\\ *-+/&|><=(".indexOf(element) == -1)
+ str = " (";
+ }
+ }
+ super.insertString(offset, str, a);
+ if (coloration_activee)
+ processChangedLines(offset, str.length());
+ }
+
+ /*
+ * Override to apply syntax highlighting after the document has been updated
+ */
+ public void remove(int offset, int length) throws BadLocationException {
+ /*
+ * if (getText(offset+length-1,1).equals(" ")){
+ * if(getLength()>offset+length) { String
+ * element=getText(offset+length,1); if (element.equals("[")) length--;
+ * } }
+ */
+ super.remove(offset, length);
+
+ if (coloration_activee)
+ processChangedLines(offset, 0);
+ }
+
+ /*
+ * Determine how many lines have been changed, then apply highlighting to
+ * each line
+ */
+ public void processChangedLines(int offset, int length)
+ throws BadLocationException {
+ String content = doc.getText(0, doc.getLength());
+
+ // The lines affected by the latest document update
+
+ int startLine = rootElement.getElementIndex(offset);
+ int endLine = rootElement.getElementIndex(offset + length);
+
+ // Do the actual highlighting
+
+ for (int i = startLine; i <= endLine; i++) {
+ applyHighlighting(content, i);
+ }
+ }
+
+ /*
+ * Parse the line to determine the appropriate highlighting
+ */
+ private void applyHighlighting(String content, int line)
+ throws BadLocationException {
+ int startOffset = rootElement.getElement(line).getStartOffset();
+ int endOffset = rootElement.getElement(line).getEndOffset() - 1;
+ int lineLength = endOffset - startOffset;
+ int contentLength = content.length();
+
+ if (endOffset > contentLength)
+ endOffset = contentLength - 1;
+
+ // set normal attributes for the line
+
+ doc.setCharacterAttributes(startOffset, lineLength, normal, true);
+
+ // check for single line comment
+ // On enlève les éventuels commentaires
+ int index = content.indexOf(getSingleLineDelimiter(), startOffset);
+ while (index != -1) {
+ if (index == 0) {
+ break;
+ } else if (!content.substring(index - 1, index).equals("\\")) {
+ break;
+ }
+ index = content.indexOf(getSingleLineDelimiter(), index + 1);
+ }
+
+ if ((index > -1) && (index < endOffset)) {
+ doc.setCharacterAttributes(index, endOffset - index + 1, comment,
+ false);
+ endOffset = index - 1;
+ }
+
+ // check for tokens
+ colore(content, startOffset, endOffset);
+ }
+
+ protected boolean isOperator(char character) {
+ String operands = "+-/*<=>&|";
+
+ if (operands.indexOf(character) != -1)
+ return true;
+ else
+ return false;
+ }
+
+ /*
+ * Override for other languages
+ */
+ protected boolean isKeyword(String token) {
+ token = token.toLowerCase();
+ return Primitive.primitives.containsKey(token);
+ }
+
+ protected String getSingleLineDelimiter() {
+ return "#";
+ }
+
+ public void Montre_Parenthese(int offset) {
+ doc.setCharacterAttributes(offset, 1, parenthese, false);
+ }
+
+ public void setColore_Parenthese(boolean b) {
+ colore_parenthese = b;
+ }
+
+ public void colore(String content, int startOffset, int endOffset) {
+ int debut = startOffset;
+ boolean nouveau_mot = true;
+ boolean backslash = false;
+ boolean mot = false;
+ boolean variable = false;
+ for (int i = startOffset; i < endOffset; i++) {
+ try { // Sometimes, Exception happens on next line
+ char c = content.charAt(i);
+ if (c == ' ' || c == '(' || c == ')' || c == '[' || c == ']') {
+ if (!backslash) {
+ String element = content.substring(debut, i);
+ if (mot) {
+ doc.setCharacterAttributes(debut, i - debut, quote,
+ false);
+ } else if (variable) {
+ doc.setCharacterAttributes(debut, i - debut, quote,
+ false);
+ } else if (isKeyword(element)) {
+ doc.setCharacterAttributes(debut, i - debut,
+ keyword, false);
+ } else
+ try {
+ Double.parseDouble(element);
+ doc.setCharacterAttributes(debut, i - debut,
+ quote, false);
+ } catch (NumberFormatException e) {
+ }
+ mot = false;
+ variable = false;
+ debut = i + 1;
+ nouveau_mot = true;
+ if (c != ' ') {
+ if (colore_parenthese)
+ doc.setCharacterAttributes(i, 1, parenthese,
+ false);
+ else {
+ doc.setCharacterAttributes(i, 1, normal, false);
+ // System.out.println(i+" normal "+StyleConstants.getFontFamily(normal)+" "+StyleConstants.isBold(normal));
+ }
+ }
+ }
+ backslash = false;
+ } else if (nouveau_mot) {
+ if (c == '\"') {
+ mot = true;
+ backslash = false;
+ nouveau_mot = false;
+ } else if (c == ':') {
+ variable = true;
+ backslash = false;
+ nouveau_mot = false;
+ } else if (isOperator(c)) {
+ backslash = false;
+ mot = false;
+ variable = false;
+ nouveau_mot = true;
+ debut = i + 1;
+ doc.setCharacterAttributes(i, 1, keyword, false);
+ } else
+ nouveau_mot = false;
+ } else if (c == '\\') {
+ if (!backslash) {
+ backslash = true;
+ } else
+ backslash = false;
+ nouveau_mot = false;
+ } else if (isOperator(c)) {
+ if (!mot) {
+ String element = content.substring(debut, i);
+ if (variable)
+ doc.setCharacterAttributes(debut, i - debut, quote,
+ false);
+ else if (isKeyword(element))
+ doc.setCharacterAttributes(debut, i - debut,
+ keyword, false);
+ else
+ try {
+ Double.parseDouble(element);
+ doc.setCharacterAttributes(debut, i - debut,
+ quote, false);
+ } catch (NumberFormatException e) {
+ }
+ backslash = false;
+ mot = false;
+ variable = false;
+ nouveau_mot = true;
+ debut = i + 1;
+ doc.setCharacterAttributes(i, 1, keyword, false);
+ }
+ }
+ } catch (StringIndexOutOfBoundsException e22) {
+ }
+ }
+ if (!nouveau_mot) {
+ String element = content.substring(debut, endOffset);
+ if (mot) {
+ doc.setCharacterAttributes(debut, endOffset - debut, quote,
+ false);
+ } else if (variable) {
+ doc.setCharacterAttributes(debut, endOffset - debut, quote,
+ false);
+ } else if (isKeyword(element)) {
+ doc.setCharacterAttributes(debut, endOffset - debut, keyword,
+ false);
+ } else
+ try {
+ Double.parseDouble(element);
+ doc.setCharacterAttributes(debut, endOffset - debut, quote,
+ false);
+ } catch (NumberFormatException e) {
+ }
+ }
+
+ }
+
+ public void insertStyleNormal(int offset, String str, AttributeSet a) {
+ try {
+ super.insertString(offset, str, a);
+ } catch (BadLocationException e) {
+ }
+ }
+}
diff --git a/logo/src/xlogo/StyledDocument/DocumentLogoCommande.java b/logo/src/xlogo/StyledDocument/DocumentLogoCommande.java
new file mode 100644
index 0000000..f0a9efa
--- /dev/null
+++ b/logo/src/xlogo/StyledDocument/DocumentLogoCommande.java
@@ -0,0 +1,62 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */package xlogo.StyledDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import java.util.*;
+import xlogo.Application;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+
+public class DocumentLogoCommande extends DocumentLogo {
+ private static final long serialVersionUID = 1L;
+ private Application cadre;
+ public DocumentLogoCommande(Application cadre) {
+ super();
+ this.cadre=cadre;
+ }
+ public void insertString(int offset, String str, AttributeSet a) throws BadLocationException
+ {
+ StringBuffer buffer=new StringBuffer();
+ StringTokenizer st=new StringTokenizer(str,"\n");
+ while(st.hasMoreTokens()){
+ buffer.append(st.nextToken());
+ }
+ if (str.equals("\n")){
+ cadre.commande_actionPerformed();
+
+ }
+ else {
+ str=new String(buffer);
+ super.insertString(offset, str, a);
+ }
+ }
+}
diff --git a/logo/src/xlogo/StyledDocument/DocumentLogoHistorique.java b/logo/src/xlogo/StyledDocument/DocumentLogoHistorique.java
new file mode 100644
index 0000000..7dc4e37
--- /dev/null
+++ b/logo/src/xlogo/StyledDocument/DocumentLogoHistorique.java
@@ -0,0 +1,170 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */package xlogo.StyledDocument;
+import java.awt.Color;
+import java.awt.Font;
+
+import javax.swing.text.*;
+
+import xlogo.gui.HistoryPanel;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+
+public class DocumentLogoHistorique extends DocumentLogo {
+ private static final long serialVersionUID = 1L;
+ private Color couleur_texte = Color.BLUE;
+ private int taille_texte = 12;
+ private String style="normal";
+ private MutableAttributeSet normal;
+ private MutableAttributeSet erreur;
+ private MutableAttributeSet commentaire;
+ private MutableAttributeSet perso;
+ private boolean tape=false;
+ public DocumentLogoHistorique() {
+ super();
+
+ Font font = WSManager.getWorkspaceConfig().getFont();
+
+ //Style normal
+ normal = new SimpleAttributeSet();
+ StyleConstants.setFontSize(normal, font.getSize());
+ StyleConstants.setFontFamily(normal, font.getName());
+
+ // Style pour l'écriture des erreurs
+ erreur = new SimpleAttributeSet();
+ StyleConstants.setForeground(erreur, Color.RED);
+ StyleConstants.setFontSize(erreur, font.getSize());
+ StyleConstants.setFontFamily(erreur, font.getName());
+
+ //Style pour les commentaires (Vous venez de définir ...)
+ commentaire = new SimpleAttributeSet();
+ StyleConstants.setForeground(commentaire, Color.BLUE);
+ StyleConstants.setFontSize(commentaire, font.getSize());
+ StyleConstants.setFontFamily(commentaire, font.getName());
+
+ // Style pour la primitive écris et la primitive tape
+ perso = new SimpleAttributeSet();
+ StyleConstants.setForeground(perso, Color.BLACK);
+ StyleConstants.setFontFamily(perso, font.getName());
+
+
+ }
+ public void setStyle(String sty){
+ style=sty;
+ }
+ public Color getCouleurtexte() {
+ return couleur_texte;
+ }
+
+ public int police() {
+ return taille_texte;
+ }
+ public void fixecouleur(Color color) {
+ couleur_texte = color;
+ StyleConstants.setForeground(perso, couleur_texte);
+ }
+ public void fixepolice(int taille) {
+ taille_texte = taille;
+ StyleConstants.setFontSize(perso, taille_texte);
+ }
+ public void fixenompolice(int id) {
+ StyleConstants.setFontFamily(perso,
+ GlobalConfig.fonts[HistoryPanel.fontPrint].getName());
+ }
+ public void fixegras(boolean b){
+ StyleConstants.setBold(perso,b);
+ }
+ public void fixeitalique(boolean b){
+ StyleConstants.setItalic(perso,b);
+ }
+ public void fixesouligne(boolean b){
+ StyleConstants.setUnderline(perso,b);
+ }
+ public void fixeexposant(boolean b){
+ StyleConstants.setSuperscript(perso,b);
+ }
+ public void fixeindice(boolean b){
+ StyleConstants.setSubscript(perso,b);
+ }
+ public void fixebarre(boolean b){
+ StyleConstants.setStrikeThrough(perso,b);
+ }
+ public boolean estgras(){
+ return StyleConstants.isBold(perso);
+ }
+ public boolean estitalique(){
+ return StyleConstants.isItalic(perso);
+ }
+ public boolean estsouligne(){
+ return StyleConstants.isUnderline(perso);
+ }
+ public boolean estexposant(){
+ return StyleConstants.isSuperscript(perso);
+ }
+ public boolean estindice(){
+ return StyleConstants.isSubscript(perso);
+ }
+ public boolean estbarre(){
+ return StyleConstants.isStrikeThrough(perso);
+ }
+ public Font getFont(){
+ return GlobalConfig.fonts[HistoryPanel.fontPrint].deriveFont(Font.BOLD,(float)taille_texte);
+
+ }
+ public void change_police_interface(Font font, int taille) {
+
+ String famille = font.getName();
+
+ StyleConstants.setFontSize(normal, taille);
+ StyleConstants.setFontFamily(normal, famille);
+
+ StyleConstants.setFontSize(commentaire, taille);
+ StyleConstants.setFontFamily(commentaire, famille);
+
+ StyleConstants.setFontSize(erreur, taille);
+ StyleConstants.setFontFamily(erreur, famille);
+ }
+ public void insertString(int offset, String str, AttributeSet a) throws BadLocationException
+ {
+ if (tape) {
+ tape=false;
+ super.insertStyleNormal(offset,str,a);
+ }
+ else super.insertString( offset, str, a);
+ if (style.equals("erreur")) this.setCharacterAttributes(offset,str.length(),erreur,true);
+ else if (style.equals("commentaire")) this.setCharacterAttributes(offset,str.length(),commentaire,true);
+ else if (style.equals("perso")) this.setCharacterAttributes(offset,str.length(),perso,true);
+ if (!str.endsWith("\n")) tape=true;
+ }
+
+} \ No newline at end of file
diff --git a/logo/src/xlogo/gpl/gpl-ar.html b/logo/src/xlogo/gpl/gpl-ar.html
new file mode 100644
index 0000000..10ee645
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-ar.html
@@ -0,0 +1,376 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ar"
+ dir="rtl" lang="ar">
+<head>
+ <title>الرخصة المشاعة العامة لجنيو</title>
+ <link rev="made" href="mailto:[email protected]" />
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=UTF-8" />
+ <meta name="author" content="Khaled Shagrouni" />
+ <meta name="description"
+ content="الترجمة العربية للرخصة المشاعة العامة لجنيو" />
+ <meta name="keywords"
+ content="رخصة، ترخيص، اتفاقية، استخدام، ملكية، برمجيات" />
+ <meta content="Software Devolepment"
+ name="document-classification" />
+ <link href="gpl.ar_fichiers/ni.css" type="text/css"
+ rel="stylesheet" />
+</head>
+<body>
+<table style="width: 801px; height: 92.38%;" border="0"
+ cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr>
+ <td style="height: 200px;" colspan="2"
+ valign="top"> <a
+ href="http://www.nidam.net/sd/index.html"><br />
+ </a>
+ <table id="TABLE1" style="width: 100%;"
+ border="0" cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr bgcolor="#ffcc99">
+ <td style="height: 2px;"> </td>
+ </tr>
+ <tr>
+ <td style="height: 4px;"> </td>
+ </tr>
+ <tr>
+ <td> &nbsp; </td>
+ </tr>
+ </tbody>
+ </table>
+ <table id="TABLE2" style="width: 100%; height: 93.03%;"
+ border="0" cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr>
+ <td style="width: 20px;" colspan="1"> </td>
+ <td
+ style="width: 493px; background-color: rgb(255, 255, 255);"
+ colspan="1" valign="top">
+ <p> <em>هذه ترجمة غير رسمية من الرخصة المشاعة
+العامة ل جنيو إلى العربية. لم يتم إصدارها من قبل مؤسسة البرمجيات الحرة،
+و لا تعبر قانونيا عن أحكام التوزيع للبرمجيات التي تستخدم الرخصة المشاعة
+العامة لجنيو - فقط النصّ الإنكليزي الأصلي للرخصة المشاعة العامة لجنيو
+تفعل هذا. إلا أننا نأمل أن هذه الترجمة سوف تعين متكلمي العربية على فهم
+أفضل للرخصة المشاعة العامة لجنيو.</em> </p>
+ <div dir="ltr">
+ <p class="PTitle"> <em>This is an
+unofficial translation of the GNU General Public License into Arabic.
+It was not published by the Free Software Foundation, and does not
+legally state the distribution terms for software that uses the GNU
+GPL--only the original English text of the GNU GPL does that. However,
+we hope that this translation will help Arabic speakers understand the
+GNU GPL better.</em><br />
+ </p>
+ </div>
+ <img alt="" src="gpl.ar_fichiers/greydot.gif"
+ height="1" width="375" /><!-- Srart-->
+ <h3>جدول المحتويات </h3>
+ <ul>
+ <li> <a href="#SEC1" name="TOC1">الرخصة
+المشاعة العامة ل ج ن يو</a>
+ <ul>
+ <li> <a href="#SEC2" name="TOC2">تمهيد</a>
+ </li>
+ <li> <a href="#SEC3" name="TOC3">أحكام
+و شروط النسخ و التوزيع و التحوير</a> </li>
+ <li> <a href="#SEC4" name="TOC4">كيف
+تطبق هذه الأحكام على برامجك الجديدة</a> </li>
+ </ul>
+ </li>
+ </ul>
+ <h3><a href="#TOC1" name="SEC1">الرخصة
+المشاعة العامة ل ج ن يو</a> </h3>
+ <p> نسخة 2، يونيو 1991 </p>
+ <pre>حقوق النسخ محفوظة (C) 1989، 1991 مؤسسة البرمجيات الحرة المساهمة<br />
+51 شارع فرانكلين، الطابق الخامس، بوسطن، م أ 02110-1301،<br />
+الولايات المتحدة الأمريكية<br />
+لكل فرد الحق في نسخ و توزيع نُسخ حرفية لوثيقة الرخصة هذه،و لكن تغييرها<br />
+غبر مسموح به.</pre>
+ <h3><a href="#TOC2" name="SEC2">تمهيد</a>
+ </h3>
+ <p> الرُخص لمعظم البرمجيات مصممة لكي تنزع عنك حرية
+تقاسمها و تغييرها. بالمقابل، فإن الرخصة المشاعة العامة ل ج ن يو أعدت
+لضمان حريتك في تقاسم وتغيير البرمجيات الحرة - لتأكيد أن البرمجية حرة
+ومجانية لكل مستخدميها. الرخصة المشاعة العامة تنطبق على معظم برمجيات
+مؤسسة البرمجيات الحرة و على كل برنامج آخر التزم مؤلفوه باستخدامها. (بعض
+برامج مؤسسة البرامج الحرة الأخرى محمية بالرخصة المشاعة العامة المخففة.)
+يمكنك تطبيقها على برامجك أيضا. </p>
+ <p> عندما نتكلم عن البرمجيات الحرة، فنحن نشير إلى
+الحرية وليس الثمن. رخصتنا المشاعة العامة قد صممت لتأكيد أن لديك الحرية
+لتوزيع نسخ من البرمجيات الحرّة (و تطلب مقابلا لخدماتك إذا رغبت)، وأنك
+قد استلمت التوليف المصدري أو انك تستطيع الحصول عليه إن أردت، أنه
+بإمكانك تغيير البرمجية أو أن تستخدم أجزاء منها في برامج حرة جديدة؛ وأنك
+تعلم أنه بإمكانك القيام بهذه الأمور. </p>
+ <p> من أجل حماية حقوقك، نحتاج لوضع قيود تمنع أي أحد
+من حجبك هذه الحقوق أو أن يطلب منك التنازل عن الحقوق. هذه القيود تُترجم
+إلى مسؤوليات محددة تقع عليك إذا ما قمت بتوزيع نسخ من البرمجية، أو إذا
+قمت بتحويرها. </p>
+ <p> مثال ذلك، إذا قمت بتوزيع نسخ من برنامج كهذه،
+مجانا كان أو بمقابل، يجب أن تعطى المستلمين كل الحقوق التي لديك. يجب
+عليك التأكد من أنهم أيضا يستلمون أو بإمكانهم الحصول على التوليف
+المصدري. و يجب أن تريهم هذه الأحكام حتى يعلموا حقوقهم. </p>
+ <p> نحن نحمي حقوقك على خطوتين: (1) إسباغ حق النشر
+والطبع على البرمجية، و (2) منحك هذه الرخصة التي تعطيك إذنا قانونيا لنسخ
+و توزيع و/أو تحوير البرمجية. &nbsp; </p>
+ <p> أيضا، ومن أجل حماية المؤلف وحمايتنا، نريد أن يكون
+واضحا بأن كل شخص يدرك بأنه لا توجد ضمانة لهذه البرمجية الحرة. إذا تم
+تحوير البرمجية من قبل شخص آخر ومرّرها، نريد من مستلميها أن يعلموا بأن
+ما لديهم ليست الأصلية، وبذلك فإن أية مشاكل يسببها آخرون سوف لن تنعكس
+على سمعة المؤلفين الأصليين. </p>
+ <p> أخيرا، أي برنامج مجاني مهدد دائما من قبل براءات
+اختراع البرمجيات. نحن نرجو أن نتجنب خطورة أن معيدي توزيع البرامج الحرة
+سيتحصلون شخصيا على رخص لبراءات اختراع، ينتج عنها جعل البرنامج ملكية
+مقيدة. لمنع هذا، جعلنا الأمر واضحا بأن كل براءة اختراع يجب أن تُرخّص
+للاستخدام الحر لكل شخص أو أن لا ترخص بتاتا. </p>
+ <p> تفصيل أحكام وشروط النسخ و التوزيع و التحوير كما
+يلي: </p>
+ <h3><a href="#TOC3" name="SEC3">نصوص
+و شروط النسخ و التوزيع و التحوير</a> </h3>
+ <p> <strong>0.</strong> هذه الرخصة تسري
+على أي برنامج أو عمل آخر يحتوي على إشعار يضعه حامل حقوق الطبع قائلا فيه
+بأنه بالإمكان توزيعه تحت أحكام الرخصة المشاعة العامة هذه. كلمة
+"برنامج"، أدناه، تشير إلى برنامج أو عمل، و "عمل مبني على برنامج" يعني
+إما البرنامج أو أي عمل مشتق منه ويكون تحت طائلة قانون حقوق الطبع: أي
+أنه، عمل يحتوي على برنامج أو قسم منه، سواء كان حرفيا أو محوّرا و/أو
+مترجما إلى لغة أخرى. (من هنا فصاعدا، الترجمة متضمنة وبدون حدود في
+المصطلح "تحوير".) كل مرّخص له/لها يشار إليه بـ "أنت". </p>
+ <p> النشاطات الأخرى غير النسخ و التوزيع و التحوير لا
+تغطيها هذه الرخصة؛ وخارج نطاقها. فعل تشغيل البرنامج غير مقيد، ومخرجات
+البرنامج مغطّاة فقط إذا كانت محتوياتها تشكل عملا مبنيا على البرنامج
+(مستقلا أو قد تم صنعه بتشغيل البرنامج). حقيقة هذا تعتمد على ما يقوم به
+البرنامج. </p>
+ <p> <strong>1.</strong> أنت بإمكانك نسخ
+و توزيع نسخا حرفية من التوليف الأصلي للبرنامج كما استلمته. في أي وسيط،
+شرط أن تنشر بوضوح وعناية على كل نسخة بشكل ملائم إشعار حقوق الطبع
+والتنصل من الضمان؛ وأن تبقيها سليمة كل الإشعارات التي تشير لهذه الرخصة
+و تشير لانتفاء أي ضمان. و أن تُعطي أي مستلم آخر للبرنامج نسخة من هذه
+الرخصة رفق البرنامج. </p>
+ <p> أنت بإمكانك أن تتقاضى أجرا مقابل الفعل المادي
+لنقل النسخة، و أنت بإمكانك حسب اختيارك أن تُعطي تعهدا بالضمان مقابل
+أجر. </p>
+ <p> <strong>2.</strong> أنت بإمكانك
+تحوير نسختك أو نسخك من البرنامج أو أي جزء منه، فيكون عملا مبينا على
+البرنامج، و تنسخ و توزع هذه التحويرات أو العمل تحت أحكام القسم 1 أعلاه،
+بشرط أنك أيضا تلبي كل الشروط أدناه: </p>
+ <dl>
+ <dd> <strong>أ)</strong> يجب أن تجعل
+من الملفات المحوّرة تحمل إشعارات صريحة تفيد بأنك قد غيّرت الملفات و
+التاريخ لأي تغيير. </dd>
+ <dd> <strong>ب)</strong> يجب أن تجعل
+من أي عمل تقوم بتوزيعه أو نشره، الذي بكامله أو في قسم منه يحوي أو مشتق
+من البرنامج أو أي قسم منه، يكون مرخّصا ككل بدون مقابل لكل الأطراف
+الثالثة تحت أحكام هذه الرخصة. </dd>
+ <dd> <strong>ج)</strong> إذا كان
+البرنامج المحوّر يقرأ الأوامر عادة بصورة تفاعلية عند تشغيله، يجب أن
+تجعله، عند بدئه التشغيل لمثل هذا الاستعمال التفاعلي و بأكثر الطرق
+اعتيادية، كي يطبع أو يُظهر إشعار حقوق الطبع وإشعار أنه لا توجد ضمانة
+(أو أخرى، تفيد بأنك تُعطي ضمانا) و أن المستخدمين يمكنهم إعادة توزيع
+البرنامج تحت هذه الشروط، وأن ترشد المستخدم لكيفية الاطلاع على نسخة من
+هذه الرخصة. (استثناء: إذا كان البرنامج ذاته تفاعليا و لكن لا يقوم عادة
+بطبع إعلان كهذا، عملك المبني على البرنامج لا يتطلب طباعة إعلان.) </dd>
+ </dl>
+ <p> هذه المقتضيات تسري على العمل المحوّر ككل. إذا
+وجدت أقسام بعينها من العمل ليست مشتقة من البرنامج، و يمكن منطقيا
+اعتبارها مستقلة وعملا منفصل في ذاتها، فإن هذه الرخصة، و أحكامها، لا
+تسري على تلك الأقسام عند توزيعك لها كأعمال منفصلة. لكن عند توزيعك لنفس
+الأقسام كجزء من كل والتي تكون عملا مبنيا على البرنامج، فإن توزيع الكل
+يجب أن يكون وفق أحكام هذه الرخصة، حيث السماح للمرخصين لهم الآخرين يمتد
+لكامل المجمل، و بالتالي لكل جزء بعينه بغض النظر عن من كتبه. </p>
+ <p> بالتالي، ليس من نية هذا القسم إدعاء حقوق أو
+منازعة حقوقك في عمل كُتِب بالكامل من قبلك؛ ولكن، النية هي في ممارسة
+الحق في السيطرة على توزيع أعمال مشتقة أو مجمعة بالتأسيس على البرنامج. </p>
+ <p> بالإضافة، مجرّد الضمّ &nbsp;لعمل آخر ليس
+مؤسسا على البرنامج مع البرنامج (أو مع عمل مؤسس على البرنامج) في حيز
+التخزين أو التوزيع لا يدخل العمل الآخر في نطاق هذه الرخصة. </p>
+ <p> <strong>3.</strong> أنت بإمكانك نسخ
+و توزيع البرنامج (أو عمل مؤسس عليه، تحت القسم 2) كتوليف كائني أو في
+صيغة تنفيذية تحت أحكام الأقسام 1 و 2 أعلاه بشرط أنك أيضا تقوم بواحدة
+مما يلي: </p>
+ <dl>
+ <dd> <strong>أ)</strong> إرفاقه بما
+يناظره كاملا من توليف مصدري مقروء_بالآلة، و الذي يجب أن يُوزّع تحت
+أحكام القسم 1 و 2 أعلاه في وسط مُستخدم عادة لتبادل البرمجيات؛ أو، </dd>
+ <dd> <strong>ب)</strong> إرفاقه مع عرض
+مكتوب، صالح لثلاثة سنوات على الأقل، لإعطاء أي طرف ثالث، بمقابل لا يزيد
+عن تكلفتك للأداء المادي لتوزيع المصدر، نسخة مقروءة_بالآلة كاملة من ما
+يناظره من توليف مصدري، لتوزيعها تحت أحكام الأقسام 1 و 2 أعلاه في وسط
+مُستخدم عادة لتبادل البرمجيات؛ أو، </dd>
+ <dd> <strong>ج)</strong> إرفاقه مع
+المعلومات التي تسلّمتها كعرض من أجل توزيع ما يناظره من التوليف المصدري.
+(هذا البديل مسموح به فقط للتوزيع غير التجاري و فقط إذا استلمت البرنامج
+كتوليف كائني أو كصيغة تنفيذية مع العرض، طبقا للقسم الفرعي ب أعلاه.) </dd>
+ </dl>
+ <p> يقصد بالتوليف المصدري للعمل الشكل المفضل للعمل
+لإجراء التحويرات فيه. بالنسبة للعمل التنفيذي، يُقصد بالتوليف المصدري
+الكامل كل التوليف المصدري لكافة القوالب التي يحويها، إضافة لأية ملفات
+مصاحبة لتعريف الواجهات، إضافة لنصوص التعليمات المستخدمة للتحكم في ترجمة
+و تثبيت التنفيذيات. عموما، و كاستثناء خاص، التوليف المصدري الموزع لا
+يحتاج أن يضمن أي شيء يكون عادة موزعا (في صيغة مصدرية أو ثنائية) مع
+المكونات الرئيسية (مخرّج"محول"، نواة، و ما إلى ذلك) لنظام التشغيل الذي
+يعمل التنفيذي فيه، ما لم تكن المكونات نفسها مرافقة للتنفيذي. </p>
+ <p> إذا كان توزيع التنفيذي أو التوليف الكائني
+قد&nbsp;أتيح بتقديم مدخل للنسخ من مكان محدد، عندها فإن تقديم مدخل
+مكافئ لنسخ التوليف المصدري من نفس المكان يتم احتسابه كتوزيع للتوليف
+المصدري، حتى لو لم تشدّد أطراف ثالثة على نسخ المصدر مع التوليف الكائني.
+ </p>
+ <p> <strong>4.</strong> أنت لا يمكنك أن
+تنسخ أو تحوّر أو ترخّص مفرعا أو توزع البرنامج إلا بالكيفية التي عُبر
+عنها بجلاء تحت هذه الرخصة. أية محاولة أخرى لنسخ أو لتحوير أو لترخيص
+مفرع أو لتوزيع البرنامج يُعد باطلا، و يُنهي آليا حقوقك تحت هذه الرخصة.
+إلا أن الأطراف الذين استلموا نُسخا، أو حقوقا منك وفق هذه الرخصة سوف لن
+تكون رخصهم منتهية طالما بقوا على التزام كامل. </p>
+ <p> <strong>5.</strong> &nbsp;أنت
+لست مطالبا بالقبول بهذه الرخصة، ما دمت لم توقع عليها. ومع ذلك، لاشيء
+آخر يضمن لك الإذن بتحوير أو توزيع البرنامج أو الأعمال المشتقة منه.
+&nbsp;هذه الأفعال ممنوعة بالقانون إن لم تقبل بهذه الرخصة. لهذا،
+بتحوير أو توزيع هذا البرنامج (أو أي عمل مؤسس على البرنامج)، فأنت تدل
+على قبولك لهذه الرخصة بهذا الفعل، وبكل نصوصها و شروطها لنسخ أو توزيع أو
+تحوير البرنامج أو الأعمال المؤسسة عليه. </p>
+ <p> <strong>6.</strong> كل مرّة تقوم
+فيها بإعادة توزيع البرنامج (أو أي عمل مؤسس على البرنامج)، يتسلّم
+المتلقون آليا رخصة من المرخّص الأصلي لنسخ أو توزيع أو تحوير البرنامج
+خاضعة لهذه النصوص و الشروط. أنت لا يمكنك فرض قيود أخرى على ممارسة
+المتلقين لحقوقهم المصانة هنا. أنت لست مسؤولا على إجبار أطراف ثالثة
+بالالتزام بهذه الرخصة. </p>
+ <p> <strong>7.</strong> في حالة، كنتيجة
+لحكم قضائي أو دعوى لخرق براءة اختراع أو لأي سبب آخر (ليس محصورا في
+قضايا براءات الاختراع)، ظروف قد فُرضت عليك (سواء بحكم محكمة أو باتفاقية
+أو بغيرها) من شأنها أن تعارض شروط هذه الرخصة، فإنها لا تعفيك من شروط
+هذه الرخصة. إذا لم تستطع التوزيع بكيفية تلبي معا التزاماتك تحت هذه
+الرخصة و أية التزامات أخرى ذات علاقة، عندها و نتيجة لذلك لا يمكنك توزيع
+البرنامج مطلقا. مثال ذلك، إذا رخصة براءة الاختراع لن تسمح بإعادة توزيع
+مجانية للبرنامج من قبل كل أولائك الذين استلموا نسخا بصورة مباشرة أو غير
+مباشرة من خلالك، فإن الوسيلة الوحيدة لتلبيتك لها و لهذه الرخصة ستكون
+بالتغاضي كليا عن توزيع البرنامج. </p>
+ <p> في حالة أن جزءا من هذا القسم لا يسري أو غير قابل
+للفرض تحت ظرف معين، فإن باقي القسم سوف يسري و القسم ككل سوف يسري على
+باقي الظروف. </p>
+ <p> ليس الغرض من هذا القسم دفعك لمخالفة أية براءة
+اختراع أو دعاوى حقوق ملكية أخرى أو لمنازعة صلاحية أي من هذه دعاوى؛ هذا
+القسم له غرض وحيد لحماية استقامة نظام توزيع البرمجيات الحرة، والتي نفذت
+عبر ممارسات الترخيص المشاع. العديد من الناس قدموا إسهامات سخية لمدى
+واسع من برمجيات وُزّعت عبر هذا النظام بالاعتماد على ثبات تطبيق هذا
+النظام؛ إنه يرجع للمؤلف/المانح ليقرر ما إذا كان أو كانت راغب في توزيع
+البرمجية عبر نظام آخر و أن المرخّص له لا يمكنه فرض/اذعان هذا الاختيار. </p>
+ <p> هذا القسم قُصد به جعله واضحا كلية ما يعتقد أنه
+سيكون عواقب بقية هذه الرخصة. </p>
+ <p> <strong>8.</strong> إذا توزيع و/أو
+استخدام البرنامج مقيد في بلدان معينة إما ببراءات اختراع أو بتداخلات ذات
+حقوق تأليف، فإن حامل حقوق التأليف الأصلي الذي وضع البرنامج تحت هذه
+الرخصة قد يضيف حدود توزيع جغرافية صريحة تستثني تلك البلدان، فيكون ذاك
+التوزيع مسموحا فقط في أو عبر البلدان غير المستثناة. في مثل هذه الحالة،
+هذه الرخصة تتضمن التقيبد كما لو أنها كتبت في متن هذه الرخصة. </p>
+ <p> <strong>9.</strong> مؤسسة البرمجيات
+الحرة قد تنشر إصدارات معدّلة و/أو جديدة من الرخصة المشاعة العامة من وقت
+لآخر. مثل هذه الإصدارات الجديدة ستكون شبيهة في روحها للإصدارة الحالية،
+لكن قد تختلف في تفاصيل تتناول مسائل أو اهتمامات جديدة. </p>
+ <p> كلّ إصدارة تُعطى رقم إصدار مميّز. إذا حدّد
+البرنامج رقم إصدارة لهذه الرخصة يتبعها و يتبع "أية إصدارة لاحقة"، يكون
+لك الخيار بأن تتبع نصوص و شروط إما تلك الإصدارة أو أية إصدارة لاحقة
+تُنشر من قبل مؤسسة البرمجيات الحرة. إذا البرنامج لم يحدد رقم إصدارة
+لهذه الرخصة، يمكنك اختيار أية إصدارة تم نشرها من قبل مؤسسة البرمجيات
+الحرة. </p>
+ <p> <strong>10.</strong> إذا أنت رغبت في
+تضمين أجزاء من البرنامج في برامج حرة أخرى ذات شروط توزيع مختلفة، خاطب
+المؤلف لالتماس الإذن. بالنسبة للبرامج التي ترجع حقوق تأليفها لمؤسسة
+البرامج الحرة، كاتب مؤسسة البرامج الحرة؛ نحن أحيانا نضع استثناءات لهذه
+الحالة. قرارنا سيكون محكوما بالغايتين الحفاظ على حالة الحرية لكل تفرعات
+برامجنا الحرة &nbsp;و الدعوة لمشاركة و إعادة استخدام البرمجيات بصفة
+عامة. </p>
+ <p> <strong>بدون ضمان</strong> </p>
+ <p> <strong>11.</strong> لأن البرنامج
+مرخص بدون مقابل، لا يوجد ضمان على البرنامج، للمدى المسموح به من قبل
+القانون الساري. باستثناء أن تكون حالات تبين ذلك كتابة فأن حاملي حقوق
+التأليف و/أو الأطراف الأخرى يعرضون البرنامج "كما هو" بدون ضمان من أي
+نوع، إما صراحة أو ضمنا، بما في ذلك، ولكن ليس محدودا به، الضمانات
+المنطوية الخاصة بالصلاحية السوقية والموائمة لغاية معينة. المخاطرة
+بأكملها فيما يخصّ جودة و أداء البرنامج تقع عليك. إذا أثبت البرنامج
+قصورا، تتحمل أنت تكلفة كافة ما يلزم من صيانة أو إصلاح أو تقويم. </p>
+ <p> <strong>12.</strong> ليس تحت أي ظرف
+ما لم يتطلبه قانون نافذ أو ما أتفق عليه كتابة سيكون أي مالك لحق
+التأليف، أو أي طرف آخر قد حور و/أو أعاد توزيع البرنامج كما سُوغ أعلاه،
+مسؤولا &nbsp;أمامك عن أضرار، بما في ذلك أضرار عامة أو خاصة أو عارضة
+أو مستتبعة تنشأ عن استخدام أو عدم قدرة على استخدام البرنامج ( بما في
+ذلك و لكن ليس محدودا به فقدان بيانات أو بيانات تم تسويتها بطريقة خاطئة
+&nbsp;أو خسائر تتكبدها أنت أو أطراف ثالثة أو فشل البرنامج في أن
+يعمل مع أي برامج أخرى)، حتى لو أن المالك أو أطراف ثالثة قد تم إعلامهم
+باحتمال مثل هذه الأضرار. </p>
+ <h3>نهاية النصوص و الشروط </h3>
+ <h3><a href="#TOC4" name="SEC4">كيف
+تطبق هذه الأحكام على برامجك الجديدة</a> </h3>
+ <p> إذا كنت تطوّر برنامجا جديدا، و تريد له أن يحظى
+بأكبر استخدام ممكن له للعموم، فإن أفضل طريقة لتحقيق ذلك هي أن تجعله
+برنامجا حرّا حيث يمكن لكل واحد أن يعيد توزيعه و يغيّره وفق هذه الأحكام.
+ </p>
+ <p> لتحقيق ذلك، قم بإرفاق الإشعارات التالية
+بالبرنامج. من الأضمن إرفاقها مع بداية كل ملف مصدري بالطريقة الأكثر
+فعالية لتبيان استبعاد الضمان؛ وكل ملف يجب أن يملك على الأقل سطر "حقوق
+الطبع" و إشارة لأين يوجد كامل الإشهار. </p>
+ <pre><var>سطر واحد لتقديم إسم البرنامج و فكرة عن عمله.</var>
+<br />
+حقوق الطبع (C) <var>السنة</var> <var>إسم المؤلف</var>
+<br />
+هذا البرنامج هو يرمجية حرّة، بإمكانك إعادة توزيعه و/أو تحويره وفق أحكام<br />
+الرخصة المشاعة العامة لجنيو كما أصدرتها مؤسسة البرمجيات الحرة؛إما الإصدارة 2<br />
+من الرخصة، أو (حسب خيارك) أية إصدارة لاحقة. هذا البرنامج يوزع بأمل أن<br />
+يكون نافعا، لكن بدون أية ضمانة، بدون حتى ضمانة<br />
+ضمنية للصلاحية السوقية أو الملائمة لغرض معين. راجع الرخصة المشاعة العامة<br />
+لجنيو لمزيد من التفاصيل.<br />
+من المفترض أن تستلم نسخة من الرخصة المشاعة العامة لجنيو مع هذا البرنامج؛<br />
+إذا لا، كاتب مؤسسة البرمجيات الحرة، 51 شارع فرانكلين، الطابق الخامس،<br />
+بوسطن، م أ 02110-1301، الولايات المتحدة الأمريكية.</pre>
+ <p> أضف أيضا معلومات عن كيفية الاتصال بك عبر بريدك
+الإلكتروني أو الورقي. </p>
+ <p> إذا كان البرنامج تفاعليا، إجعله يخرج إشعارا قصيرا
+مثل هذا حين يبدأ بصورة تفاعلية: </p>
+ <pre>جينوموفيجن إصدارة 69، حقوق الطبع (C) السنة اسم المؤلف<br />
+جينوموفيجن يأتي قطعيا بلا ضمانة؛ للتفاصيل اطبع 'عرض ض'. هذه برمجية حرة،<br />
+أنت مرحب بك لإعادة توزيعه وفق شروط محددة؛ اطبع 'عرض ش' للتفاصيل.</pre>
+ <p> الأوامر الافتراضية <samp> 'عرض ض'</samp>
+و <samp> 'عرض ش'</samp> يجب أن تظهر ما يتناسب من أجزاء من
+الرخصة المشاعة العامة. بالطبع، الأوامر التي تستخدمها قد تسمى بشيء آخر
+غير <samp> 'عرض ض'</samp> و <samp> 'عرض ش'</samp>؛
+يمكن أن تكون حتى نقرات فأرة أو بنود قائمة -- أيا كانت ما يناسب برنامجك.
+ </p>
+ <p> يجب عليك أيضا أن تجعل رب عملك (إذا كنت تعمل
+كمبرمج) أو المدرسة، إذا وجدت، يوقعان "تنازلا عن حقوق الطبع" للبرنامج،
+إذا دعت الضرورة. </p>
+ <p> ها هنا مثال؛ استبدل الأسماء: </p>
+ <pre>مؤسسة يويوداين المحدودة، بهذا تتنازل عن كل<br />
+مصالح حقوق الطبع في برنامج 'جينوموفيجن'<br />
+(الذي يصنع الاجتيازات عند المجمعات)<br />
+توقيع وع نون، 1 أبريل 1998<br />
+وع نون، رئيس النائب</pre>
+ <p> الرخصة المشاعة هذه لا تسمح بتضمين برنامجك في
+البرامج الممتلكة. إذا كان برنامجك مكتبة إجرائية، قد تراه أكثر نفعا أن
+تسمح بربط التطبيقات الممتلكة بالمكتبة. إذا كان هذا ما تريده، استخدم
+الرخصة المشاعة العامة المخففة لجنيو بدلا عن هذه الرخصة </p>
+ <hr /> </td>
+ <td width="10"> </td>
+ <td colspan="1" valign="top"> <br />
+ <br />
+&nbsp; <br />
+ <br />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td style="height: 200px;" colspan="2" dir="rtl">
+ <table style="width: 100%; height: 6.14%;" border="0"
+ cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr>
+ <td style="height: 10px;"> </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+</table>
+</body>
+</html>
diff --git a/logo/src/xlogo/gpl/gpl-de.html b/logo/src/xlogo/gpl/gpl-de.html
new file mode 100644
index 0000000..20f33e2
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-de.html
@@ -0,0 +1,767 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+ <title>GNU General Public License</title>
+</head>
+<body>
+<div class="main">
+<h1 class="dropshadow">
+<div>GNU General Public License
+</div>
+</h1>
+<p><strong>
+Deutsche &Uuml;bersetzung der Version 2, Juni 1991
+</strong></p>
+<p><em>
+Den offiziellen englischen Originaltext finden Sie unter
+<a href="gpl-en.html">http://www.gnu.org/licenses/gpl.html</a>.
+</em></p>
+<blockquote>
+ <p>Diese &Uuml;bersetzung wurde urspr&uuml;nglich
+erstellt von Katja Lachmann
+&Uuml;bersetzungen im Auftrag der S.u.S.E.&nbsp;GmbH
+&ndash; <tt>http://www.suse.de</tt>. Sie wurde
+&uuml;berarbeitet von Peter Gerwinski, G-N-U
+GmbH &ndash; <tt>http://www.g-n-u.de</tt>
+(31.&nbsp;Oktober 1996,
+4.&nbsp;Juni 2000). </p>
+ <p>Diese &Uuml;bersetzung wird mit der Absicht angeboten,
+das Verst&auml;ndnis
+der <em>GNU General Public License</em> (GNU GPL) zu
+erleichtern.
+Es handelt sich jedoch nicht um eine offizielle oder im
+rechtlichen Sinne anerkannte &Uuml;bersetzung. </p>
+ <p>Die <em>Free Software Foundation</em> (FSF) ist
+nicht der
+Herausgeber dieser &Uuml;bersetzung, und sie hat diese
+&Uuml;bersetzung
+auch nicht als rechtskr&auml;ftigen Ersatz f&uuml;r die
+Original-GNU-GPL
+anerkannt. Da die &Uuml;bersetzung nicht sorgf&auml;ltig von
+Anw&auml;lten
+&uuml;berpr&uuml;ft wurde, k&ouml;nnen die
+&Uuml;bersetzer nicht garantieren, da&szlig; die
+&Uuml;bersetzung die rechtlichen Aussagen der GNU GPL exakt
+wiedergibt. Wenn Sie sichergehen wollen, da&szlig; von Ihnen
+geplante
+Aktivit&auml;ten im Sinne der GNU GPL gestattet sind, halten Sie
+sich
+bitte an die <a href="gpl-en.html">englischsprachige
+Originalversion</a>. </p>
+ <p>Die &Uuml;bersetzer und die <em>Free Software
+Foundation</em> m&ouml;chten
+Sie darum bitten, diese &Uuml;bersetzung nicht als offizielle
+Lizenzbedingungen f&uuml;r von Ihnen geschriebene Programme zu
+verwenden. Bitte benutzen Sie hierf&uuml;r stattdessen die von der <em>Free
+Software Foundation</em> herausgegebene <a href="gpl-en.html">englischsprachige
+Originalversion</a>. </p>
+ <p><em>This is a translation of the GNU General Public
+License into
+German. This translation is distributed in the hope that it will
+facilitate understanding, but it is not an official or legally
+approved translation. </em></p>
+ <p><em>The Free Software Foundation is not the publisher
+of this
+translation and has not approved it as a legal substitute for the
+authentic GNU General Public License. The translation has not
+been reviewed carefully by lawyers, and therefore the translator
+cannot be sure that it exactly represents the legal meaning of
+the GNU General Public License. If you wish to be sure whether
+your planned activities are permitted by the GNU General Public
+License, please refer to the <a href="gpl-en.html">authentic
+English version</a>. </em></p>
+ <p><em>The translators and the Free Software Foundation
+strongly urge
+you not to use this translation as the official distribution
+terms for your programs; instead, please use the <a
+ href="gpl-en.html">authentic English version</a>
+published by the Free Software Foundation. </em></p>
+</blockquote>
+<h1 align="center">GNU General Public License</h1>
+<p align="center"><strong>
+Deutsche &Uuml;bersetzung der Version 2, Juni 1991
+</strong></p>
+<p align="center"><strong>
+Copyright &copy; 1989, 1991 Free Software Foundation, Inc.<br>
+51 Franklin St, Fifth Floor, Boston, MA 02110, USA
+</strong></p>
+<p>
+Es ist jedermann gestattet, diese Lizenzurkunde zu
+vervielf&auml;ltigen
+und unver&auml;nderte Kopien zu verbreiten; &Auml;nderungen
+sind jedoch
+nicht erlaubt.
+</p>
+<p><strong>
+Diese &Uuml;bersetzung ist kein rechtskr&auml;ftiger Ersatz
+f&uuml;r die
+<a href="gpl-en.html">englischsprachige
+Originalversion</a>!
+</strong></p>
+<h2>Vorwort</h2>
+<p>
+Die meisten Softwarelizenzen sind daraufhin entworfen worden,
+Ihnen die Freiheit zu nehmen, die Software weiterzugeben und zu
+ver&auml;ndern. Im Gegensatz dazu soll Ihnen die <em>GNU
+General Public
+License</em>, die Allgemeine &Ouml;ffentliche GNU-Lizenz,
+ebendiese
+Freiheit garantieren. Sie soll sicherstellen, da&szlig; die
+Software f&uuml;r
+alle Benutzer frei ist. Diese Lizenz gilt f&uuml;r den
+Gro&szlig;teil der von
+der <em>Free Software Foundation</em> herausgegebenen
+Software und
+f&uuml;r alle anderen Programme, deren Autoren ihr Werk dieser
+Lizenz
+unterstellt haben. Auch Sie k&ouml;nnen diese M&ouml;glichkeit
+der
+Lizenzierung f&uuml;r Ihre Programme anwenden. (Ein anderer Teil
+der
+Software der <em>Free Software Foundation</em> unterliegt
+stattdessen der <em>GNU Lesser General Public License</em>,
+der
+Kleineren Allgemeinen &Ouml;ffentlichen GNU-Lizenz.)
+</p>
+<p>
+Die Bezeichnung <em>&bdquo;freie&ldquo; Software</em>
+bezieht sich
+auf Freiheit, nicht auf den Preis. Unsere Lizenzen sollen Ihnen
+die Freiheit garantieren, Kopien freier Software zu verbreiten
+(und etwas f&uuml;r diesen Service zu berechnen, wenn Sie
+m&ouml;chten), die
+M&ouml;glichkeit, die Software im Quelltext zu erhalten oder den
+Quelltext auf Wunsch zu bekommen. Die Lizenzen sollen garantieren,
+da&szlig; Sie die Software &auml;ndern oder Teile davon in
+neuen freien
+Programmen verwenden d&uuml;rfen &ndash; und da&szlig; Sie
+wissen, da&szlig; Sie
+dies alles tun d&uuml;rfen.
+</p>
+<p>
+Um Ihre Rechte zu sch&uuml;tzen, m&uuml;ssen wir
+Einschr&auml;nkungen machen, die
+es jedem verbieten, Ihnen diese Rechte zu verweigern oder Sie
+aufzufordern, auf diese Rechte zu verzichten. Aus diesen
+Einschr&auml;nkungen folgen bestimmte Verantwortlichkeiten
+f&uuml;r Sie,
+wenn Sie Kopien der Software verbreiten oder sie ver&auml;ndern.
+</p>
+<p>
+Beispielsweise m&uuml;ssen Sie den Empf&auml;ngern alle Rechte
+gew&auml;hren, die
+Sie selbst haben, wenn Sie &ndash; kostenlos oder gegen Bezahlung
+&ndash; Kopien eines solchen Programms verbreiten. Sie
+m&uuml;ssen
+sicherstellen, da&szlig; auch die Empf&auml;nger den Quelltext
+erhalten
+bzw.&nbsp;erhalten k&ouml;nnen. Und Sie m&uuml;ssen ihnen
+diese Bedingungen
+zeigen, damit sie ihre Rechte kennen.
+</p>
+<p>
+Wir sch&uuml;tzen Ihre Rechte in zwei Schritten: (1) Wir stellen
+die
+Software unter ein Urheberrecht (Copyright), und (2) wir bieten
+Ihnen diese Lizenz an, die Ihnen das Recht gibt, die Software zu
+vervielf&auml;ltigen, zu verbreiten und/oder zu ver&auml;ndern.
+</p>
+<p>
+Um die Autoren und uns zu sch&uuml;tzen, wollen wir
+dar&uuml;berhinaus
+sicherstellen, da&szlig; jeder erf&auml;hrt, da&szlig;
+f&uuml;r diese freie Software
+keinerlei Garantie besteht. Wenn die Software von jemand anderem
+modifiziert und weitergegeben wird, m&ouml;chten wir, da&szlig;
+die Empf&auml;nger
+wissen, da&szlig; sie nicht das Original erhalten haben, damit
+irgendwelche von anderen verursachte Probleme nicht den Ruf des
+urspr&uuml;nglichen Autors sch&auml;digen.
+</p>
+<p>
+Schlie&szlig;lich und endlich ist jedes freie Programm permanent
+durch
+Software-Patente bedroht. Wir m&ouml;chten die Gefahr
+ausschlie&szlig;en, da&szlig;
+Distributoren eines freien Programms individuell Patente
+lizensieren &ndash; mit dem Ergebnis, da&szlig; das Programm
+propriet&auml;r
+w&uuml;rde. Um dies zu verhindern, haben wir klargestellt,
+da&szlig; jedes
+Patent entweder f&uuml;r freie Benutzung durch jedermann lizenziert
+werden mu&szlig; oder &uuml;berhaupt nicht lizenziert werden
+darf.
+</p>
+<p>
+Es folgen die genauen Bedingungen f&uuml;r die
+Vervielf&auml;ltigung,
+Verbreitung und Bearbeitung:
+</p>
+<h2>Allgemeine &Ouml;ffentliche GNU-Lizenz</h2>
+<h2>Bedingungen f&uuml;r die Vervielf&auml;ltigung,
+Verbreitung und Bearbeitung</h2>
+<p>
+&sect;0. Diese Lizenz gilt f&uuml;r jedes Programm und jedes
+andere
+Werk, in dem ein entsprechender Vermerk des Copyright-Inhabers
+darauf hinweist, da&szlig; das Werk unter den Bestimmungen dieser
+<em>General Public License</em> verbreitet werden darf. Im
+folgenden wird jedes derartige Programm oder Werk als &bdquo;das
+Programm&ldquo; bezeichnet; die Formulierung &bdquo;auf dem
+Programm basierendes Werk&ldquo; bezeichnet das Programm sowie
+jegliche Bearbeitung des Programms im urheberrechtlichen Sinne,
+also ein Werk, welches das Programm, auch auszugsweise, sei es
+unver&auml;ndert oder ver&auml;ndert und/oder in eine andere
+Sprache
+&uuml;bersetzt, enth&auml;lt. (Im folgenden wird die
+&Uuml;bersetzung ohne
+Einschr&auml;nkung als &bdquo;Bearbeitung&ldquo;
+eingestuft.) Jeder
+Lizenznehmer wird im folgenden als &bdquo;Sie&ldquo;
+angesprochen.
+</p>
+<p>
+Andere Handlungen als Vervielf&auml;ltigung, Verbreitung und
+Bearbeitung werden von dieser Lizenz nicht ber&uuml;hrt; sie fallen
+nicht in ihren Anwendungsbereich. Der Vorgang der Ausf&uuml;hrung
+des
+Programms wird nicht eingeschr&auml;nkt, und die Ausgaben des
+Programms
+unterliegen dieser Lizenz nur, wenn der Inhalt ein auf dem
+Programm basierendes Werk darstellt (unabh&auml;ngig davon,
+da&szlig; die
+Ausgabe durch die Ausf&uuml;hrung des Programmes erfolgte). Ob dies
+zutrifft, h&auml;ngt von den Funktionen des Programms ab.
+</p>
+<p>
+&sect;1. Sie d&uuml;rfen auf beliebigen Medien
+unver&auml;nderte Kopien des
+Quelltextes des Programms, wie sie ihn erhalten haben, anfertigen
+und verbreiten. Voraussetzung hierf&uuml;r ist, da&szlig; Sie
+mit jeder Kopie
+einen entsprechenden Copyright-Vermerk sowie einen
+Haftungsausschlu&szlig; ver&ouml;ffentlichen, alle Vermerke,
+die sich auf
+diese Lizenz und das Fehlen einer Garantie beziehen,
+unver&auml;ndert
+lassen und desweiteren allen anderen Empf&auml;ngern des Programms
+zusammen mit dem Programm eine Kopie dieser Lizenz zukommen
+lassen.
+</p>
+<p>
+Sie d&uuml;rfen f&uuml;r den eigentlichen Kopiervorgang eine
+Geb&uuml;hr
+verlangen. Wenn Sie es w&uuml;nschen, d&uuml;rfen Sie auch
+gegen Entgelt
+eine Garantie f&uuml;r das Programm anbieten.
+</p>
+<p>
+&sect;2. Sie d&uuml;rfen Ihre Kopie(n) des Programms oder eines
+Teils
+davon ver&auml;ndern, wodurch ein auf dem Programm basierendes Werk
+entsteht; Sie d&uuml;rfen derartige Bearbeitungen unter den
+Bestimmungen von Paragraph 1 vervielf&auml;ltigen und verbreiten,
+vorausgesetzt, da&szlig; zus&auml;tzlich alle im folgenden
+genannten
+Bedingungen erf&uuml;llt werden:
+</p>
+<ol type="lower-alpha">
+ <li>
+ <p>Sie m&uuml;ssen die ver&auml;nderten Dateien mit
+einem auff&auml;lligen Vermerk
+versehen, der auf die von Ihnen vorgenommene Modifizierung und
+das Datum jeder &Auml;nderung hinweist. </p>
+ </li>
+ <li>
+ <p>Sie m&uuml;ssen daf&uuml;r sorgen, da&szlig;
+jede von Ihnen verbreitete oder
+ver&ouml;ffentlichte Arbeit, die ganz oder teilweise von dem
+Programm
+oder Teilen davon abgeleitet ist, Dritten gegen&uuml;ber als Ganzes
+unter den Bedingungen dieser Lizenz ohne Lizenzgeb&uuml;hren zur
+Verf&uuml;gung gestellt wird. </p>
+ </li>
+ <li>
+ <p>Wenn das ver&auml;nderte Programm normalerweise bei
+der Ausf&uuml;hrung
+interaktiv Kommandos einliest, m&uuml;ssen Sie daf&uuml;r
+sorgen, da&szlig; es,
+wenn es auf dem &uuml;blichsten Wege f&uuml;r solche
+interaktive Nutzung
+gestartet wird, eine Meldung ausgibt oder ausdruckt, die einen
+geeigneten Copyright-Vermerk enth&auml;lt sowie einen Hinweis,
+da&szlig; es
+keine Gew&auml;hrleistung gibt (oder anderenfalls, da&szlig;
+Sie Garantie
+leisten), und da&szlig; die Benutzer das Programm unter diesen
+Bedingungen weiter verbreiten d&uuml;rfen. Auch mu&szlig; der
+Benutzer
+darauf hingewiesen werden, wie er eine Kopie dieser Lizenz
+ansehen kann. (Ausnahme: Wenn das Programm selbst interaktiv
+arbeitet, aber normalerweise keine derartige Meldung ausgibt,
+mu&szlig;
+Ihr auf dem Programm basierendes Werk auch keine solche Meldung
+ausgeben). </p>
+ </li>
+</ol>
+<p>
+Diese Anforderungen gelten f&uuml;r das bearbeitete Werk als
+Ganzes.
+Wenn identifizierbare Teile des Werkes nicht von dem Programm
+abgeleitet sind und vern&uuml;nftigerweise als unabh&auml;ngige
+und
+eigenst&auml;ndige Werke f&uuml;r sich selbst zu betrachten
+sind, dann
+gelten diese Lizenz und ihre Bedingungen nicht f&uuml;r die
+betroffenen
+Teile, wenn Sie diese als eigenst&auml;ndige Werke weitergeben.
+Wenn
+Sie jedoch dieselben Abschnitte als Teil eines Ganzen weitergeben,
+das ein auf dem Programm basierendes Werk darstellt, dann mu&szlig;
+die
+Weitergabe des Ganzen nach den Bedingungen dieser Lizenz erfolgen,
+deren Bedingungen f&uuml;r weitere Lizenznehmer somit auf das
+gesamte
+Ganze ausgedehnt werden &ndash; und somit auf jeden einzelnen
+Teil, unabh&auml;ngig vom jeweiligen Autor.
+</p>
+<p>
+Somit ist es nicht die Absicht dieses Abschnittes, Rechte f&uuml;r
+Werke in Anspruch zu nehmen oder Ihnen die Rechte f&uuml;r Werke
+streitig zu machen, die komplett von Ihnen geschrieben wurden;
+vielmehr ist es die Absicht, die Rechte zur Kontrolle der
+Verbreitung von Werken, die auf dem Programm basieren oder unter
+seiner auszugsweisen Verwendung zusammengestellt worden sind,
+auszu&uuml;ben.
+</p>
+<p>
+Ferner bringt auch das einfache Zusammenlegen eines anderen
+Werkes, das nicht auf dem Programm basiert, mit dem Programm oder
+einem auf dem Programm basierenden Werk auf ein- und demselben
+Speicher- oder Vertriebsmedium dieses andere Werk nicht in den
+Anwendungsbereich dieser Lizenz.
+</p>
+<p>
+&sect;3. Sie d&uuml;rfen das Programm (oder ein darauf
+basierendes Werk
+gem&auml;&szlig; Paragraph 2) als Objectcode oder in
+ausf&uuml;hrbarer Form unter
+den Bedingungen der Paragraphen 1 und 2 kopieren und weitergeben
+&ndash; vorausgesetzt, da&szlig; Sie au&szlig;erdem eine
+der folgenden
+Leistungen erbringen:
+</p>
+<ol type="lower-alpha">
+ <li>
+ <p>Liefern Sie das Programm zusammen mit dem
+vollst&auml;ndigen
+zugeh&ouml;rigen maschinenlesbaren Quelltext auf einem f&uuml;r
+den
+Datenaustausch &uuml;blichen Medium aus, wobei die Verteilung unter
+den Bedingungen der Paragraphen 1 und 2 erfolgen mu&szlig;. Oder: </p>
+ </li>
+ <li>
+ <p>Liefern Sie das Programm zusammen mit einem mindestens
+drei Jahre
+lang g&uuml;ltigen schriftlichen Angebot aus, jedem Dritten eine
+vollst&auml;ndige maschinenlesbare Kopie des Quelltextes zur
+Verf&uuml;gung
+zu stellen &ndash; zu nicht h&ouml;heren Kosten als denen, die
+durch
+den physikalischen Kopiervorgang anfallen &ndash;, wobei der
+Quelltext unter den Bedingungen der Paragraphen 1 und 2 auf einem
+f&uuml;r den Datenaustausch &uuml;blichen Medium weitergegeben
+wird. Oder: </p>
+ </li>
+ <li>
+ <p>Liefern Sie das Programm zusammen mit dem schriftlichen
+Angebot
+der Zurverf&uuml;gungstellung des Quelltextes aus, das Sie selbst
+erhalten haben. (Diese Alternative ist nur f&uuml;r
+nicht-kommerzielle
+Verbreitung zul&auml;ssig und nur, wenn Sie das Programm als
+Objectcode oder in ausf&uuml;hrbarer Form mit einem entsprechenden
+Angebot gem&auml;&szlig; Absatz b erhalten haben.) </p>
+ </li>
+</ol>
+<p>
+Unter dem Quelltext eines Werkes wird diejenige Form des Werkes
+verstanden, die f&uuml;r Bearbeitungen vorzugsweise verwendet wird.
+F&uuml;r
+ein ausf&uuml;hrbares Programm bedeutet &bdquo;der komplette
+Quelltext&ldquo;: Der Quelltext aller im Programm enthaltenen
+Module einschlie&szlig;lich aller zugeh&ouml;rigen
+Modulschnittstellen-Definitionsdateien sowie der zur Compilation
+und Installation verwendeten Skripte. Als besondere Ausnahme
+jedoch braucht der verteilte Quelltext nichts von dem zu
+enthalten, was &uuml;blicherweise (entweder als Quelltext oder in
+bin&auml;rer Form) zusammen mit den Hauptkomponenten des
+Betriebssystems (Kernel, Compiler usw.) geliefert wird, unter dem
+das Programm l&auml;uft &ndash; es sei denn, diese Komponente
+selbst
+geh&ouml;rt zum ausf&uuml;hrbaren Programm.
+</p>
+<p>
+Wenn die Verbreitung eines ausf&uuml;hrbaren Programms oder von
+Objectcode dadurch erfolgt, da&szlig; der Kopierzugriff auf eine
+daf&uuml;r
+vorgesehene Stelle gew&auml;hrt wird, so gilt die
+Gew&auml;hrung eines
+gleichwertigen Zugriffs auf den Quelltext als Verbreitung des
+Quelltextes, auch wenn Dritte nicht dazu gezwungen sind, den
+Quelltext zusammen mit dem Objectcode zu kopieren.
+</p>
+<p>
+&sect;4. Sie d&uuml;rfen das Programm nicht
+vervielf&auml;ltigen, ver&auml;ndern,
+weiter lizenzieren oder verbreiten, sofern es nicht durch diese
+Lizenz ausdr&uuml;cklich gestattet ist. Jeder anderweitige Versuch
+der
+Vervielf&auml;ltigung, Modifizierung, Weiterlizenzierung und
+Verbreitung ist nichtig und beendet automatisch Ihre Rechte unter
+dieser Lizenz. Jedoch werden die Lizenzen Dritter, die von Ihnen
+Kopien oder Rechte unter dieser Lizenz erhalten haben, nicht
+beendet, solange diese die Lizenz voll anerkennen und befolgen.
+</p>
+<p>
+&sect;5. Sie sind nicht verpflichtet, diese Lizenz anzunehmen, da
+Sie sie nicht unterzeichnet haben. Jedoch gibt Ihnen nichts
+anderes die Erlaubnis, das Programm oder von ihm abgeleitete Werke
+zu ver&auml;ndern oder zu verbreiten. Diese Handlungen sind
+gesetzlich
+verboten, wenn Sie diese Lizenz nicht anerkennen. Indem Sie das
+Programm (oder ein darauf basierendes Werk) ver&auml;ndern oder
+verbreiten, erkl&auml;ren Sie Ihr Einverst&auml;ndnis mit
+dieser Lizenz und
+mit allen ihren Bedingungen bez&uuml;glich der
+Vervielf&auml;ltigung,
+Verbreitung und Ver&auml;nderung des Programms oder eines darauf
+basierenden Werks.
+</p>
+<p>
+&sect;6. Jedesmal, wenn Sie das Programm (oder ein auf dem
+Programm basierendes Werk) weitergeben, erh&auml;lt der
+Empf&auml;nger
+automatisch vom urspr&uuml;nglichen Lizenzgeber die Lizenz, das
+Programm entsprechend den hier festgelegten Bestimmungen zu
+vervielf&auml;ltigen, zu verbreiten und zu ver&auml;ndern. Sie
+d&uuml;rfen keine
+weiteren Einschr&auml;nkungen der Durchsetzung der hierin
+zugestandenen
+Rechte des Empf&auml;ngers vornehmen. Sie sind nicht daf&uuml;r
+verantwortlich, die Einhaltung dieser Lizenz durch Dritte
+durchzusetzen.
+</p>
+<p>
+&sect;7. Sollten Ihnen infolge eines Gerichtsurteils, des Vorwurfs
+einer Patentverletzung oder aus einem anderen Grunde (nicht auf
+Patentfragen begrenzt) Bedingungen (durch Gerichtsbeschlu&szlig;,
+Vergleich oder anderweitig) auferlegt werden, die den Bedingungen
+dieser Lizenz widersprechen, so befreien Sie diese Umst&auml;nde
+nicht
+von den Bestimmungen dieser Lizenz. Wenn es Ihnen nicht
+m&ouml;glich
+ist, das Programm unter gleichzeitiger Beachtung der Bedingungen
+in dieser Lizenz und Ihrer anderweitigen Verpflichtungen zu
+verbreiten, dann d&uuml;rfen Sie als Folge das Programm
+&uuml;berhaupt nicht
+verbreiten. Wenn zum Beispiel ein Patent nicht die
+geb&uuml;hrenfreie
+Weiterverbreitung des Programms durch diejenigen erlaubt, die das
+Programm direkt oder indirekt von Ihnen erhalten haben, dann
+besteht der einzige Weg, sowohl das Patentrecht als auch diese
+Lizenz zu befolgen, darin, ganz auf die Verbreitung des Programms
+zu verzichten.
+</p>
+<p>
+Sollte sich ein Teil dieses Paragraphen als ung&uuml;ltig oder
+unter
+bestimmten Umst&auml;nden nicht durchsetzbar erweisen, so soll
+dieser
+Paragraph seinem Sinne nach angewandt werden; im &uuml;brigen soll
+dieser Paragraph als Ganzes gelten.
+</p>
+<p>
+Zweck dieses Paragraphen ist nicht, Sie dazu zu bringen,
+irgendwelche Patente oder andere Eigentumsanspr&uuml;che zu
+verletzen
+oder die G&uuml;ltigkeit solcher Anspr&uuml;che zu bestreiten;
+dieser
+Paragraph hat einzig den Zweck, die Integrit&auml;t des
+Verbreitungssystems der freien Software zu sch&uuml;tzen, das durch
+die
+Praxis &ouml;ffentlicher Lizenzen verwirklicht wird. Viele Leute
+haben
+gro&szlig;z&uuml;gige Beitr&auml;ge zu dem gro&szlig;en
+Angebot der mit diesem System
+verbreiteten Software im Vertrauen auf die konsistente Anwendung
+dieses Systems geleistet; es liegt am Autor/Geber, zu entscheiden,
+ob er die Software mittels irgendeines anderen Systems verbreiten
+will; ein Lizenznehmer hat auf diese Entscheidung keinen
+Einflu&szlig;.
+</p>
+<p>
+Dieser Paragraph ist dazu gedacht, deutlich klarzustellen, was als
+Konsequenz aus dem Rest dieser Lizenz betrachtet wird.
+</p>
+<p>
+&sect;8. Wenn die Verbreitung und/oder die Benutzung des Programms
+in bestimmten Staaten entweder durch Patente oder durch
+urheberrechtlich gesch&uuml;tzte Schnittstellen
+eingeschr&auml;nkt ist, kann
+der Urheberrechtsinhaber, der das Programm unter diese Lizenz
+gestellt hat, eine explizite geographische Begrenzung der
+Verbreitung angeben, in der diese Staaten ausgeschlossen werden,
+so da&szlig; die Verbreitung nur innerhalb und zwischen den Staaten
+erlaubt ist, die nicht ausgeschlossen sind. In einem solchen Fall
+beinhaltet diese Lizenz die Beschr&auml;nkung, als w&auml;re
+sie in diesem
+Text niedergeschrieben.
+</p>
+<p>
+&sect;9. Die <em>Free Software Foundation</em> kann
+von Zeit zu
+Zeit &uuml;berarbeitete und/oder neue Versionen der <em>General
+Public
+License</em> ver&ouml;ffentlichen. Solche neuen Versionen
+werden vom
+Grundprinzip her der gegenw&auml;rtigen entsprechen,
+k&ouml;nnen aber im
+Detail abweichen, um neuen Problemen und Anforderungen gerecht zu
+werden.
+</p>
+<p>
+Jede Version dieser Lizenz hat eine eindeutige Versionsnummer.
+Wenn in einem Programm angegeben wird, da&szlig; es dieser Lizenz
+in
+einer bestimmten Versionsnummer oder &bdquo;jeder sp&auml;teren
+Version&ldquo; <em>(&ldquo;any later version&rdquo;)</em>
+unterliegt, so haben Sie die Wahl, entweder den Bestimmungen der
+genannten Version zu folgen oder denen jeder beliebigen
+sp&auml;teren
+Version, die von der <em>Free Software Foundation</em>
+ver&ouml;ffentlicht wurde. Wenn das Programm keine Versionsnummer
+angibt, k&ouml;nnen Sie eine beliebige Version w&auml;hlen, die
+je von der
+<em>Free Software Foundation</em> ver&ouml;ffentlicht
+wurde.
+</p>
+<p>
+&sect;10. Wenn Sie den Wunsch haben, Teile des Programms in
+anderen freien Programmen zu verwenden, deren Bedingungen f&uuml;r
+die
+Verbreitung anders sind, schreiben Sie an den Autor, um ihn um die
+Erlaubnis zu bitten. F&uuml;r Software, die unter dem Copyright der
+<em>Free Software Foundation</em> steht, schreiben Sie an
+die
+<em>Free Software Foundation</em>; wir machen zu diesem
+Zweck
+gelegentlich Ausnahmen. Unsere Entscheidung wird von den beiden
+Zielen geleitet werden, zum einen den freien Status aller von
+unserer freien Software abgeleiteten Werke zu erhalten und zum
+anderen das gemeinschaftliche Nutzen und Wiederverwenden von
+Software im allgemeinen zu f&ouml;rdern.
+</p>
+<h3>Keine Gew&auml;hrleistung</h3>
+<p><strong>
+&sect;11. Da das Programm ohne jegliche Kosten lizenziert wird,
+besteht keinerlei Gew&auml;hrleistung f&uuml;r das Programm,
+soweit dies
+gesetzlich zul&auml;ssig ist. Sofern nicht anderweitig schriftlich
+best&auml;tigt, stellen die Copyright-Inhaber und/oder Dritte das
+Programm so zur Verf&uuml;gung, &bdquo;wie es ist&ldquo;,
+ohne
+irgendeine Gew&auml;hrleistung, weder ausdr&uuml;cklich noch
+implizit,
+einschlie&szlig;lich &ndash; aber nicht begrenzt auf
+&ndash; Marktreife
+oder Verwendbarkeit f&uuml;r einen bestimmten Zweck. Das volle
+Risiko
+bez&uuml;glich Qualit&auml;t und Leistungsf&auml;higkeit
+des Programms liegt bei
+Ihnen. Sollte sich das Programm als fehlerhaft herausstellen,
+liegen die Kosten f&uuml;r notwendigen Service, Reparatur oder
+Korrektur bei Ihnen.
+</strong></p>
+<p><strong>
+&sect;12. In keinem Fall, au&szlig;er wenn durch geltendes
+Recht
+gefordert oder schriftlich zugesichert, ist irgendein
+Copyright-Inhaber oder irgendein Dritter, der das Programm wie
+oben erlaubt modifiziert oder verbreitet hat, Ihnen gegen&uuml;ber
+f&uuml;r
+irgendwelche Sch&auml;den haftbar, einschlie&szlig;lich
+jeglicher allgemeiner
+oder spezieller Sch&auml;den, Sch&auml;den durch Seiteneffekte
+(Nebenwirkungen) oder Folgesch&auml;den, die aus der Benutzung des
+Programms oder der Unbenutzbarkeit des Programms folgen
+(einschlie&szlig;lich &ndash; aber nicht beschr&auml;nkt
+auf &ndash;
+Datenverluste, fehlerhafte Verarbeitung von Daten, Verluste, die
+von Ihnen oder anderen getragen werden m&uuml;ssen, oder dem
+Unverm&ouml;gen
+des Programms, mit irgendeinem anderen Programm
+zusammenzuarbeiten), selbst wenn ein Copyright-Inhaber oder
+Dritter &uuml;ber die M&ouml;glichkeit solcher Sch&auml;den
+unterrichtet worden
+war.
+</strong></p>
+<h2>Ende der Bedingungen</h2>
+<h2>
+Wie Sie diese Bedingungen auf Ihre eigenen, neuen Programme
+anwenden k&ouml;nnen
+</h2>
+<p>
+Wenn Sie ein neues Programm entwickeln und wollen, da&szlig; es vom
+gr&ouml;&szlig;tm&ouml;glichen Nutzen f&uuml;r die
+Allgemeinheit ist, dann erreichen
+Sie das am besten, indem Sie es zu freier Software machen, die
+jeder unter diesen Bestimmungen weiterverbreiten und ver&auml;ndern
+kann.
+</p>
+<p>
+Um dies zu erreichen, f&uuml;gen Sie die folgenden Vermerke zu
+Ihrem
+Programm hinzu. Am sichersten ist es, sie an den Anfang einer
+jeden Quelldatei zu stellen, um den
+Gew&auml;hrleistungsausschlu&szlig;
+m&ouml;glichst deutlich darzustellen; zumindest aber sollte jede
+Datei
+eine Copyright-Zeile besitzen sowie einen kurzen Hinweis darauf,
+wo die vollst&auml;ndigen Vermerke zu finden sind.
+</p>
+<blockquote>
+ <p><tt>[<i>eine Zeile mit dem Programmnamen und
+einer kurzen Beschreibung</i>]<br>
+Copyright (C) [<i>Jahr</i>]&nbsp;&nbsp;[<i>Name
+des Autors</i>] </tt></p>
+ <p><tt>This program is free software; you can
+redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version. </tt></p>
+ <p><tt>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. </tt></p>
+ <p><tt>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 St, Fifth Floor, Boston, MA 02110,
+USA </tt></p>
+</blockquote>
+<p>
+Auf Deutsch:
+</p>
+<blockquote>
+ <p><tt>[<i>eine Zeile mit dem Programmnamen und
+einer kurzen Beschreibung</i>]<br>
+Copyright (C) [<i>Jahr</i>]&nbsp;&nbsp;[<i>Name
+des Autors</i>] </tt></p>
+ <p><tt>Dieses Programm ist freie Software. Sie
+k&ouml;nnen es unter den
+Bedingungen der GNU General Public License, wie von der Free
+Software Foundation ver&ouml;ffentlicht, weitergeben und/oder
+modifizieren, entweder gem&auml;&szlig; Version 2 der Lizenz
+oder (nach
+Ihrer Option) jeder sp&auml;teren Version. </tt></p>
+ <p><tt>Die Ver&ouml;ffentlichung dieses Programms
+erfolgt in der Hoffnung,
+da&szlig; es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE
+GARANTIE,
+sogar ohne die implizite Garantie der MARKTREIFE oder der
+VERWENDBARKEIT F&Uuml;R EINEN BESTIMMTEN ZWECK. Details finden Sie
+in
+der GNU General Public License. </tt></p>
+ <p><tt>Sie sollten ein Exemplar der GNU General Public
+License zusammen
+mit diesem Programm erhalten haben. Falls nicht, schreiben Sie an
+die Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+Boston, MA 02110, USA. </tt></p>
+</blockquote>
+<p>
+F&uuml;gen Sie auch einen kurzen Hinweis hinzu, wie Sie
+elektronisch
+und per Brief erreichbar sind.
+</p>
+<p>
+Wenn Ihr Programm interaktiv ist, sorgen Sie daf&uuml;r,
+da&szlig; es nach
+dem Start einen kurzen Vermerk ausgibt:
+</p>
+<blockquote>
+ <p><tt>version 69, Copyright (C)
+[<i>Jahr</i>]&nbsp;&nbsp;[<i>Name des Autors</i>]<br>
+Gnomovision
+comes with ABSOLUTELY NO WARRANTY; for details type &lsquo;show
+w&rsquo;. This is free software, and you are welcome to
+redistribute it under certain conditions; type &lsquo;show
+c&rsquo; for details. </tt></p>
+</blockquote>
+<p>
+Auf Deutsch:
+</p>
+<blockquote>
+ <p><tt>Version 69, Copyright (C)
+[<i>Jahr</i>]&nbsp;&nbsp;[<i>Name des Autors</i>]
+F&uuml;r Gnomovision besteht KEINERLEI GARANTIE; geben Sie "show
+w" f&uuml;r Details ein. Gnonovision ist freie Software, die Sie
+unter bestimmten Bedingungen weitergeben d&uuml;rfen; geben Sie
+"show c" f&uuml;r Details ein. </tt></p>
+</blockquote>
+<p>
+Die hypothetischen Kommandos &bdquo;<code>show w</code>&ldquo;
+und
+&bdquo;<code>show c</code>&ldquo; sollten die
+entsprechenden Teile
+der GNU-GPL anzeigen. Nat&uuml;rlich k&ouml;nnen die von Ihnen
+verwendeten
+Kommandos anders hei&szlig;en als &bdquo;<code>show w</code>&ldquo;
+und
+&bdquo;<code>show c</code>&ldquo;; es
+k&ouml;nnten auch Mausklicks oder
+Men&uuml;punkte sein &ndash; was immer am besten in Ihr
+Programm pa&szlig;t.
+</p>
+<p>
+Soweit vorhanden, sollten Sie auch Ihren Arbeitgeber (wenn Sie als
+Programmierer arbeiten) oder Ihre Schule einen Copyright-Verzicht
+f&uuml;r das Programm unterschreiben lassen. Hier ein Beispiel. Die
+Namen m&uuml;ssen Sie nat&uuml;rlich &auml;ndern.
+</p>
+<blockquote>
+ <p><tt>Yoyodyne, Inc., hereby disclaims all copyright
+interest in the
+program &lsquo;Gnomovision&rsquo; (which makes passes at
+compilers) written by James Hacker. </tt></p>
+ <p><tt>[<i>Unterschrift von Ty Coon</i>], 1
+April 1989<br>
+Ty Coon, President of Vice </tt></p>
+</blockquote>
+<p>
+Auf Deutsch:
+</p>
+<blockquote>
+ <p><tt>Die Yoyodyne GmbH erhebt keinen urheberrechtlichen
+Anspruch auf
+das von James Hacker geschriebene Programm
+"Gnomovision" (einem Schrittmacher f&uuml;r Compiler). </tt></p>
+ <p><tt>[<i>Unterschrift von Ty Coon</i>], 1.
+April 1989<br>
+Ty Coon, Vizepr&auml;sident </tt></p>
+</blockquote>
+<p>
+Diese <em>General Public License</em> gestattet nicht die
+Einbindung des Programms in propriet&auml;re Programme. Ist Ihr
+Programm eine Funktionsbibliothek, so kann es sinnvoller sein, das
+Binden propriet&auml;rer Programme mit dieser Bibliothek zu
+gestatten.
+Wenn Sie dies tun wollen, sollten Sie die
+GNU Lesser General Public License anstelle dieser Lizenz verwenden.
+</p>
+</div>
+</body>
+</html>
diff --git a/logo/src/xlogo/gpl/gpl-el.html b/logo/src/xlogo/gpl/gpl-el.html
new file mode 100644
index 0000000..1855643
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-el.html
@@ -0,0 +1,175 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+ <TITLE></TITLE>
+ <META NAME="GENERATOR" CONTENT="OpenOffice.org 2.4 (Linux)">
+ <META NAME="CREATED" CONTENT="0;0">
+ <META NAME="CHANGED" CONTENT="0;0">
+ <STYLE TYPE="text/css">
+ <!--
+ @page { size: 21cm 29.7cm; margin: 2cm }
+ P { margin-bottom: 0.21cm }
+ PRE { font-family: "Times New Roman" }
+ -->
+ </STYLE>
+</HEAD>
+<BODY LANG="fr-FR" DIR="LTR">
+<PRE>Αυτή είναι μια ανεπίσημη μετάφραση της Γενικής ’δειας Δημόσιας Χρήσης
+GNU (GNU GPL) στα ελληνικά. Δεν εκδόθηκε από το Ίδρυμα Ελεύθερου
+Λογισμικού (Free Software Foundation) και δεν διατυπώνει νομικά τους όρους
+διανομής λογισμικού που υπάγεται στη Γενική ’δεια Δημόσιας Χρήσης
+GNU-αυτό γίνεται μόνο από την επίσημη αγγλική έκδοση της ’δειας (GNU GPL).
+Ωστόσο, ελπίζουμε ότι η μετάφραση αυτή θα βοηθήσει όσους μιλούν την
+ελληνική να κατανοήσουν καλύτερα την ’δεια GNU GPL.
+
+This is an unofficial translation of the GNU General Public License into greek. It was not published by the Free Software Foundation, and does not legally state the distribution terms for software that uses the GNU GPL-only the original English text of the GNU GPL does that. However, we hope that this translation will help greek speakers understand the GNU GPL better.
+
+
+ ΓΕΝΙΚΗ ΑΔΕΙΑ ΔΗΜΟΣΙΑΣ ΧΡΗΣΗΣ GNU
+
+ Έκδοση 2, Ιούνιος 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Επιτρέπεται σε όλους η αντιγραφή και διανομή αυτούσιων αντιγράφων
+ αυτού του εγγράφου άδειας χρήσης, χωρίς ωστόσο να επιτρέπεται η αλλοίωσή του.
+
+ Εισαγωγή
+
+ Οι άδειες χρήσης των περισσότερων προγραμμάτων συντάσσονται για να περιορίσουν την ελευθερία σας να τα μοιράζεστε με άλλους και να τα επεξεργάζεστε. Εν αντιθέσει, η Γενική ’δεια Δημόσιας Χρήσης GNU έχει σκοπό να εγγυηθεί την ελευθερία σας να χρησιμοποιείτε από κοινού με άλλους και να τροποποιείτε προγράμματα που διατίθενται ελεύθερα -- δηλαδή να εγγυηθεί ότι το πρόγραμμα είναι ελεύθερο για όλους τους χρήστες. Αυτή η Γενική ’δεια Δημόσιας Χρήσης ισχύει για τα περισσότερα προγράμματα του Ιδρύματος Ελεύθερου Λογισμικού (Free Software Foundation), καθώς και για κάθε άλλο πρόγραμμα οι δημιουργοί του οποίου συμμορφώνονται με την άδεια αυτή. (Ορισμένα άλλα προγράμματα του Ιδρύματος Ελεύθερου Λογισμικού καλύπτονται από τη Γενική ’δεια Δημόσιας Χρήσης Βιβλιοθήκης GNU.) Την άδεια αυτή μπορείτε να την εφαρμόσετε και στα δικά σας προγράμματα.
+
+ Μιλώντας για ελεύθερο λογισμικό, αναφερόμαστε στην ελευθερία χρήσης του, όχι
+στο κόστος του. Οι Γενικές ’δειες Δημόσιας Χρήσης τις οποίες συντάσσουμε έχουν σκοπό να κατοχυρώσουν την ελευθερία σας να διανέμετε αντίγραφα ελεύθερου λογισμικού (και να χρεώνετε, εάν το επιθυμείτε, την παροχή αυτής της υπηρεσίας), να σας εξασφαλίσουν το δικαίωμα να λαμβάνετε τον πηγαίο κώδικα, εάν τον χρειάζεστε, καθώς και να τροποποιείτε το πρόγραμμα ή να χρησιμοποιείτε τμήματά του σε καινούργια ελεύθερα προγράμματα -- και να διασφαλίσουν ότι είστε ενήμεροι για τα παραπάνω δικαιώματά σας.
+
+ Για την προστασία των δικαιωμάτων σας, επιβάλλεται να προβούμε σε περιορισμούς
+οι οποίοι θα εμποδίζουν σε κάποιον να αμφισβητήσει τα δικαιώματά σας ή να σας ζητήσει να παραιτηθείτε από αυτά. Αυτοί οι περιορισμοί ερμηνεύονται ως συγκεκριμένες ευθύνες για εσάς εάν διανέμετε αντίγραφα κάποιου ελεύθερου λογισμικού ή εάν το τροποποιείτε.
+
+ Για παράδειγμα, εάν διανέμετε αντίγραφα ενός τέτοιου προγράμματος, είτε δωρεάν
+είτε με χρέωση, πρέπει να εκχωρήσετε στους παραλήπτες όλα τα δικαιώματα που έχετε και εσείς. Πρέπει να εγγυηθείτε ότι και εκείνοι επίσης λαμβάνουν, ή μπορούν να λάβουν, τον πηγαίο κώδικα. Πρέπει επίσης να τους επιδείξετε τους όρους αυτής της άδειας χρήσης, ώστε να είναι ενήμεροι για τα δικαιώματά τους.
+
+ Προστατεύουμε τα δικαιώματά σας με δύο τρόπους: (1) προστατεύοντας το λογισμικό και (2) προσφέροντάς σας αυτήν την άδεια, με την οποία αποκτάτε νόμιμο δικαίωμα αντιγραφής, διανομής ή/και τροποποίησης του λογισμικού.
+
+ Επιπλέον, για την προστασία των δημιουργών και τη δική μας, θέλουμε να καταστήσουμε βέβαιο ότι όλοι κατανοούν την απουσία εγγύησης για αυτό το ελεύθερο λογισμικό. Εάν το λογισμικό τροποποιηθεί από κάποιον τρίτο και στη συνέχεια διανεμηθεί, θέλουμε να γνωρίζουν οι παραλήπτες ότι το λογισμικό που απέκτησαν δεν είναι το πρωτότυπο, έτσι ώστε οποιοδήποτε πρόβλημα προκληθεί από τρίτους να μην βαρύνει το όνομα του δημιουργού.
+
+ Τέλος, κάθε ελεύθερο λογισμικό απειλείται συνεχώς από τις κατοχυρώσεις ευρεσιτεχνίας λογισμικού. Θέλουμε να αποφύγουμε τον κίνδυνο να αποκτήσουν οι αναδιανομείς ελεύθερου λογισμικού τίτλους ευρεσιτεχνίας, καθιστώντας έτσι το λογισμικό προσωπική τους ιδιοκτησία. Για να αποκλείσουμε αυτό το ενδεχόμενο, έχουμε ξεκαθαρίσει ότι οποιαδήποτε ευρεσιτεχνία θα πρέπει να παρέχει άδεια ελεύθερης χρήσης από όλους, διαφορετικά να μην παρέχει καμιά απολύτως άδεια.
+
+ Ακολουθούν οι ακριβείς όροι και συνθήκες αντιγραφής, διανομής και τροποποίησης.
+
+ ΓΕΝΙΚΗ ΑΔΕΙΑ ΔΗΜΟΣΙΑΣ ΧΡΗΣΗΣ GNU
+ ΟΡΟΙ ΚΑΙ ΣΥΝΘΗΚΕΣ ΑΝΤΙΓΡΑΦΗΣ, ΔΙΑΝΟΜΗΣ ΚΑΙ ΤΡΟΠΟΠΟΙΗΣΗΣ
+
+ 0. Η ’δεια αυτή ισχύει για κάθε πρόγραμμα ή άλλο έργο που περιέχει
+σημείωμα από τον κάτοχο πνευματικών δικαιωμάτων, στο οποίο αναφέρεται ότι η διανομή του προγράμματος είναι δυνατή υπό τους όρους αυτής της Γενικής ’δειας Δημόσιας Χρήσης. Ο όρος &quot;Πρόγραμμα&quot;, παρακάτω, αναφέρεται σε οποιοδήποτε τέτοιο πρόγραμμα ή έργο, ενώ ο όρος &quot;έργο βασισμένο στο Πρόγραμμα&quot; σημαίνει είτε το Πρόγραμμα είτε κάθε άλλο παραγόμενο έργο που υπάγεται στο νόμο περί πνευματικής ιδιοκτησίας: με λίγα λόγια, ένα έργο που περιέχει ακέραιο το Πρόγραμμα ή ένα μέρος του, είτε αυτούσιο είτε με τροποποιήσεις ή/και μεταφρασμένο σε άλλη γλώσσα. (Από αυτό το σημείο, η μετάφραση θα περιλαμβάνεται χωρίς περιορισμούς στον όρο &quot;τροποποίηση&quot;.) Κάθε κάτοχος της άδειας χρήσης θα αναφέρεται στο εξής ως &quot;εσείς/εσάς&quot;.
+
+’λλες δραστηριότητες πέραν της αντιγραφής, της διανομής και της τροποποίησης δεν καλύπτονται από αυτήν την ’δεια - είναι εκτός των πλαισίων της. Δεν υπάρχει περιορισμός στην ενέργεια εκτέλεσης ενός προγράμματος, ενώ το προϊόν της χρήσης του Προγράμματος καλύπτεται μόνο εφόσον το περιεχόμενό του συνιστά έργο βασισμένο στο Πρόγραμμα (ανεξάρτητα από το εάν δημιουργήθηκε με την εκτέλεση του Προγράμματος). Το κατά πόσο συμβαίνει αυτό εξαρτάται από το είδος του Προγράμματος.
+
+ 1. Επιτρέπεται η αντιγραφή και διανομή αυτούσιων αντιγράφων του πηγαίου κώδικα του Προγράμματος όπως ακριβώς το έχετε λάβει, σε οποιοδήποτε αποθηκευτικό μέσο, με την προϋπόθεση ότι: θα δημοσιεύσετε εμφανώς και καταλλήλως, σε κάθε αντίγραφο, ένα σημείωμα πνευματικής ιδιοκτησίας και ένα σημείωμα αποποίησης ευθυνών εγγύησης - ότι θα συμπεριλάβετε ακέραια όλα τα σημειώματα που αναφέρονται στην ’δεια αυτή και στην απουσία οποιασδήποτε εγγύησης - και, τέλος, ότι θα εκχωρήσετε σε κάθε άλλον παραλήπτη του Προγράμματος ένα αντίγραφο αυτής της ’δειας μαζί με το Πρόγραμμα.
+
+Έχετε δικαίωμα να επιβάλετε χρέωση για τη φυσική ενέργεια της μεταφοράς ενός αντιγράφου, καθώς και να παράσχετε, κατά την κρίση σας, προστασία εγγύησης με χρέωση.
+
+ 2. Επιτρέπεται η τροποποίηση του αντιγράφου ή των αντιγράφων του Προγράμματος ολόκληρου ή μέρους του, η οποία συνιστά συνεπώς δημιουργία ενός έργου βασισμένου στο Πρόγραμμα, και η διανομή αυτών των τροποποιήσεων ή έργων υπό τους όρους της Ενότητας 1 ως ανωτέρω, με την προϋπόθεση ότι και εσείς πληροίτε όλες τις παρακάτω προϋποθέσεις:
+
+ α) Πρέπει να φροντίζετε ώστε τα τροποποιημένα αρχεία να παρέχουν εμφανή σημειώματα στα οποία να δηλώνεται η τροποποίηση των αρχείων και η ημερομηνία τροποποίησης.
+
+ β) Πρέπει να φροντίζετε ώστε για κάθε έργο το οποίο διανέμετε ή δημοσιεύετε, και το οποίο περιέχει ή παράγεται από ολόκληρο ή μέρος του Προγράμματος, να παρέχεται άδεια χρήσης του, χωρίς χρέωση, σε όλα τα τρίτα μέρη, σύμφωνα με τους όρους αυτής της ’δειας.
+
+ γ) Εάν το τροποποιημένο πρόγραμμα διαβάζει εντολές αλληλεπιδραστικά, κατά την τυπική εκτέλεσή του, πρέπει να φροντίζετε ώστε, κατά την έναρξη τυπικής εκτέλεσής του για αυτήν την αλληλεπιδραστική χρήση, να εκτυπώνεται ή να εμφανίζεται στην οθόνη μια ανακοίνωση, η οποία θα περιλαμβάνει το απαραίτητο σημείωμα πνευματικής ιδιοκτησίας και ένα σημείωμα στο οποίο θα αναφέρεται ότι δεν υπάρχει καμιά εγγύηση (ή, αντίθετα, ότι παρέχετε εγγύηση) και ότι οι χρήστες έχουν τη δυνατότητα να αναδιανέμουν το πρόγραμμα σύμφωνα με τις προϋποθέσεις αυτές, καθώς και οδηγίες προς το χρήστη για τον τρόπο προβολής ενός αντιγράφου αυτής της ’δειας. (Εξαίρεση: εάν το ίδιο το Πρόγραμμα είναι αλληλεπιδραστικό αλλά κανονικά δεν εκτυπώνει αυτήν την ανακοίνωση, δεν απαιτείται από το έργο που δημιουργήσατε βασισμένοι στο Πρόγραμμα να εκτυπώνει ανακοίνωση.)
+
+Οι απαιτήσεις αυτές ισχύουν για ολόκληρο το τροποποιημένο έργο. Εάν συγκεκριμένες ενότητες του έργου αυτού δεν παράγονται από το Πρόγραμμα, και μπορούν να θεωρηθούν με ασφάλεια από μόνες τους ως ανεξάρτητα και ξεχωριστά έργα, τότε αυτή η ’δεια και οι όροι της δεν ισχύουν για τις ενότητες αυτές, κατά τη διανομή τους ως ξεχωριστά έργα. Αλλά όταν διανέμετε τις ίδιες ενότητες ως τμήματα ενός ευρύτερου έργου το οποίο βασίζεται στο Πρόγραμμα, η διανομή του συνόλου πρέπει να υπόκειται στους όρους της ’δειας, σύμφωνα με την οποία τα δικαιώματα των άλλων χρηστών εκτείνονται σε ολόκληρο το έργο, επομένως και σε καθένα χωριστό τμήμα του, ανεξάρτητα από το ποιος είναι ο δημιουργός του.
+
+Επομένως, πρόθεση αυτής της ενότητας δεν είναι να εγείρει δικαιώματα ή να αμφισβητήσει τα δικά σας δικαιώματα σε μια εργασία που δημιουργήσατε εξ ολοκλήρου οι ίδιοι - η πρόθεση, περισσότερο, είναι να ασκήσει το δικαίωμα ελέγχου της διανομής των παραγόμενων ή των συλλογικών έργων που βασίζονται στο Πρόγραμμα.
+
+Επιπλέον, η απλή προσθήκη ενός άλλου έργου, που δεν βασίζεται στο Πρόγραμμα, μαζί με το Πρόγραμμα (ή με ένα έργο που βασίζεται στο Πρόγραμμα) σε τόμο ενός μέσου αποθήκευσης ή διανομής, δεν υπάγει το άλλο έργο στα πλαίσια αυτής της ’δειας.
+
+ 3. Επιτρέπεται η αντιγραφή και διανομή του Προγράμματος (ή ενός έργου βασισμένο σε αυτό, σύμφωνα με την Ενότητα 2) σε μορφή αντικειμενικού κώδικα ή εκτελέσιμη μορφή, σύμφωνα με τους όρους των Ενοτήτων 1 και 2 ως ανωτέρω, με την προϋπόθεση ότι πραγματοποιείτε και μια από τις ακόλουθες ενέργειες:
+
+ α) Το συνοδεύετε με τον αντίστοιχο, πλήρη πηγαίο κώδικα, ο οποίος είναι αναγνώσιμος από το σύστημα και ο οποίος πρέπει να διανέμεται σύμφωνα με τους όρους των Ενοτήτων 1 και 2 παραπάνω, σε ένα συνηθισμένο μέσο μεταφοράς λογισμικού - ή,
+ β) Το συνοδεύετε με γραπτή προσφορά, ισχύουσα τουλάχιστον για τρία χρόνια και με χρέωση όχι μεγαλύτερη από το κόστος της φυσικής διανομής κώδικα, παράδοσης σε τρίτους του πλήρους, αναγνώσιμου από το σύστημα αντιγράφου του αντίστοιχου πηγαίου κώδικα, ο οποίος θα διανεμηθεί υπό τους όρους των Ενοτήτων 1 και 2 ως ανωτέρω, σε συνηθισμένο μέσο μεταφοράς λογισμικού - ή,
+
+ γ) Το συνοδεύετε με τις πληροφορίες που λάβατε όσον αφορά την προσφορά διανομής του αντίστοιχου πηγαίου κώδικα. (Η εναλλακτική αυτή επιλογή επιτρέπεται μόνο για μη εμπορική διανομή και μόνο εφόσον λάβατε το πρόγραμμα σε αντικειμενικό κώδικα ή εκτελέσιμη μορφή με αυτήν την προσφορά, σύμφωνα με την Υποενότητα [β] παραπάνω.)
+
+Ο πηγαίος κώδικας για ένα έργο συνιστά την προτιμώμενη μορφή του έργου για πραγματοποίηση τροποποιήσεων σε αυτό. Για ένα εκτελέσιμο έργο, πλήρης πηγαίος κώδικας σημαίνει όλον τον πηγαίο κώδικα για όλες τις λειτουργικές μονάδες που περιλαμβάνει, συν οποιαδήποτε σχετικά αρχεία ορισμού διασύνδεσης, συν τις δέσμες ενεργειών που χρησιμοποιούνται για τον έλεγχο της μεταγλώττισης και εγκατάστασης του εκτελέσιμου αρχείου. Ωστόσο, ως ειδική εξαίρεση, ο πηγαίος κώδικας που διανέμεται δεν χρειάζεται να περιλαμβάνει οτιδήποτε διανέμεται κανονικά (είτε ως κώδικας, είτε σε δυαδική μορφή) μαζί με τα μεγαλύτερα στοιχεία (μεταγλωττιστές, πυρήνας κ.ο.κ.) του λειτουργικού συστήματος στο οποίο εκτελείται το εκτελέσιμο αρχείο, εκτός εάν το ίδιο το στοιχείο συνοδεύει το εκτελέσιμο.
+
+Εάν η διανομή του εκτελέσιμου ή του αντικειμενικού κώδικα πραγματοποιείται με
+παραχώρηση πρόσβασης για αντιγραφή από καθορισμένη τοποθεσία, τότε η παραχώρηση
+ισοδύναμης πρόσβασης για αντιγραφή του πηγαίου κώδικα από την ίδια τοποθεσία λογίζεται ως διανομή του πηγαίου κώδικα - αν και τα τρίτα μέλη δεν
+υποχρεούνται να αντιγράψουν τον πηγαίο κώδικα μαζί με τον αντικειμενικό.
+
+ 4. Δεν επιτρέπεται η αντιγραφή, τροποποίηση, παραχώρηση άδειας περαιτέρω εκμετάλλευσης ή διανομή του Προγράμματος εκτός εάν προβλέπεται ρητά στην παρούσα ’δεια. Διαφορετικά, κάθε απόπειρα για αντιγραφή, τροποποίηση, παραχώρηση άδειας εκμετάλλευσης ή διανομή του Προγράμματος είναι άκυρη και αυτομάτως καταργεί τα δικαιώματα που σας παραχωρεί η παρούσα ’δεια.
+Ωστόσο, οι άδειες χρήσης των μελών που έχουν λάβει αντίγραφα ή δικαιώματα από εσάς, μέσω της παρούσας ’δειας, δεν θα ακυρωθούν, εφόσον τα μέλη αυτά
+παραμένουν πλήρως συμμορφωμένα με τους όρους της ’δειας.
+
+ 5. Δεν απαιτείται από εσάς να δεχθείτε την παρούσα ’δεια, εφόσον δεν την έχετε υπογράψει. Ωστόσο, τίποτε άλλο δεν σας δίνει το δικαίωμα να τροποποιήσετε ή να διανείμετε το Πρόγραμμα ή τα παραγόμενα από αυτό έργα. Οι ενέργειες αυτές απαγορεύονται από το νόμο, εάν δεν αποδεχθείτε την παρούσα ’δεια. Συνεπώς, με το να τροποποιήσετε ή να διανείμετε το Πρόγραμμα (ή οποιοδήποτε έργο που βασίζεται στο Πρόγραμμα), δηλώνετε ότι αποδέχεστε την παρούσα ’δεια, καθώς και όλους τους όρους και συνθήκες που προβλέπει η ’δεια για την αντιγραφή, διανομή ή τροποποίηση του Προγράμματος ή έργων που βασίζονται σε αυτό.
+
+ 6. Κάθε φορά που αναδιανέμετε το Πρόγραμμα (ή ένα έργο βασισμένο στο Πρόγραμμα), ο αποδέκτης αυτόματα παραλαμβάνει την αρχική άδεια αντιγραφής, διανομής ή τροποποίησης του Προγράμματος σύμφωνα με τους όρους και τις συνθήκες αυτές. Δεν επιτρέπεται να επιβάλλετε περαιτέρω περιορισμούς στην άσκηση των δικαιωμάτων του αποδέκτη τα οποία προβλέπονται εδώ. Δεν είστε υπεύθυνοι για το εάν τρίτα μέλη επιβάλλουν συμμόρφωση σε αυτήν τη ’δεια.
+
+ 7. Εάν, ως συνέπεια δικαστικής απόφασης ή κατηγορίας για παράβαση νόμου περί πνευματικής ιδιοκτησίας ή για οποιονδήποτε άλλο λόγο (μη περιοριζόμενο σε θέματα ευρεσιτεχνίας), σας επιβληθούν όροι (είτε μέσω δικαστικής απόφασης, συμφωνίας ή μέσω άλλου τρόπου) οι οποίοι αντιβαίνουν τους όρους της παρούσας ’δειας, οι όροι εκείνοι δεν σας απαλλάσσουν από τους όρους της παρούσας. Εάν δεν είναι δυνατή η αναδιανομή με τρόπο ώστε να ικανοποιεί συγχρόνως τις υποχρεώσεις σας σύμφωνα με την παρούσα ’δεια και οποιεσδήποτε άλλες υποχρεώσεις απορρέουν από αυτή, τότε, ως συνέπεια, δεν επιτρέπεται να αναδιανέμετε το Πρόγραμμα με κανένα τρόπο. Για παράδειγμα, εάν μια άδεια ευρεσιτεχνίας δεν επιτρέπει τη χωρίς δικαιώματα εκμετάλλευσης αναδιανομή του Προγράμματος από όλους όσους λαμβάνουν αντίγραφα άμεσα ή έμμεσα από εσάς, τότε ο μόνος τρόπος με τον οποίο θα μπορούσατε να ικανοποιήσετε την άδεια εκείνη και την παρούσα ’δεια θα ήταν να αποφύγετε εντελώς την αναδιανομή του Προγράμματος.
+
+Εάν οποιοδήποτε τμήμα αυτής της ενότητας καταστεί άκυρο ή μη δυνάμενο να επιβληθεί σε κάποια συγκεκριμένη περίπτωση, το υπόλοιπο τμήμα της ενότητας αυτής εφαρμόζεται και η ενότητα ως σύνολο εφαρμόζεται υπό οποιεσδήποτε συγκυρίες.
+
+Δεν ανήκει στους σκοπούς της ενότητας αυτής να σας παρακινήσει να παραβιάσετε
+την ευρεσιτεχνία ή άλλες αξιώσεις πνευματικής ιδιοκτησίας ή να αμφισβητήσετε τον κύρος οποιωνδήποτε τέτοιων αξιώσεων. Μοναδικός σκοπός αυτής της ενότητας είναι να προστατέψει την ακεραιότητα του συστήματος διανομής ελεύθερου λογισμικού, η οποία υλοποιείται μέσω της πρακτικής των αδειών δημόσιας χρήσης. Πολλοί άνθρωποι έχουν συνεισφέρει γενναιόδωρα στην ευρεία έκταση του λογισμικού που διανέμεται μέσω αυτού του συστήματος, εμπιστευόμενοι την συνεπή εφαρμογή αυτού του συστήματος. Είναι στην ευχέρεια του δημιουργού/δωρητή να αποφασίσει εάν προτίθεται να διανείμει λογισμικό μέσω οποιουδήποτε άλλου συστήματος, και μια άδεια δεν είναι δυνατό να επιβάλει αυτήν την επιλογή.
+
+Η ενότητα αυτή έχει ως σκοπό να καταστήσει σαφές ό,τι συνεπάγεται το υπόλοιπο τμήμα της παρούσας ’δειας.
+
+ 8. Εάν η διανομή ή/και η χρήση του Προγράμματος εμποδίζεται σε ορισμένες χώρες, είτε μέσω κατοχυρωμένης ευρεσιτεχνίας είτε μέσω διασυνδέσεων που προστατεύονται από πνευματικά δικαιώματα, επιτρέπεται στον κάτοχο του αρχικού πνευματικού δικαιώματος, ο οποίος θέτει το Πρόγραμμα υπό τους όρους της παρούσας ’δειας, να προσθέσει έναν ρητό γεωγραφικό περιορισμό στη διανομή, εξαιρώντας εκείνες τις χώρες, έτσι ώστε η διανομή να επιτρέπεται μόνο για τις χώρες οι οποίες δεν εξαιρούνται. Σε τέτοια περίπτωση, η παρούσα ’δεια ενσωματώνει τον περιορισμό σαν να ήταν διατυπωμένος στο σώμα της παρούσας ’δειας.
+
+ 9. Το Ίδρυμα Ελεύθερου Λογισμικού (Free Software Foundation) έχει τη δυνατότητα περιστασιακά να δημοσιεύει αναθεωρημένες ή/και νέες εκδόσεις της Γενικής ’δειας Δημόσιας Χρήσης. Αυτές οι νέες εκδόσεις θα είναι συναφείς στο πνεύμα με την παρούσα έκδοση, όμως ενδέχεται να διαφέρουν στις λεπτομέρειες,
+καθώς αναφέρονται σε νέα προβλήματα και ζητήματα.
+
+Σε κάθε έκδοση δίνεται ένας διακριτικός αριθμός έκδοσης. Εάν στο Πρόγραμμα
+καθορίζεται ένας αριθμός έκδοσης της παρούσας ’δειας, η οποία ισχύει σε αυτό, καθώς και &quot;οποιασδήποτε μεταγενέστερης έκδοσης&quot;, μπορείτε να επιλέξετε ανάμεσα στο να ακολουθήσετε τους όρους και τις συνθήκες είτε εκείνης της έκδοσης είτε οποιασδήποτε άλλης έκδοσης που δημοσιεύεται από το Ίδρυμα Ελεύθερου Λογισμικού (Free Software Foundation). Εάν στο Πρόγραμμα δεν καθορίζεται αριθμός έκδοσης
+της παρούσας ’δειας, μπορείτε να επιλέξετε οποιαδήποτε έκδοση η οποία έχει δημοσιευθεί από το Ίδρυμα Ελεύθερου Λογισμικού.
+
+ 10. Εάν επιθυμείτε να ενσωματώσετε μέρη του Προγράμματος σε άλλα ελεύθερα
+προγράμματα, των οποίων οι όροι διανομής είναι διαφορετικοί, επικοινωνήστε με το δημιουργό του Προγράμματος για να ζητήσετε την έγκρισή του. Για λογισμικό του οποίου η πνευματική ιδιοκτησία ανήκει στο Ίδρυμα Ελεύθερου Λογισμικού (Free Software Foundation), επικοινωνήστε μαζί μας στο Ίδρυμα Ελεύθερου Λογισμικού (σε ορισμένες περιπτώσεις προβαίνουμε σε εξαιρέσεις). Η απόφασή μας θα ληφθεί βάσει του διττού στόχου μας να διατηρήσουμε την ελευθερία όλων των προϊόντων που παράγονται από το ελεύθερο λογισμικό μας, καθώς και να προωθήσουμε γενικότερα την κοινή χρήση και τη δυνατότητα επαναχρησιμοποίησης του λογισμικού.
+
+ ΚΑΜΙΑ ΕΓΓΥΗΣΗ
+
+ 11. ΕΠΕΙΔΗ Η ΑΔΕΙΑ ΧΡΗΣΗΣ ΤΟΥ ΠΡΟΓΡΑΜΜΑΤΟΣ ΠΑΡΕΧΕΤΑΙ ΧΩΡΙΣ ΧΡΕΩΣΗ, ΔΕΝ ΥΠΑΡΧΕΙ ΕΓΓΥΗΣΗ ΓΙΑ ΤΟ ΠΡΟΓΡΑΜΜΑ, ΣΤΟ ΒΑΘΜΟ ΠΟΥ ΕΠΙΤΡΕΠΕΙ Η ΙΣΧΥΟΥΣΑ ΝΟΜΟΘΕΣΙΑ. ΕΦΟΣΟΝ ΔΕΝ ΥΠΑΡΧΕΙ ΔΙΑΦΟΡΕΤΙΚΗ ΕΓΓΡΑΦΗ ΔΗΛΩΣΗ, ΟΙ ΚΑΤΟΧΟΙ ΠΝΕΥΜΑΤΙΚΩΝ ΔΙΚΑΙΩΜΑΤΩΝ Ή/ΚΑΙ ΑΛΛΕΣ ΠΛΕΥΡΕΣ ΠΑΡΕΧΟΥΝ ΤΟ ΠΡΟΓΡΑΜΜΑ &quot;ΩΣ ΕΧΕΙ&quot; ΧΩΡΙΣ ΚΑΝΕΝΟΣ ΕΙΔΟΥΣ ΕΓΓΥΗΣΕΙΣ, ΕΙΤΕ ΡΗΤΕΣ ΕΙΤΕ ΕΜΜΕΣΕΣ, ΣΤΙΣ ΟΠΟΙΕΣ ΣΥΜΠΕΡΙΛΑΜΒΑΝΟΝΤΑΙ, ΕΝΔΕΙΚΤΙΚΑ, ΟΙ ΕΜΜΕΣΕΣ ΕΓΓΥΗΣΕΙΣ ΕΜΠΟΡΕΥΣΙΜΟΤΗΤΑΣ ΚΑΙ ΚΑΤΑΛΛΗΛΟΤΗΤΑΣ. ΟΠΟΙΟΣΔΗΠΟΤΕ ΚΙΝΔΥΝΟΣ ΑΠΟ ΤΗΝ ΠΟΙΟΤΗΤΑ ΚΑΙ ΤΗΝ ΑΠΟΔΟΣΗ ΤΟΥ ΠΡΟΓΡΑΜΜΑΤΟΣ ΑΝΗΚΕΙ ΕΞ ΟΛΟΚΛΗΡΟΥ ΕΣΑΣ. ΕΑΝ ΤΟ ΠΡΟΓΡΑΜΜΑ ΑΠΟΔΕΙΧΘΕΙ ΕΛΑΤΤΩΜΑΤΙΚΟ, ΤΟ ΚΟΣΤΟΣ ΟΛΩΝ ΤΩΝ ΕΡΓΑΣΙΩΝ ΕΠΙΣΚΕΥΗΣ Ή ΔΙΟΡΘΩΣΗΣ ΒΑΡΥΝΕΙ ΕΣΑΣ.
+
+ 12. ΣΕ ΚΑΜΙΑ ΠΕΡΙΠΤΩΣΗ, ΕΚΤΟΣ ΕΑΝ ΑΠΑΙΤΕΙΤΑΙ ΑΠΟ ΤΗΝ ΙΣΧΥΟΥΣΑ ΝΟΜΟΘΕΣΙΑ Ή ΕΧΕΙ ΣΥΜΦΩΝΗΘΕΙ ΓΡΑΠΤΩΣ, Ο ΚΑΤΟΧΟΣ ΤΩΝ ΠΝΕΥΜΑΤΙΚΩΝ ΔΙΚΑΙΩΜΑΤΩΝ, Ή ΟΠΟΙΟΔΗΠΟΤΕ ΑΛΛΟ ΜΕΛΟΣ ΤΟ ΟΠΟΙΟ ΜΠΟΡΕΙ ΝΑ ΤΡΟΠΟΠΟΙΗΣΕΙ Ή/ΚΑΙ ΝΑ ΑΝΑΔΙΑΝΕΙΜΕΙ ΤΟ ΠΡΟΓΡΑΜΜΑ ΟΠΩΣ ΠΡΟΒΛΕΠΕΤΑΙ ΠΑΡΑΠΑΝΩ, ΔΕΝ ΦΕΡΕΤΑΙ ΩΣ ΥΠΕΥΘΥΝΟΣ ΑΠΕΝΑΝΤΙ ΣΑΣ ΓΙΑ ΖΗΜΙΕΣ, ΣΥΜΠΕΡΙΛΑΜΒΑΝΟΜΕΝΩΝ ΟΛΩΝ ΤΩΝ ΓΕΝΙΚΩΝ, ΕΙΔΙΚΩΝ, ΣΥΜΠΤΩΜΑΤΙΚΩΝ Ή ΣΥΝΕΠΑΚΟΛΟΥΘΩΝ ΖΗΜΙΩΝ ΠΟΥ ΕΝΔΕΧΕΤΑΙ ΝΑ ΠΡΟΚΥΨΟΥΝ ΛΟΓΩ ΤΗΣ ΧΡΗΣΗΣ Ή ΤΗΣ ΑΔΥΝΑΜΙΑΣ ΧΡΗΣΗΣ ΤΟΥ ΠΡΟΓΡΑΜΜΑΤΟΣ (ΣΥΜΠΕΡΙΛΑΜΒΑΝΟΜΕΝΩΝ, ΕΝΔΕΙΚΤΙΚΑ, ΤΗΣ ΑΠΩΛΕΙΑΣ ΔΕΔΟΜΕΝΩΝ Ή ΤΗΣ ΑΛΛΟΙΩΣΗΣ ΤΗΣ ΑΚΡΙΒΕΙΑΣ ΤΟΥΣ, Ή ΑΠΩΛΕΙΑΣ ΠΟΥ ΕΠΗΛΘΕ ΑΠΟ ΕΣΑΣ Ή ΑΠΟ ΤΡΙΤΑ ΜΕΛΗ, Ή ΑΔΥΝΑΜΙΑΣ ΤΟΥ ΠΡΟΓΡΑΜΜΑΤΟΣ ΝΑ ΛΕΙΤΟΥΡΓΗΣΕΙ ΜΕ ΑΛΛΑ ΠΡΟΓΡΑΜΜΑΤΑ), ΕΣΤΩ ΚΑΙ ΑΝ Ο ΚΑΤΟΧΟΣ ΑΥΤΟΣ Ή ΤΟ ΑΛΛΟ ΜΕΛΟΣ ΕΧΕΙ ΕΝΗΜΕΡΩΘΕΙ ΓΙΑ ΤΟ ΕΝΔΕΧΟΜΕΝΟ ΤΕΤΟΙΩΝ ΖΗΜΙΩΝ.
+
+
+ ΤΕΛΟΣ ΤΩΝ ΟΡΩΝ ΚΑΙ ΤΩΝ ΣΥΝΘΗΚΩΝ
+
+
+ Τρόπος εφαρμογής των όρων στα νέα Προγράμματα
+
+ Εάν αναπτύσσετε ένα νέο πρόγραμμα και θέλετε να έχει τη μεγαλύτερη δυνατή
+χρήση από το κοινό, ο καλύτερος τρόπος να επιτύχετε αυτό είναι να το χαρακτηρίσετε ελεύθερο λογισμικό, το οποίο όλοι θα μπορούν να αναδιανέμουν και να τροποποιούν υπό τους όρους αυτούς.
+ Για να γίνει αυτό, συνάψτε το ακόλουθο σημείωμα στο πρόγραμμα. Είναι πιο ασφαλές να το συνάψετε στην αρχή κάθε αρχείου πηγαίου κώδικα, ώστε να φέρεται πιο αποτελεσματικά η εξαίρεση της εγγύησης - και κάθε αρχείο θα πρέπει να διαθέτει τουλάχιστον τη γραμμή &quot;πνευματικής ιδιοκτησίας&quot; και έναν δείκτη για το που βρίσκεται το πλήρες σημείωμα.
+
+ &lt;μια γραμμή που παρέχει το όνομα του προγράμματος και μια σύντομη περιγραφή της λειτουργίας του.&gt;
+ Copyright (C) &lt;έτος&gt; &lt;όνομα δημιουργού&gt;
+
+ Το πρόγραμμα αυτό είναι ελεύθερο λογισμικό. Επιτρέπεται η αναδιανομή ή/και τροποποίησή του υπό τους όρους της Γενικής ’δειας Δημόσιας Χρήσης GNU (GNU General Public License), όπως αυτή δημοσιεύεται από το Ίδρυμα Ελεύθερου Λογισμικού (Free Software Foundation) - είτε της έκδοσης 2 της ’δειας, είτε (κατ' επιλογήν) οποιασδήποτε μεταγενέστερης έκδοσης.
+
+ Το πρόγραμμα αυτό διανέμεται με την ελπίδα ότι θα αποδειχθεί χρήσιμο, παρόλα αυτά ΧΩΡΙΣ ΚΑΜΙΑ ΕΓΓΥΗΣΗ - χωρίς ακόμη και την έμμεση εγγύηση ΕΜΠΟΡΕΥΣΙΜΟΤΗΤΑΣ ή ΚΑΤΑΛΛΗΛΟΤΗΤΑΣ. Για περισσότερες λεπτομέρειες ανατρέξτε στη Γενική ’δεια Δημόσιας Χρήσης GNU (GNU General Public License).
+
+ Θα πρέπει να έχετε λάβει ένα αντίγραφο της Γενικής ’δειας Δημόσιας Χρήσης GNU (GNU General Public License) μαζί με αυτό το πρόγραμμα. Εάν όχι, επικοινωνήστε γραπτώς με το Ίδρυμα Ελεύθερου Λογισμικού (Free Software Foundation), Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Επίσης, προσθέστε πληροφορίες για τον τρόπο με τον οποίο μπορεί κάποιος να επικοινωνήσει μαζί σας μέσω ηλεκτρονικού ή παραδοσιακού ταχυδρομείου.
+
+Εάν το πρόγραμμα είναι αλληλεπιδραστικό, φροντίστε ώστε κατά την εκκίνησή του σε αλληλεπιδραστική λειτουργία να εμφανίζει ένα σύντομο σημείωμα όπως το παρακάτω:
+
+ &lt;Όνομα_προγράμματος&gt; έκδοση &lt;αριθμός_έκδοσης&gt;, Copyright (C) &lt;έτος&gt; &lt;όνομα_δημιουργού&gt;
+ Το &lt;όνομα_προγράμματος&gt; διανέμεται ΧΩΡΙΣ ΚΑΜΙΑ ΕΓΓΥΗΣΗ. Για λεπτομέρειες πληκτρολογήστε `show w'.
+ Το λογισμικό αυτό είναι ελεύθερο, και η αναδιανομή του είναι ευπρόσδεκτη υπό συγκεκριμένους όρους. Πληκτρολογήστε `show c' για λεπτομέρειες.
+
+Οι υποθετικές εντολές `show w' και `show c' θα πρέπει να προβάλλουν τις αντίστοιχες ενότητες της Γενικής ’δειας Δημόσιας Χρήσης. Ασφαλώς οι εντολές που χρησιμοποιείτε δεν είναι απαραίτητο να ονομάζονται `show w' και `show c'. Μπορούν να είναι ακόμη και σύνδεσμοι που ενεργοποιούνται με πάτημα του ποντικιού ή στοιχεία μενού--οτιδήποτε ταιριάζει με το πρόγραμμά σας.
+
+Εάν κρίνετε απαραίτητο, θα πρέπει επίσης να ζητήσετε από τον εργοδότη σας (εάν εργάζεστε ως προγραμματιστής) ή τη σχολή σας, εάν υπάρχουν, να υπογράψουν μια &quot;δήλωση αποκήρυξης πνευματικών δικαιωμάτων&quot; (copyright disclaimer) για το πρόγραμμα.
+Μπορείτε να ακολουθήσετε το παρακάτω δείγμα, συμπληρώνοντας τα ονόματα:
+
+ Η &lt;επωνυμία_εταιρίας/σχολής&gt; αποκηρύσσει οποιοδήποτε δικαίωμα πνευματικής ιδιοκτησίας επί του προγράμματος &lt;όνομα_προγράμματος&gt;', το οποίο αποτελεί δημιουργία του &lt;όνομα_δημιουργού&gt;.
+
+ &lt;υπογραφή_εκπροσώπου_εταιρίας/σχολής&gt;, &lt;ημερομηνία, έτος&gt;
+
+
+Η παρούσα Γενική ’δεια Δημόσιας Χρήσης δεν επιτρέπει την ενσωμάτωση του προγράμματός σας σε ιδιόκτητα προγράμματα. Εάν το πρόγραμμά σας αποτελεί βιβλιοθήκη υπορουτίνας, θα είναι ενδεχομένως χρησιμότερο να επιτρέπετε σύνδεση ιδιόκτητων εφαρμογών με τη βιβλιοθήκη. Εάν όντως αυτό επιθυμείτε, χρησιμοποιήστε τη Γενική ’δεια Δημόσιας Χρήσης Βιβλιοθήκης GNU (GNU Library General Public License) αντί της παρούσας ’δειας.
+</PRE>
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/logo/src/xlogo/gpl/gpl-en.html b/logo/src/xlogo/gpl/gpl-en.html
new file mode 100644
index 0000000..f404388
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-en.html
@@ -0,0 +1,499 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+ <title></title>
+
+
+</head>
+
+
+<body>
+
+<h3>Table of Contents</h3>
+
+<ul>
+
+ <li><a name="TOC1" href="#SEC1">GNU GENERAL
+PUBLIC LICENSE</a></li>
+
+ <li>
+ <ul>
+
+ <li><a name="TOC2" href="#SEC2">Preamble</a></li>
+
+ <li><a name="TOC3" href="#SEC3">TERMS AND
+CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></li>
+
+ <li><a name="TOC4" href="#SEC4">How to
+Apply These Terms to Your New Programs</a></li>
+
+ </ul>
+
+ </li>
+
+</ul>
+
+<hr>
+<h3><a name="SEC1" href="#TOC1">GNU GENERAL
+PUBLIC LICENSE</a></h3>
+
+<p>
+Version 2, June 1991
+</p>
+
+<pre>Copyright (C) 1989, 1991 Free Software Foundation, Inc. <br>51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA<br><br>Everyone is permitted to copy and distribute verbatim copies<br>of this license document, but changing it is not allowed.<br></pre>
+
+<h3><a name="SEC2" href="#TOC2">Preamble</a></h3>
+
+<p> The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+</p>
+
+<p> When we speak of free software, we are referring to freedom,
+not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+</p>
+
+<p> To protect your rights, we need to make restrictions that
+forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+</p>
+
+<p> For example, if you distribute copies of such a program,
+whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+</p>
+
+<p> We protect your rights with two steps: (1) copyright the
+software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+</p>
+
+<p> Also, for each author's protection and ours, we want to make
+certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+</p>
+
+<p> Finally, any free program is threatened constantly by
+software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+</p>
+
+<p> The precise terms and conditions for copying, distribution
+and
+modification follow.
+</p>
+
+<h3><a name="SEC3" href="#TOC3">TERMS AND
+CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h3>
+
+<p>
+<strong>0.</strong> This License applies to any program or
+other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+</p>
+
+<p>
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+</p>
+
+<p>
+<strong>1.</strong> You may copy and distribute verbatim
+copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+</p>
+
+<p>
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+</p>
+
+<p>
+<strong>2.</strong> You may modify your copy or copies of
+the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+</p>
+
+<dl>
+
+ <dt></dt>
+
+ <dd> <strong>a)</strong> You must cause the
+modified files to carry prominent notices stating that you changed the
+files and the date of any change. </dd>
+
+ <dt></dt>
+
+ <dd> <strong>b)</strong> You must cause any work
+that you distribute or publish, that in whole or in part contains or is
+derived from the Program or any part thereof, to be licensed as a whole
+at no charge to all third parties under the terms of this License. </dd>
+
+ <dt></dt>
+
+ <dd> <strong>c)</strong> If the modified program
+normally reads commands interactively when run, you must cause it, when
+started running for such interactive use in the most ordinary way, to
+print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you
+provide a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but does not
+normally print such an announcement, your work based on the Program is
+not required to print an announcement.) </dd>
+
+</dl>
+
+<p>
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+</p>
+
+<p>
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+</p>
+
+<p>
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+</p>
+
+<p>
+<strong>3.</strong> You may copy and distribute the Program
+(or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+</p>
+
+<!-- we use this doubled UL to get the sub-sections indented, -->
+<!-- while making the bullets as unobvious as possible. -->
+<dl>
+
+ <dt></dt>
+
+ <dd> <strong>a)</strong> Accompany it with the
+complete corresponding machine-readable source code, which must be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or, </dd>
+
+ <dt></dt>
+
+ <dd> <strong>b)</strong> Accompany it with a
+written offer, valid for at least three years, to give any third party,
+for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding
+source code, to be distributed under the terms of Sections 1 and 2
+above on a medium customarily used for software interchange; or, </dd>
+
+ <dt></dt>
+
+ <dd> <strong>c)</strong> Accompany it with the
+information you received as to the offer to distribute corresponding
+source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or
+executable form with such an offer, in accord with Subsection b above.)
+ </dd>
+
+</dl>
+
+<p>
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+</p>
+
+<p>
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+</p>
+
+<p>
+<strong>4.</strong> You may not copy, modify, sublicense,
+or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+</p>
+
+<p>
+<strong>5.</strong> You are not required to accept this
+License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+</p>
+
+<p>
+<strong>6.</strong> Each time you redistribute the Program
+(or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+</p>
+
+<p>
+<strong>7.</strong> If, as a consequence of a court
+judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+</p>
+
+<p>
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+</p>
+
+<p>
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+</p>
+
+<p>
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+</p>
+
+<p>
+<strong>8.</strong> If the distribution and/or use of the
+Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+</p>
+
+<p>
+<strong>9.</strong> The Free Software Foundation may
+publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail
+to
+address new problems or concerns.
+</p>
+
+<p>
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and
+conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number
+of
+this License, you may choose any version ever published by the Free
+Software
+Foundation.
+</p>
+
+<p>
+<strong>10.</strong> If you wish to incorporate parts of
+the Program into other free
+programs whose distribution conditions are different, write to the
+author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we
+sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software
+and
+of promoting the sharing and reuse of software generally.
+</p>
+
+<p><strong>NO WARRANTY</strong></p>
+
+<p>
+<strong>11.</strong> BECAUSE THE PROGRAM IS LICENSED FREE
+OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
+AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
+SERVICING,
+REPAIR OR CORRECTION.
+</p>
+
+<p>
+<strong>12.</strong> IN NO EVENT UNLESS REQUIRED BY
+APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT
+LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED
+BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+</p>
+
+<h3>END OF TERMS AND CONDITIONS</h3>
+
+<h3><a name="SEC4" href="#TOC4">How to
+Apply These Terms to Your New Programs</a></h3>
+
+<p> If you develop a new program, and you want it to be of the
+greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+</p>
+
+<p> To do so, attach the following notices to the program. It is
+safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+</p>
+
+<pre><var>one line to give the program's name and an idea of what it does.</var><br>Copyright (C) <var>yyyy</var> <var>name of author</var><br><br>This program is free software; you can redistribute it and/or<br>modify it under the terms of the GNU General Public License<br>as published by the Free Software Foundation; either version 2<br>of the License, or (at your option) any later version.<br><br>This program is distributed in the hope that it will be useful,<br>but WITHOUT ANY WARRANTY; without even the implied warranty of<br>MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>GNU General Public License for more details.<br><br>You should have received a copy of the GNU General Public License<br>along with this program; if not, write to the Free Software<br>Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.<br></pre>
+
+<p>
+Also add information on how to contact you by electronic and paper
+mail.
+</p>
+
+<p>
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+</p>
+
+<pre>Gnomovision version 69, Copyright (C) <var>year</var> <var>name of author</var><br>Gnomovision comes with ABSOLUTELY NO WARRANTY; for details<br>type `show w'. This is free software, and you are welcome<br>to redistribute it under certain conditions; type `show c' <br>for details.<br></pre>
+
+<p>
+The hypothetical commands <samp>`show w'</samp> and <samp>`show
+c'</samp> should show
+the appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than <samp>`show
+w'</samp> and
+<samp>`show c'</samp>; they could even be mouse-clicks or
+menu items--whatever
+suits your program.
+</p>
+
+<p>
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+</p>
+
+<pre>Yoyodyne, Inc., hereby disclaims all copyright<br>interest in the program `Gnomovision'<br>(which makes passes at compilers) written <br>by James Hacker.<br><br><var>signature of Ty Coon</var>, 1 April 1989<br>Ty Coon, President of Vice<br></pre>
+
+<p>
+This General Public License does not permit incorporating your program
+into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with
+the
+library. If this is what you want to do, use the GNU Lesser General Public License
+instead of this License.
+</p>
+
+</body>
+</html>
diff --git a/logo/src/xlogo/gpl/gpl-eo.html b/logo/src/xlogo/gpl/gpl-eo.html
new file mode 100644
index 0000000..86f91db
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-eo.html
@@ -0,0 +1,714 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+ <title>GNU General Public Licence</title>
+
+
+</head>
+
+
+<body>
+
+<pre><b><br> Note about this translation of Avizo pri &#265;i tiu traduko de la<br> GNU General Public License GNUa &#284;enerala Publika Permesilo<br></b><br>This is an unofficial translation &#264;i tiu esperantigo de la &laquo;GNUa<br>of the GNU General Public License &#284;enerala publika permesilo&raquo; ne estas<br>into Esperanto. It was not oficiala. &#284;in ne publikigis &laquo;Free<br>published by the Free Software Software Foundation&raquo; kaj &#285;i ne estas<br>Foundation, and does not legally jura dokumento determinanta<br>state the distribution terms for kondi&#265;ojn por distribuado de<br>software that uses the GNU General programaro la&#365; la &laquo;GNUa &#284;enerala<br>Public License &ndash; only the Publika Permesilo&raquo; &ndash; nur la<br>original English text of the GNU originala anglalingva teksto validas<br>GPL does that. However, we hope por tio. Tamen espereble la traduko<br>that this translation will help helpos la Esperanto-parolantojn pli<br>Esperanto speakers understand the bone kompreni la enhavon de la &laquo;GNUa<br>GNU General Public License better. &#284;enerala publika permesilo&raquo;.<br><br>You can find the official text of La oficialan tekston de la GNUa<br>the GNU General Public License at &#284;enerala Publika Permesilo vidu &#265;e<br> <a target="_blank" href="gpl-en.html">http://www.gnu.org/copyleft/gpl.html</a><br>Translated by Sergei B. Pokrovsky. Elangligis Sergio Pokrovskij.<br></pre>
+
+<h1> GNU General Public License / GNUa &#284;enerala Publika Permesilo
+</h1>
+
+<h2> Esperantigo de la versio 2 (Junio 1991)
+</h2>
+
+<blockquote> Copyright &copy; 1989, 1991, Free Software
+Foundation Inc. <br>
+
+59 Temple Place, Suite 330, Boston, MA 02111-1307 <br>
+
+Usono
+ <p> &#264;iu rajtas reprodukti kaj disvastigi ne&#349;an&#285;itajn kopiojn de
+&#265;i tiu Permesilo; sed &#349;an&#285;i &#285;in oni ne rajtas. </p>
+
+</blockquote>
+
+<h2> Anta&#365;parolo
+</h2>
+
+<p>
+La pliparto de la permesiloj pri uzo de programo celas limigi vian
+liberon &#285;in kunhavigi kaj modifi. Male, la GNUa &#284;enerala Publika
+Permesilo celas garantii vian liberon kunhavigi kaj modifi liberan
+programaron &ndash; certigi ke la programaro estu libera por &#265;iuj
+uzantoj. &#264;i
+tiu &#284;enerala Publika Permesilo validas por pliparto da programaro de
+Free
+Software Foundation (la Fonda&#309;o de Libera Programaro) kaj por multaj
+aliaj programoj kies a&#365;toroj volas &#285;in uzi. (Por alia parto de la
+programaro de Free Software Foundation validas, anstata&#365;e,
+&laquo;GNU Library
+General Public License&raquo;, la <a name="re-lgpl" href="#lgpl">GNUa
+&#284;enerala Publika Permesilo Biblioteka [1]</a>.)
+Anka&#365; vi povas apliki &#285;in al viaj programoj.
+</p>
+
+<p>
+Kiam ni parolas pri liberaj programoj, ja temas pri libero, ne pri
+prezo.
+Niaj &#284;eneralaj Publikaj Permesiloj estas faritaj por disponigi al vi la
+liberon
+</p>
+
+<ul>
+
+ <li> <a name="re-distribui" href="#distribui">distribui
+[2]</a> ekzemplerojn de liberaj programverkoj (kaj postuli pagon
+por ili, se vi volas);</li>
+
+ <li> ricevi la fontotekstojn a&#365; povi preni ilin, se vi deziras;</li>
+
+ <li> modifi la programojn a&#365; uzi pecojn el ili en novaj liberaj
+programoj;</li>
+
+ <li> kaj scii ke vi rajtas fari tiojn.</li>
+
+</ul>
+
+<p>
+Por protekti viajn rajtojn ni devas enkonduki limigojn kiuj malebligu
+al
+iu ajn rifuzi al vi tiujn rajtojn a&#365; proponi al vi cedi ilin. Tiuj
+limigoj esprimi&#285;as en certaj devigoj por vi, se vi distribuas
+ekzemplerojn de programo a&#365; se vi modifas ilin.
+</p>
+
+<p>
+Ekzemple, se vi distribuas ekzemplerojn de tia programo, &#265;u senpage a&#365;
+kontra&#365; pago, vi devas pludoni al la ricevantoj &#265;iujn rajtojn je &#285;i,
+kiujn vi havas. Vi devas certigi ke anka&#365; la novaj posedantoj ricevu a&#365;
+povu havigi al si la fontotekstojn. Kaj vi devas informi ilin pri la
+kondi&#265;oj, por ke ili sciu siajn rajtojn.
+</p>
+
+<p>
+Ni protektas viajn rajtojn en du pa&#349;oj:
+</p>
+
+<ol>
+
+ <li> {ni} rezervas {al ni} la <a href="#copy" name="re-copy">a&#365;torrajton [3]</a> je la programoj, kaj</li>
+
+ <li> ni disponigas al vi &#265;i tiun Permesilon, kiu donas al vi
+juran eblon <a href="#prod" name="re-prod">reprodukti [4]</a>, distribui kaj/a&#365;
+modifi la programojn.</li>
+
+</ol>
+
+<p>
+Krome, por protekti la a&#365;torojn kaj nin mem, ni sciigas al &#265;iuj ke
+estas
+nenia garantio pri la libera programaro. Personoj akirintaj programon
+modifitan per tria partio sciu, ke ili ricevis ne la originalon, kaj
+tial
+la problemoj ka&#365;zitaj de triapartiaj &#349;an&#285;oj ne kompromitu la reputacion
+la originala a&#365;toro.
+</p>
+
+<p>
+Fine, &#265;iun liberan programon konstante minacas
+<a href="#pat" name="re-pat">programa patento [5]</a>.
+Ni malvolas ke perantoj de libera programo individue akiru patenton je
+&#285;i, per tio fakte farante la programon
+<a href="#propr" name="re-propr">proprieta [6]</a>. Por malebligi tion
+ni klarigis, ke patento estu koncesiita al &#265;ies ajn libera uzo
+&ndash; a&#365; tute
+ne estu koncesiita.
+</p>
+
+<p>
+Nun sekvas la precizaj kondi&#265;oj pri reproduktado, distribuado kaj
+modifado.
+</p>
+
+<h2> La kondi&#265;oj pri reproduktado, distribuado kaj modifado
+<br>
+
+la&#365; la GNUa &#284;enerala Publika Permesilo
+</h2>
+
+<p>
+&sect;0. &#264;i tiu Permesilo validas por ajna programo a&#365; alia verko
+entenanta
+averton enmetitan de la posedanto de la a&#365;torrajto, dirantan ke &#285;i
+estas
+distribuebla la&#365; la kondi&#265;oj de &#265;i tiu &#284;enerala Publika Permesilo.
+&#264;i-sube &laquo;Programo&raquo; indikas &#265;ian tian programon a&#365;
+verkon, kaj &laquo;verko
+bazita sur la Programo&raquo; estas a&#365; la Programo mem, a&#365; ia ajn
+derivita
+verko la&#365; la difino de la le&#285;o pri la a&#365;torrajto
+<a href="#deriv" name="re-deriv">[7]</a>: t.e. verko entenata
+la Programon a&#365; ties pecon, &#265;u modifitan a&#365; ne, kaj/a&#365; tradukitan en
+alian lingvon. (&#264;i tie kaj sube la termino <dfn>modifo</dfn>
+kovras, senrezerve,
+tradukojn la&#365; la plej &#285;enerala senco.) &#264;i-sube <dfn>vi</dfn>
+indikas
+koncesiulon de la Permesilo.
+</p>
+
+<p>
+&#264;i tiu Permesilo ne koncernas realigon de la ceteraj rajtoj, aliaj ol
+reproduktado, distribuado kaj modifado. Rulado de la Programo ne estas
+limigita, kaj ties eliga&#309;o estas koncernata nur se &#285;ia enhavo
+konstituas
+verkon bazitan sur la Programo (alie ol pro la fakto ke &#285;i estas
+rezulto
+de rulo de la Programo). &#264;u tio validas a&#365; ne, dependas de la funkcio
+de
+la Programo.
+</p>
+
+<p>
+&sect;1. Vi rajtas sen&#349;an&#285;e reprodukti kaj distribui ekzemplerojn
+de la
+fontoteksto de la Programo tia kia vi &#285;in ricevis, sur ajna
+datumportilo,
+kondi&#265;e ke
+</p>
+
+<ul>
+
+ <li> &#265;iu ekzemplero havu klare videblan kaj ta&#365;gan avizon pri
+la a&#365;torrajto (copyright notice) kaj malgarantiilon (disclaimer of
+warranty);</li>
+
+ <li> &#265;iuj avizoj pri &#265;i tiu Permesilo kaj malgarantiiloj restas
+netu&#349;itaj;</li>
+
+ <li> kun la ekzemplero de la Programo la akiranto ricevu kopion
+de &#265;i tiu Permesilo.</li>
+
+</ul>
+
+<p>
+Vi rajtas fakturi la fizikan agon por liverado de ekzemplero de la
+Programo, kaj la&#365;vole vi rajtas oferti pagajn servojn garantiajn.
+</p>
+
+<p>
+&sect;2. Vi rajtas modifi vian ekzemplerojn a&#365; viajn ekzemplerojn
+de la
+Programo, a&#365; ajnan ties pecon, tiel kreante verkon bazitan sur la
+Programo. Vi rajtas reprodukti kaj distribui ekzemplerojn de tia verko
+la&#365; la reguloj de &sect;1, se vi anka&#365; plenumas &#265;iujn sekvajn
+kondi&#265;ojn:
+</p>
+
+<ol type="a">
+
+ <li>la de vi modifitaj dosieroj enhavu bone videblan avizon ke
+vi &#349;an&#285;is ilin, kaj la datojn de &#265;iuj &#349;an&#285;oj;</li>
+
+ <li>distribuante a&#365; publikigante ajnan verkon kiu tute a&#365; parte
+entenas la Programon a&#365; estas bazita sur la Programo a&#365; ties parto, vi
+devas koncesii la rajtojn uzi tiun verkon al &#265;iu tria partio la&#365; la
+kondi&#265;oj de &#265;i tiu Permesilo, sen ajna koncesia pago, kaj la verko estu
+koncesiata kiel unu tuto;</li>
+
+ <li>se dum la normala uzmaniero de la modifita programo &#285;i
+dialoge legas komandojn, vi devas zorgi ke &#285;i, lan&#265;ite en la plej
+kutima maniero por dialoga uzo, eligu anoncon kiu entenu:
+ <ul>
+
+ <li> avizon pri la a&#365;torrajto;</li>
+
+ <li> malgarantiilon (a&#365; male, informon ke vi provizas
+garantion);</li>
+
+ <li> permeson al la uzantoj plu distribui la Programon sub
+&#265;i tiuj kondi&#265;oj, kaj indikon kiel la uzanto povas vidi kopion de &#265;i
+tiu Permesilo.</li>
+
+ </ul>
+
+(Escepto: se la Programo mem estas dialoga sed normale ne eligas tian
+anoncon, tiam anka&#365; por via derivita verko tia anonco ne estas deviga.)</li>
+
+</ol>
+
+<p>
+Tiuj postuloj rilatas al la modifita verko konsiderata kiel unu tuto.
+Se
+apartigeblaj komponantoj de la verko ne estas derivitaj el la Programo,
+kaj sencas uzi ilin kiel sendependajn kaj apartajn verkojn, tiam &#265;i tiu
+Permesilo ne koncernas tiujn komponantojn en la okazoj kiam vi
+distribuas
+ilin kiel apartajn verkojn. Sed kiam vi distribuas tiujn samajn
+komponantojn kiel parton de tuto estanta verko bazita sur la Programo,
+tiam la distribuado de tiu tuto obeu la regulojn de &#265;i tiu Permesilo,
+kaj
+la rajtoj akirataj de la pluaj koncesiuloj surbaze de &#265;i tiu Permesilo
+etendi&#285;as &#285;is la plena tuto, inklude &#265;iujn ties partojn, sendepende
+tion,
+kiu a&#365;toris ilin.
+</p>
+
+<p>
+Do, la motivo de &#265;i tiu &sect;2 ne estas akiri a&#365; kontesti viajn la
+rajtojn je
+verko kiun vi a&#365;toras; la celo estas realigi la rajton determini la
+manieron distribui la derivitajn a&#365; kolektivajn verkojn bazitajn sur la
+Programo.
+</p>
+
+<p>
+Simpla kunesto de alia verko, ne bazita sur la Programo, apud la
+Programo
+(a&#365; apud verko bazita sur la Programo) sur unu sama datumportilo memora
+a&#365; distribua ne etendas la kondi&#265;ojn de &#265;i tiu Permesilo sur tiun alian
+verkon.
+</p>
+
+<p>
+&sect;3. Vi rajtas reprodukti kaj distribui ekzemplerojn de la
+Programo (a&#365; de
+verko bazita sur la Programo, la&#365; &sect;2) en celkodo a&#365; en formo
+de
+plenumebla programo la&#365; la reguloj de &sect;&sect;1 kaj 2
+&#265;i-supre, kondi&#265;e ke vi
+anka&#365; plenumas iun el la sekvaj postuloj:
+</p>
+
+<ol type="a">
+
+ <li>la ekzempleron akompanu la responda kompleta fontoteksto en
+komputile legebla formo, distribuenda la&#365; la reguloj de
+&sect;&sect;1 kaj 2 &#265;i-supre, sur datumportilo kutime uzata por
+inter&#349;an&#285;i programaron; a&#365;</li>
+
+ <li>la ekzempleron akompanu skribita oferto, valida por almena&#365;
+3 jaroj, disponigi al ajna tria partio, kontra&#365; pago ne pli granda ol
+via kosto de fizika transmeto, ekzempleron de kompleta responda
+komputile legebla fontoteksto distribuenda la&#365; la reguloj de
+&sect;&sect;1 kaj 2 &#265;i-supre, sur datumportilo kutime uzata por
+inter&#349;an&#285;i programaron; a&#365;</li>
+
+ <li>la ekzempleron akompanu la de vi ricevita informo pri
+distribuado de la responda fontoteksto. (Tiu opcio validas nur okaze de
+nekomerca distribuado kaj nur se vi ricevis la programon en celkodo a&#365;
+en formo de plenumebla programo, akompanitan de tia oferto la&#365;
+&sect;3.b de &#265;i tiu Permesilo.)</li>
+
+</ol>
+
+<p>
+La fontoteksto de verko estas tiu &#285;ia formo, kiu plej oportunas por
+modifado. La kompleta fontoteksto de plenumebla verko estas &#265;iuj
+fontotekstoj de &#265;iuj komponantaj moduloj, plus &#265;iuj dosieroj difinantaj
+interfacojn, plus la skriptoj uzataj por regi tradukadon kaj instaladon
+de la plenumebla programo. Tamen, per speciala escepto, en la
+distribuata fontoteksto povas foresti &#265;io normale kundistribuata (&#265;u en
+formo de fontoteksto a&#365; kiel plenumebla programo) por la bazaj
+komponantoj (programlingva tradukilo, operaciuma kerno ktp) de la
+operaciumo per kiu ruli&#285;as la plenumebla programo &ndash; krom se
+tia
+komponanto mem apartenas al la plenumebla programo.
+</p>
+
+<p>
+Se distribuado de la verko en formo de plenumebla programo a&#365; celkodo
+okazas per atingebligo por kopiado el difinita loko, tiam disponigo de
+egala atingeblo por kopii fontotekston el la sama loko plenumas la
+postulon pri liverado de la fontoteksto, e&#265; malgra&#365; ke la tria partio
+ne
+estas devigata kopii la fontotekston kun la celkodo.
+</p>
+
+<p>
+&sect;4. Vi ne rajtas reprodukti, modifi, subkoncesii a&#365; distribui
+la
+Programon alie ol la&#365; la malimplicaj reguloj de &#265;i tiu Permesilo. &#264;ia
+provo alimaniere reprodukti, modifi, subkoncesii a&#365; distribui la
+Programon estas nevalida kaj a&#365;tomate estingas &#265;iujn viajn rajtojn
+donitajn per &#265;i tiu Permesilo. Tamen la rajtoj de la partioj ricevintaj
+de vi ekzemplerojn de la Programo a&#365; rajtojn la&#365; &#265;i tiu Permesilo plu
+validas, dum ili mem observas la kondi&#265;ojn de la Permesilo.
+</p>
+
+<p>
+&sect;5. Ne estas postulate ke vi akceptu &#265;i tiun Permesilon, &#265;ar
+vi &#285;in ne
+subskribis. Tamen nenio alia rajtigas vin modifi a&#365; distribui la
+Programon a&#365; verkojn el &#285;i derivitajn. Tiajn agojn malpermesas la le&#285;o,
+krom se vi akceptas la Permesilon. Tial, se vi modifas a&#365; distribuas
+ekzemplerojn de la Programo (a&#365; de verko bazita sur la Programo), vi
+per
+tio indikas vian akcepton de la rajtigo per la Permesilo fari tion, kaj
+la akcepton de &#265;iuj &#285;iaj kondi&#265;oj kaj reguloj pri reprodukto,
+distribuado
+kaj modifado de la Programo a&#365; de verkoj sur &#285;i bazitaj.
+</p>
+
+<p>
+&sect;6. &#264;iufoje kiam vi pludistribuas la Programon (a&#365; verkon
+bazitan sur la
+Programo), la ricevanto a&#365;tomate ricevas permeson reprodukti, distribui
+a&#365; modifi la Programon la&#365; la kondi&#265;oj kaj reguloj de &#265;i tiu Permesilo.
+Vi ne rajtas aldoni iajn ajn kromajn limigojn je la rajtoj de la
+ricevanto provizitaj per &#265;i tiu Permesilo. Vi ne responsas pri obeigo
+de
+triaj partioj al &#265;i tiu Permesilo.
+</p>
+
+<p>
+&sect;7. Se, sekve de ju&#285;a decido, a&#365; de plendo pri rompo de
+patenta rajto, a&#365;
+pro alia ka&#365;zo (ne nepre ligita al la patenta juro), vi trafas sub
+devigon (&#265;u per ju&#285;a decido, kontrakto a&#365; alie), kiu kontra&#365;as al la
+kondi&#265;oj de &#265;i tiu Permesilo, tio ne rajtigas vin malobei la kondi&#265;ojn
+de
+&#265;i tiu Permesilo. Se vi ne povas distribui plenumante kaj la kondi&#265;ojn
+de &#265;i tiu Permesilo, kaj viajn aliajn koncernajn devigojn, tiam vi
+neniel
+povas &#285;in distribui. Ekzemple, se patenta permesilo malebligas al la
+subkoncesiuloj, akirintaj ekzemplerojn de la Programo rekte de vi a&#365;
+per
+tria partio, &#285;in senhonorarie pludistribui, tiam vi nur povas rezigni
+pri
+distribuado de la Programo.
+</p>
+
+<p>
+Se iu postulo de &#265;i tiu paragrafo i&#285;as nevalida a&#365; neplenumebla &#265;e
+esti&#285;o
+de iuj specialaj cirkonstancoj, apliki&#285;u la resto de &#265;i tiu paragrafo
+sen
+tiu postulo. &#264;i tiu paragrafo apliki&#285;as plene
+<a href="#cxeso" name="re-cxeso">{post la &#265;eso de tia
+cirkonstanco a&#365;} [8]</a> se malestas tiaj cirkonstancoj.
+</p>
+
+<p>
+Ne estas celo de &#265;i tiu &sect;7 instigi vin rompi patentojn a&#365;
+aliajn
+pretendojn je proprieto a&#365; kontesti validon de tiaj pretendoj; la sola
+celo estas protekti la integron de la sistemo de distribuado de libera
+programaro, sistemo funkcianta per publikaj permesiloj. Multaj homoj
+malavare kontribuis al la kreo de ri&#265;a gamo da programaro distribuata
+per
+tiu sistemo, fidante je &#285;ia konsekvenca aplikado; la rajto elekti la
+sistemon de distribuado de programverko apartenas al la a&#365;toro /
+donanto,
+kaj ne koncesiulo &#285;in altrudu.
+</p>
+
+<p>
+La intenco de &#265;i tiu &sect;7 estas klare prezenti, kiajn
+konsekvencojn celas
+estigi la ceteraj partoj de &#265;i tiu Permesilo.
+</p>
+
+<p>
+&sect;8. Se distribuadon kaj/a&#365; uzadon de la Programo en iuj landoj
+limigas
+patentoj a&#365; interfacoj kovritaj per a&#365;torrajto, la praposedanto de la
+a&#365;torrajto, metanta la Programon sub la kondi&#265;ojn de &#265;i tiu Permesilo,
+rajtas malimplice limigi la teritorion de distribuado de la Programo,
+ekskludante tiajn landojn, tiel ke la distribuado estu permesita nur en
+a&#365; inter la landoj ne tu&#349;itaj per tia limigo. En tia okazo la koncerna
+limigo estu rigardata kiel unu el la kondi&#265;oj de &#265;i tiu Permesilo.
+</p>
+
+<p>
+&sect;9. La&#365;bezone &laquo;Free Software Foundation&raquo;
+rajtas publikigi reviziitajn
+kaj/a&#365; novajn versiojn de la &#284;enerala Publika Permesilo. Tiaj versioj
+konservos la bazajn principojn de &#265;i tiu versio, sed povas diferenci en
+detaloj, responde al novaj problemoj a&#365; situacioj.
+</p>
+
+<p>
+&#264;iu versio ricevas sian propran numeron. Se la Programo indikas, ke &#285;i
+estas submetita al &#265;i tiu Permesilo en ties difinita versinumero
+&laquo;a&#365; ajna
+posta versio&raquo; (&laquo;any later version&raquo;), tiam
+vi rajtas, la&#365;pla&#265;e, sekvi a&#365;
+la kondi&#265;ojn kaj regulojn de la indikita versio, a&#365; tiujn de ajna pli
+malfrua versio publikigita de &laquo;Free Software
+Foundation&raquo;. Se la Programo
+ne indikas versinumeron, vi rajtas elekti iun ajn version iam ajn
+publikigitan de &laquo;Free Software Foundation&raquo;.
+</p>
+
+<p>
+&sect;10. Se vi volas uzi partojn de la Programo en aliaj
+programoj, kies
+distribuaj kondi&#265;oj diferencas, skribe petu permeson de la a&#365;toro. Por
+programaro kies a&#365;torrajton tenas &laquo;Free Software
+Foundation&raquo;, petu
+permeson de &laquo;Free Software Foundation&raquo;; iam &#285;i
+faras esceptojn pri tio.
+La decidojn de &laquo;Free Software Foundation&raquo; motivas
+du celoj: konservi la
+liberon de &#265;iu verko derivita el nia libera programaro, kaj &ndash;
+&#285;enerale
+&ndash; helpi kunhavigon kaj reuzon de programaro.
+</p>
+
+<h2> Malgarantiilo
+</h2>
+
+<p>
+&sect;11. <em>&#264;ar la Programo estas koncesiata senpage, al
+&#285;i apliki&#285;as nenia
+garantio, kiom tion permesas la koncerna le&#285;o. Krom se alio estas
+malimplice indikita en skriba formo, la tenantoj de la a&#365;torrajto
+kaj/a&#365;
+aliaj partioj disponigas la Programon &laquo;tia, kia &#285;i
+estas&raquo; (&laquo;as is&raquo;) sen
+ia ajn garantio, nek esprimita nek implica, interalie sen la implica
+garantio pri surmerkatigeblo, a&#365; ta&#365;geco por konkreta uzo. La tuta
+risko
+koncerne la kvaliton kaj funkcikapablon de la programo estas via. Se la
+programo montri&#285;os difekta, vi portos la kostojn de &#265;iuj necesaj
+servoj,
+riparo a&#365; korekto.</em>
+</p>
+
+<p>
+&sect;12. <em>Neniokaze, krom se alion postulas aplikebla
+le&#285;o a&#365; skribita
+interkonsento, neniu tenanto de la a&#365;torrajto a&#365; alia partio rajtigita
+modifi kaj/a&#365; pludistribui la Programon la&#365; la &#265;i-supraj permesoj, ne
+responsos al vi pro la dama&#285;oj, inklude &#285;eneralajn, specialajn,
+akcidentajn a&#365; malrektajn, rezultantaj el uzo a&#365; maleblo uzi la
+Programon (interalie, perdon a&#365; difekton de datumoj, a&#365; perdojn
+suferitajn de vi a&#365; de triaj partioj, a&#365; nekapablo de la Programo
+kunlabori kun iuj ajn aliaj programoj) &ndash; e&#265; se tiaj tenanto
+a&#365; alia
+partio estis avertitaj pri la eblo de tiaj dama&#285;oj.</em>
+</p>
+
+<h3> Fino de la reguloj kaj kondi&#265;oj
+</h3>
+
+<h2> Aldono:<br>
+
+Kiel apliki tiujn regulojn al viaj novaj programoj
+</h2>
+
+<p>
+Se vi verkas novan programon kaj volas fari &#285;in kiel eble plej uzebla
+por
+la socio, la plej bona maniero atingi tion estas fari &#285;in libera, kaj
+tiam &#265;iu povos &#285;in pludistribui kaj modifi la&#365; la kondi&#265;oj de &#265;i tiu
+Permesilo.
+</p>
+
+<p>
+Tiucele, aldonu al la programo la sekvajn avizojn. Plej sekure estas
+meti ilin komence de &#265;iu fonta dosiero, por plej klare averti pri
+malesto
+de garantioj; kaj &#265;iu dosiero enhavu almena&#365; la linion kun la
+a&#365;torrajta
+signo kaj referencon al la plena teksto de la avizo:
+</p>
+
+<pre>&lt;unu linio kun la nomo de la programo kaj resumo de &#285;ia funkcio&gt;<br>Copyright &copy; &lt;jaro&gt; &lt;nomo la la a&#365;toro&gt;<br><br>This program is free software; you &#264;i tiu verko estas libera programo;<br>can redistribute it and/or modify vi povas &#285;in pludistribui kaj/a&#365;<br>it under the terms of the GNU modifi je la kondi&#265;oj de la GNUa<br>General Public License as published &#284;enerala Publika Permesilo, eldonita<br>by the Free Software Foundation; de &laquo;Free Software Foundation&raquo;, la&#365;<br>either version 2 of the License, or la versio 2 de tiu Permesilo a&#365;, se<br>(at your option) any later version. vi preferas, ajna posta versio.<br><br>This program is distributed in the Ni distribuas &#265;i tiun programon<br>hope that it will be useful, but esperante ke &#285;i estos utila, tamen<br>WITHOUT ANY WARRANTY; without even SEN IA AJN GARANTIO, i.a. sen la<br>the implied warranty of implica garantio pri SURMERKATIGEBLO<br>MERCHANTABILITY or FITNESS FOR A a&#365; TA&#364;GECO POR IU KONKRETA CELO.<br>PARTICULAR PURPOSE. See the GNU Pliajn detalojn vidu en la GNUa<br>General Public License for more &#284;enerala Publika Permesilo.<br>details.<br><br>You should have received a copy of Ekzemplero de la GNUa &#284;enerala<br>the GNU General Public License Publika Permesilo devas esti<br>along with this program; if not, liverita al vi kun &#265;i tiu programo;<br>write to the se vi &#285;in ne ricevis, turnu vin al<br><br> Free SoftwareFoundation, Inc.,<br> 59 Temple Place, Suite 330,<br> Boston, MA 02111-1307<br> USA<br></pre>
+
+<p>
+Krome, indiku kiel eblas vin kontakti per la reta a&#365; papera po&#349;to.
+</p>
+
+<p>
+Se la programo estas dialoga, zorgu ke &#285;i eligu mallongan avizon, kiel
+la
+&#265;i-suba ekzemplo, &#265;e startigo en la interaga re&#285;imo:
+</p>
+
+<pre> Gnomovido, versio 69. A&#365;torrajto &copy; &lt;jaro&gt; &lt;a&#365;tornomo&gt;<br> Gnomovido estas distribuata SEN IA AJN GARANTIO;<br> por vidigi detalojn tajpu &laquo;show w&raquo;.<br> Tio estas libera programo, kaj bonvenas &#285;ia distribuado la&#365; certaj<br> kondi&#265;oj; por vidigi detalojn tajpu &laquo;show c&raquo;.<br></pre>
+
+<p>
+La proponitaj komandoj &laquo;show w&raquo; kaj &laquo;show
+c&raquo; devus surekranigi la
+koncernajn partojn de la &#284;enerala Publika Permesilo. Kompreneble, ili
+povas havi aliajn nomojn; ili e&#265; povas esti musklakoj a&#365; menueroj
+&ndash; io
+ajn konvena al via programo.
+</p>
+
+<p>
+Se necesas, vi devas anka&#365; ricevi de via dunganto (se vi estas dungita
+programisto) a&#365; eventuale de reprezentanto de via
+<a href="#dungo" name="re-dungo">lernejo [9]</a> skriban
+malpretendon je la a&#365;torrajto. Vi povas uzi &#265;i-suban modelon, konvene
+&#349;an&#285;inte la nomojn:
+</p>
+
+<blockquote> La firmao &laquo;ABC&#264;&raquo; per &#265;i tio
+rezignas pri &#265;iaj proprietaj rajtoj je la programo
+&laquo;Gnomovido&raquo; (faranta pasa&#309;ojn por tradukiloj)
+verkita de Pro Gramisto.
+ <p> &lt;subskribo de Mag Nato&gt;, la 1an de aprilo 3001<br>
+
+Mag Nato, Prezidanto </p>
+
+</blockquote>
+
+<p>
+&#264;i tiu &#284;enerala Publika Permesilo malebligas inkludigi vian programon
+en
+proprietan programaron. Se via programo estas biblioteko da
+subprogramoj,
+vi eble ju&#285;os pli utila permesi bindi proprietajn aplika&#309;ojn kun la
+biblioteko. Se vi volas tion, anstata&#365; &#265;i tiun Permesilon uzu la GNUan
+&#284;eneralan Publikan Permesilon Bibliotekan.
+</p>
+
+<h2> Notoj de la tradukinto
+</h2>
+
+<p>
+<a name="lgpl" href="#re-lgpl">[1]</a>
+Lastatempe FSF preferas
+nomi tiun permesilon ne &laquo;biblioteka&raquo; sed
+&laquo;malplia&raquo; a&#365; &laquo;reduktita&raquo;
+(GNU Lesser General Public License); &#265;e tio la angla siglo restas la
+sama (LGPL).
+</p>
+
+<p>
+<a href="#re-distribui" name="distribui">[2] Distribui</a> programon:
+disponigi atingon al ekzemplero de programo en
+ajna formo por <a href="#prod" name="re-prod">reproduktado [4]</a>,
+interalie per la Reto, vendo, ludono, pruntedono ktp.
+</p>
+
+<p>
+<a href="#re-copy" name="copy">[3]</a> Anglalingve temas pri
+&laquo;copyright&raquo;, kaj &laquo;kopirajto&raquo; ja
+ekzistas en
+Esperanto; tamen la termino &laquo;a&#365;torrajto&raquo; &#349;ajnas iom
+preferata. &ndash; La
+angla teksto estas iom malklara: kiu i&#285;as la tenanto de la a&#365;torrajto,
+&#265;u
+nepre FSF a&#365; eventuale iu alia, ekz-e la a&#365;toro mem? La gramatiko de &#265;i
+tiu loko pensigas ke temas pri transdono de la a&#365;torrajto al FSF; sed
+en
+la postaj rekomendoj ni vidas modelan ekzemplon
+</p>
+
+<blockquote> Gnomovido, versio 69. A&#365;torrajto &copy;
+&lt;jaro&gt; &lt;a&#365;tornomo&gt;
+</blockquote>
+
+<p>
+A&#365; eble &laquo;ni&raquo; estas ne FSF sed
+&laquo;oni&raquo;?
+</p>
+
+<p>
+<a href="#re-prod" name="prod">[4]</a> La originalo &#265;iam parolas pri
+&laquo;copying&raquo;, tamen en la a&#365;torrajta juro
+la jura termino estas &laquo;reprodukti&raquo;: Reproduktado de
+programo estas farado
+de unu a&#365; pli ol unu ekzempleroj de la programo sur ajna datumportilo,
+inklude registradon de la programo en komputilan memoron.
+</p>
+
+<p>
+<a href="#re-pat" name="pat">[5]</a> La Usona juro ebligas patenti
+certajn programojn (Software Patents).
+</p>
+
+<p>
+<a href="#re-propr" name="propr">[6]</a> Proprieta rajto je programo estas
+la ekskluziva rajto plenumi kaj/a&#365; permesi plenumi la sekvajn agojn:
+</p>
+
+<ul>
+
+ <li> publikigi programon;</li>
+
+ <li> reprodukti programon, plene a&#365; parte, en ajna formo;</li>
+
+ <li> distribui la programon;</li>
+
+ <li> modifi la programon (inklude tradukon en aliajn lingvojn);</li>
+
+ <li> kaj aliajn agojn menciitajn en la koncerna le&#285;o.</li>
+
+</ul>
+
+<p>
+<a href="#re-deriv" name="deriv">[7]</a> &laquo;derivative work under
+copyright law&raquo;, termino de la Usona le&#285;o.
+</p>
+
+<p>
+<a href="#re-cxeso" name="cxeso">[8]</a> La teksto intervinkula
+en la originalo malestas, sed estus
+memtrudi&#285;anta en la responda teksto rusa (kaj enestas en la rusa
+traduko).
+</p>
+
+<p>
+<a href="#re-dungo" name="dungo">[9]</a> Postulo de Usona le&#285;o. Sed
+ekz-e en Ruslando nur la dunganto povas
+a&#365;tomate ricevi la a&#365;torrajton je via verko, kaj nur je verko ofice
+komisiita al vi (krom se la dunga kontrakto anta&#365;vidis alion).
+</p>
+
+<h2> Tradukproblemoj
+</h2>
+
+<p>
+&#264;i tio estas mia unua teksto jura, kaj super &#265;io la originalo estas en
+fremda lingvo. Pri la senco de kelkaj lokoj mi havas dubojn.
+</p>
+
+<dl>
+
+ <dt>En &sect;11</dt>
+
+ <dd>The entire risk as to the quality and PERFORMANCE of the
+program is with you.<br>
+
+La tuta risko koncerne la kvaliton kaj FUNKCIKAPABLON de la programo
+estas via. <br>
+
+{Normale PERFORMANCE signifus &laquo;rendimento&raquo;, sed la
+kunteksto &#349;ajnas
+postuli funkcikapablon, kiel en la traduko germana
+(Leistungsf&auml;higkeit);
+cetere mi atendus &#285;in unualoke, kaj nur poste zorgus pri kvalito.} </dd>
+
+ <dt>En &sect;12</dt>
+
+ <dd>including any GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+damages<br>
+
+dama&#285;oj, inklude &#284;ENERALAJN, SPECIALAJN, AKCIDENTAJN a&#365; MALREKTAJN <br>
+
+{Mi tradukis &laquo;la&#365;vorte&raquo;, sen bone kompreni kio
+estas tiuj &laquo;specialaj
+dama&#285;oj&raquo; ktp; la rusa traduko &#265;i tie parolas pri
+&laquo;&#285;eneralaj (&#265;u sumaj?),
+realaj, anta&#365;videblaj kaj malrektaj dama&#285;oj&raquo;; la germana
+traduko, pri
+&laquo;allgemeiner oder spezieller Sch&auml;den,
+Sch&auml;den durch Seiteneffekte
+(Nebenwirkungen) oder Folgesch&auml;den&raquo;; la ceteraj
+tradukoj, same papage
+kiel mi.} </dd>
+
+ <dt>(garantio pri) surmerkatigeblo </dt>
+
+ <dd>tiel mi tradukis &laquo;merchantability&raquo;.
+En la germana traduko, &laquo;Marktreife&raquo;; ruse,
+proksimume, &laquo;var/ec/o,
+var/esto, var/statuso&raquo;; france
+&laquo;commercialisation&raquo;. </dd>
+
+</dl>
+
+</body>
+</html>
diff --git a/logo/src/xlogo/gpl/gpl-es.html b/logo/src/xlogo/gpl/gpl-es.html
new file mode 100644
index 0000000..f78887a
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-es.html
@@ -0,0 +1,773 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+ <title>Licencia P&uacute;blica General</title>
+
+
+</head>
+
+
+<body>
+
+<h3><font face="Verdana" size="2">Licencia
+P&uacute;blica General</font></h3>
+
+<p>
+<font face="Verdana" size="2">Versi&oacute;n 2,
+Junio de 1991<br>
+
+Traducci&oacute;n al Espa&ntilde;ol : Diciembre de 2001 por<br>
+
+Jos&eacute; Mar&iacute;a Sar&aacute;chaga Fischer:
+jmsarachaga at garaitia dot com
+</font></p>
+
+<p>
+<font face="Verdana" size="2"><b>Contribuciones:</b><br>
+
+13-Dic-2002 Fabi&aacute;n Rodr&iacute;guez
+www.fabianrodriguez.com
+</font></p>
+
+<p>
+<font face="Verdana" size="1"> &gt; <a href="#preambulo">pre&aacute;mbulo</a><br>
+
+&gt; <a href="#terminos">t&eacute;rminos
+y condiciones</a><br>
+
+&gt; <a href="#aplicacion">c&oacute;mo
+aplicar esta licencia</a></font></p>
+
+<font face="Verdana" size="1"><font size="3">
+</font></font>
+<hr><font face="Verdana" size="1"><font size="3"><tt>This is an unofficial
+translation of the GNU General Public License into spanish. It was not
+published by the Free Software Foundation, and does not legally state
+the distribution terms for software that uses the GNU GPL--only the
+original English text of the GNU GPL does that. However, we hope that
+this translation will help spanish speakers understand the GNU GPL
+better.<br>
+
+</tt></font></font>
+<hr width="60%"><font face="Verdana" size="1"><font size="3"><tt>Esta es una traducci&oacute;n
+NO oficial de la "GNU General Public License" al espa&ntilde;ol. No
+fu&eacute;
+publicada por la "FSF Free Software Foundation", y no respalda
+legalmente los t&eacute;rminos de distribuci&oacute;n del
+software que utiliza la
+"GNU GPL", s&oacute;lo el texto original en ingl&eacute;s lo
+hace. Sin embargo
+esperamos que esta traducci&oacute;n ayude a las personas de habla
+hispana a
+entender mejor la "GPL".<br>
+
+</tt></font></font>
+<hr>
+<p><font face="Verdana" size="1"><font face="Verdana" size="2">
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.<br>
+
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Toda
+persona tiene permiso de copiar y distribuir copias fieles de este
+documento de licencia, pero no se permite hacer modificaciones.
+</font></font></p>
+
+<p><font face="Verdana" size="1"><font face="Verdana" size="2"><br>
+
+<br>
+
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><a name="preambulo"></a><font size="3"><b>PREAMBULO</b></font>
+</font></font></p>
+
+<hr>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Los
+contratos de licencia de la mayor parte del software est&aacute;n
+dise&ntilde;ados
+para quitarle su libertad de compartir y modificar dicho software. En
+contraste, la "GNU General Public License" pretende garantizar su
+libertad de compartir y modificar el software "libre", esto es para
+asegurar que el software es libre para todos sus usuarios. Esta
+licencia p&uacute;blica general se aplica a la mayor&iacute;a
+del software de la "FSF
+Free Software Foundation" (Fundaci&oacute;n para el Software Libre)
+y a
+cualquier otro programa de software cuyos autores as&iacute; lo
+establecen.
+Algunos otros programas de software de la Free Software Foundation
+est&aacute;n cubiertos por la "LGPL Library General Public License"
+(Licencia
+P&uacute;blica General para Librer&iacute;as), la cual puede
+aplicar a sus programas
+tambi&eacute;n.
+</font></font>
+<p><font face="Verdana" size="1"><font face="Verdana" size="2">Cuando
+hablamos de software libre, nos referimos a libertad, no precio.
+Nuestras licencias "General Public Licenses" est&aacute;n
+dise&ntilde;adas para
+asegurar que:
+</font></font></p>
+
+<ol>
+
+ <font face="Verdana" size="1"><font face="Verdana" size="2"> <li> usted tiene la
+libertad de distribuir copias del software libre (y cobrar por ese
+sencillo servicio si as&iacute; lo desea) </li>
+
+ <li> recibir el c&oacute;digo fuente (o tener la
+posibilidad de obtenerlo si as&iacute; lo desea) </li>
+
+ <li> que usted puede modificar el software o utilizar partes de
+el en nuevos programas de software libre </li>
+
+ <li> que usted est&eacute; enterado de que tiene la
+posibilidad de hacer todas estas cosas. </li>
+
+ </font></font>
+</ol>
+
+<font face="Verdana" size="1"><font face="Verdana" size="2">Para
+proteger sus derechos, necesitamos hacer restricciones que prohiban a
+cualquiera denegarle estos derechos o a pedirle que renuncie a ellos.
+Estas restricciones se traducen en algunas responsabilidades para usted
+si distribuye copias del software, o si lo modifica.
+</font></font>
+<p><font face="Verdana" size="1"><font face="Verdana" size="2">Por
+ejemplo, si usted distribuye copias de un programa, ya sea
+gratuitamente o por algun importe, usted debe dar al que recibe el
+software todos los derechos que usted tiene sobre el mismo. Debe
+asegurarse tambi&eacute;n que reciban el c&oacute;digo fuente o
+bi&eacute;n que puedan
+obtenerlo si lo desean. Y por &uacute;ltimo debe mostrarle a esa
+persona estos
+t&eacute;rminos para que conozca los derechos de que goza.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Nosotros
+protegemos sus derechos en 2 pasos: (1) protegiendo los derechos de
+autor del software y (2) ofreciendole este contrato de licencia que le
+otorga permiso legal para copiar, distribuir y modificar el software.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Adem&aacute;s,
+para la protecci&oacute;n de los autores de software y la nuestra,
+queremos
+asegurarnos de que toda persona entienda que no existe ninguna
+garant&iacute;a
+del software libre. Si el software es modificado por alguien y lo
+distribuye, queremos que quienes lo reciban sepan que la copia que
+obtuvieron no es la original, por lo que cualquier problema provocado
+por quien realiz&oacute; la modificaci&oacute;n no
+afectar&aacute; la reputaci&oacute;n del autor
+original.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Finalmente,
+cualquier programa de software libre es constantemente amenazado por
+las patentes de software. Deseamos evadir el peligro de que los
+re-distribuidores de un programa de software libre obtenga
+individualmente los derechos de patente con el fin de volver dicho
+programa propietario. Para prevenir esto, hemos dejado en claro que
+cualquier patente deber&aacute; ser licenciada para el uso libre de
+toda
+persona o que no est&eacute; licenciada del todo.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">A
+continuaci&oacute;n se describen con precisi&oacute;n los
+t&eacute;rminos y condiciones para copiar, distribuir y modificar
+el
+software.
+</font></font></p>
+
+<p><font face="Verdana" size="1"><font face="Verdana" size="2"><br>
+
+<br>
+
+</font></font></p>
+
+<p>
+</p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><a name="terminos"></a><font size="3"><b>TERMINOS Y CONDICIONES PARA COPIA,
+MODIFICACION Y DISTRIBUCION</b></font>
+</font></font></p>
+
+<hr>
+<p><font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>0.</b></font>
+Esta licencia aplica a cualquier programa o trabajo que contenga una
+nota puesta por el propietario de los derechos del trabajo
+estableciendo que su trabajo puede ser distribuido bajo los
+t&eacute;rminos de
+esta "GPL General Public License". El "Programa", utilizado en lo
+subsecuente, se refiere a cualquier programa o trabajo original, y el
+"trabajo basado en el Programa" significa ya sea el Programa o
+cualquier trabajo derivado del mismo bajo la ley de derechos de autor:
+es decir, un trabajo que contenga el Programa o alguna
+porci&oacute;n de el,
+ya sea &iacute;ntegra o con modificaciones y/o traducciones a otros
+idiomas.
+De aqu&iacute; en adelante "traducci&oacute;n"
+estar&aacute; incluida (pero no limitada a)
+en el t&eacute;rmino "modificaci&oacute;n", y la persona a la
+que se aplique esta
+licencia ser&aacute; llamado "usted".
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Otras
+actividades que no sean copia, distribuci&oacute;n o
+modificaci&oacute;n no est&aacute;n
+cubiertas en esta licencia y est&aacute;n fuera de su alcance. El
+acto de
+ejecutar el programa no est&aacute; restringido, y la salida de
+informaci&oacute;n
+del programa est&aacute; cubierta s&oacute;lo si su contenido
+constituye un trabajo
+basado en el Programa (es independiente de si fue resultado de ejecutar
+el programa). Si esto es cierto o no depende de la funci&oacute;n
+del
+programa.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>1.</b></font>
+Usted puede copiar y distribuir copias fieles del c&oacute;digo
+fuente del
+programa tal como lo recibi&oacute;, en cualquier medio, siempre
+que
+proporcione de manera conciente y apropiada una nota de derechos de
+autor y una declaraci&oacute;n de no garant&iacute;a,
+adem&aacute;s de mantener intactas
+todas las notas que se refieran a esta licencia y a la ausencia de
+garant&iacute;a, y que le proporcione a las dem&aacute;s
+personas que reciban el
+programa una copia de esta licencia junto con el Programa.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Usted puede
+aplicar un cargo por el acto f&iacute;sico de transferir una copia,
+y ofrecer
+protecci&oacute;n de garant&iacute;a por una cuota, lo cual no
+compromete a que el
+autor original del Programa responda por tal efecto.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>2.</b></font>
+Usted puede modificar su copia del Programa o de cualquier parte de el,
+formando as&iacute; un trabajo basado en el Programa, y copiar y
+distribuir
+tales modificaciones o bi&eacute;n trabajar bajo los
+t&eacute;rminos de la secci&oacute;n 1
+arriba descrita, siempre que cumpla con las siguientes condiciones:
+</font></font></p>
+
+<p>
+</p>
+
+<ol type="A">
+
+ <font face="Verdana" size="1"><font face="Verdana" size="2"> <li> Usted debe
+incluir en los archivos modificados notas declarando que
+modific&oacute; dichos archivos y la fecha de los cambios. </li>
+
+ <li>Usted debe notificar que ese trabajo que distribuye
+contiene totalmente
+o en partes al Programa, y que debe ser licenciado como un conjunto sin
+cargo alguno a cualquier otra persona que reciba sus modificaciones
+bajo los t&eacute;rminos de esta Licencia. </li>
+
+ <li> Si el programa modificado lee normalmente comandos
+interactivamente cuando es ejecutado, usted debe presentar un aviso,
+cuando el programa inicie su ejecuci&oacute;n en ese modo
+interactivo de la
+forma m&aacute;s ordinaria, que contenga una noticia de derechos de
+autor y un
+aviso de que no existe garant&iacute;a alguna (o que s&iacute;
+existe si es que usted
+la proporciona) y que los usuarios pueden redistribuir el programa bajo
+esas condiciones, e informando al usuario como puede ver una copia de
+esta Licencia. (Excepci&oacute;n: si el programa en s&iacute;
+es interactivo pero
+normalmente no muestra notas, su trabajo basado en el Programa no tiene
+la obligaci&oacute;n de mostrar tales notas) </li>
+
+ </font></font>
+</ol>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Estos
+requerimientos aplican al trabajo modificado como un todo. Si existen
+secciones identificables de tal trabajo que no son derivadas del
+Programa original, y pueden ser razonablemente consideradas trabajos
+separados e independientes como tal, entonces esta Licencia y sus
+t&eacute;rminos no aplican a dichas secciones cuando usted las
+distribuye como
+trabajos separados. Pero cuando usted distribuye las mismas secciones
+como parte de un todo que es un trabajo basado en el Programa, la
+distribuci&oacute;n del conjunto debe ser bajo los
+t&eacute;rminos de esta Licencia,
+cuyos permisos para otras personas que obtengan el software se
+extienden para todo el software, as&iacute; como para cada parte de
+el,
+independientemente de qui&eacute;n lo escribi&oacute;.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">No es la
+intenci&oacute;n de esta secci&oacute;n de reclamar derechos o
+pelear sus derechos
+sobre trabajos hechos enteramente por usted, en lugar de eso, la
+intenci&oacute;n es ejercer el derecho de controlar la
+distribuci&oacute;n de los
+trabajos derivados o colectivos basados en el Programa.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Adicionalmente,
+el simple agregado de otro trabajo NO basado en el Programa al Programa
+en cuesti&oacute;n (o a un trabajo basado en el Programa) en
+alg&uacute;n medio de
+almacenamiento no pone el otro trabajo bajo el alcance de esta
+Licencia.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>3.</b></font>
+Usted puede copiar y distribuir el Programa (o un trabajo basado en
+&eacute;l,
+bajo la Secci&oacute;n 2) en c&oacute;digo objeto o en forma de
+ejecutable najo los
+t&eacute;rminos de las secciones 1 y 2 arriba descritas siempre que
+cumpla los
+siguientes requisitos:
+</font></font></p>
+
+<ol type="A">
+
+ <font face="Verdana" size="1"><font face="Verdana" size="2"> <li>Acompa&ntilde;arlo
+con el correspondiente c&oacute;digo fuente legible por la
+m&aacute;quina, que debe ser distribu&iacute;do bajo los
+t&eacute;rminos de las secciones 1
+y 2 y en un medio comunmente utilizado para el intercambio de software,
+o </li>
+
+ <li> Acompa&ntilde;arlo con una oferta escrita,
+v&aacute;lida por al menos 3
+a&ntilde;os y para cualquier persona, por un cargo no mayor al
+costo que
+conlleve la distribuci&oacute;n f&iacute;sica del
+c&oacute;digo fuente correspondiente en un
+medio comunmente utilizado para el intercambio de software, o </li>
+
+ <li> Acompa&ntilde;arlo con la informaci&oacute;n que
+usted recibi&oacute; sobre la
+oferta de distribuci&oacute;n del c&oacute;digo fuente
+correspondiente. (Esta
+alternativa est&aacute; permitida s&oacute;lo para
+distribuci&oacute;n no-comercial y s&oacute;lo
+si usted recibi&oacute; el Programa en c&oacute;digo objeto o
+en forma de ejecutable
+con tal oferta de acuerdo a la subsecci&oacute;n b anterior) </li>
+
+ </font></font>
+</ol>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">El c&oacute;digo
+fuente de un trabajo significa la forma preferida de hacer
+modificaciones al mismo. Para un trabajo ejecutable, un
+c&oacute;digo fuente
+completo significa todo el c&oacute;digo fuente de todos los
+m&oacute;dulos que
+contiene, mas cualquier archivo de definici&oacute;n de interfases,
+mas los
+programas utilizados para controlas la compilaci&oacute;n y la
+instalaci&oacute;n del
+ejecutable.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Sinembargo, como
+excepci&oacute;n especial, no se requiere que el c&oacute;digo
+fuente distribu&iacute;do incluya cualquier cosa que no sea
+normalmente
+distribu&iacute;da con las componentes mayores (compilador, kernel,
+etc.)
+del sistema operativo en el cual el ejecutable corre, a menos de que
+una componente en particular acompa&ntilde;e al ejecutable.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Si la
+distribuci&oacute;n del ejecutable o del c&oacute;digo objeto
+se hace ofreciendo
+acceso a copiar desde un lugar designado, entonces el ofrecer acceso
+equivalente para copiar el c&oacute;digo fuente desde el mismo
+lugar se
+considera distribuci&oacute;n del c&oacute;digo fuente, aunque
+las dem&aacute;s personas no
+copien el c&oacute;digo fuente junto con el c&oacute;digo
+objeto.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>4.</b></font>
+Usted no puede copiar, modificar, sub-licenciar ni distribuir el
+Programa a menos que sea expresamente bajo esta Licencia, de otra forma
+cualquier intento de copiar, modificar, sub-licenciar o distribuir el
+programa es nulo, y autom&aacute;ticamente causar&aacute; la
+p&eacute;rdida de sus derechos
+bajo esta Licencia. Sin embargo, cualquier persona que haya recibido
+copias o derechos de usted bajo esta Licencia no ver&aacute;n
+terminadas sus
+Licencias ni sus derechos perdidos mientras ellas contin&uacute;en
+cumpliendo
+los t&eacute;rminos de esta Licencia.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>5.</b></font>
+Usted no est&aacute; obligado a aceptar esta Licencia, dado que no
+la ha
+firmado. Sin embargo, nada le otorga el permiso de modificar o
+distribuir el Programa ni sus trabajos derivados. Estas acciones
+est&aacute;n
+prohibidas por la ley si usted no acepta esta Licencia. Sin embargo,
+modificando o distribuyendo el Programa (o cualquier trabajo basado en
+el Programa) indica su aceptaci&oacute;n de esta Licencia y de
+todos sus
+t&eacute;rminos y condiciones para copiar, distribuir o modificar
+el Programa
+y/o trabajos basados en el.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>6.</b></font>
+Cada vez que usted redistribuye el Programa (o cualquier trabajo basado
+en el Programa), la persona que lo recibe autom&aacute;ticamente
+recibe una
+licencia del autor original para copiar, distribuir o modificar el
+Programa sujeto a estos t&eacute;rminos y condiciones. Usted no
+puede imponer
+ninguna restricci&oacute;n adicional a las personas que reciban el
+Programa
+sobre los derechos que en esta Licencia se les otorga. Usted no es
+responsable de forzar a terceras personas en el cumplimiento de esta
+Licencia.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>7.</b></font>
+Si como consecuencia de un veredicto de un juzgado o por el alegato de
+infringir una patente o por cualquier otra raz&oacute;n (no
+limitado solo a
+cuestiones de patentes) se imponen condiciones sobre usted que
+contradigan los t&eacute;rminos y condiciones de esta Licencia,
+&eacute;stas no le
+excusan de los t&eacute;rminos y condiciones aqu&iacute;
+descritos. Si usted no puede
+distribuir el producto cumpliendo totalmente con las obligaciones
+concernientes a la resoluci&oacute;n oficial y al mismo tiempo con
+las
+obligaciones que se describen en este contrato de Licencia, entonces no
+podr&aacute; distribuir m&aacute;s este producto. Por ejemplo,
+si una licencia de
+patente no permitir&aacute; la distribuci&oacute;n del Programa
+de forma libre de
+regal&iacute;as (sin pago de regal&iacute;as) por parte de
+quienes lo reciban directa
+o indirectamente, entonces la &uacute;nica forma de cumplir con
+ambas
+obligaciones es renunciar a la distribuci&oacute;n del mismo.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Si
+cualquier parte de esta secci&oacute;n resulta inv&aacute;lida,
+inaplicable o no
+obligatoria bajo cualquier circunstancia en particular, la tendencia de
+esta es a aplicarse, y la secci&oacute;n completa se
+aplicar&aacute; bajo otras
+circunstancias.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">La
+intenci&oacute;n de esta secci&oacute;n no es la de inducirlo a
+infringir ninguna ley
+de patentes, ni tampoco infringir alg&uacute;n reclamo de derechos,
+ni
+discutir la validez de tales reclamos; esta secci&oacute;n tiene el
+&uacute;nico
+prop&oacute;sito de proteger la integridad del sistema de
+distribuci&oacute;n del
+software libre, que est&aacute; implementado por
+pr&aacute;cticas de licencia
+p&uacute;blica. Mucha gente ha hecho generosas contribuciones a la
+amplia gama
+de software distribuido bajo este sistema favoreciendo as&iacute;
+la constante
+aplicaci&oacute;n de este sistema de distribuci&oacute;n; es
+decisi&oacute;n del
+autor/donador si su Programa ser&aacute; distribu&iacute;do
+utilizando este u otro
+sistema de distribuci&oacute;n, y la persona que recibe el software
+no puede
+obligarlo a hacer ninguna elecci&oacute;n en particular.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Esta secci&oacute;n
+pretende dejar muy en claro lo que se cree que ser&aacute; una
+consecuencia del resto de esta Licencia.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>8.</b></font>
+Si la distribuci&oacute;n y/o el uso del Programa se restringe a
+algunos
+pa&iacute;ses ya sea por patentes, interfases protegidas por
+derechos de
+autor, el propietario original de los derechos de autor que ubica su
+Programa bajo esta Licencia puede agregar una restricci&oacute;n
+geogr&aacute;fica de
+distribuci&oacute;n expl&iacute;cita excluyendo los
+pa&iacute;ses que aplique, dando como
+resultado que su distribuci&oacute;n s&oacute;lo se permita en
+los pa&iacute;ses no
+exclu&iacute;dos. En tal caso, esta Licencia incorpora la
+limitaci&oacute;n como si
+hubiera sido escrita en el cuerpo de esta misma Licencia.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>9.</b></font>
+La "FSF Free Software Foundation" puede publicar versiones nuevas o
+revisadas de la "GPL General Public License" de uno a otro momento.
+Estas nuevas versiones mantendr&aacute;n el esp&iacute;ritu de
+la presente versi&oacute;n,
+pero pueden diferir en la inclusi&oacute;n de nuevos problemas o en
+la manera
+de tocar los problemas o aspectos ya presentes.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Cada
+versi&oacute;n tendr&aacute; un n&uacute;mero de
+versi&oacute;n que la distinga. Si el Programa
+especifica un n&uacute;mero de versi&oacute;n para esta
+Licencia que aplique a &eacute;l y
+"cualquier versi&oacute;n subsecuente", usted tiene la
+opci&oacute;n de seguir los
+t&eacute;rminos y condiciones de dicha versi&oacute;n o de
+cualquiera de las
+posteriores versiones publicadas por la "FSF". Si el programa no
+especifica una versi&oacute;n en especial de esta Licencia, usted
+puede elegir
+entre cualquiera de las versiones que han sido publicadas por la "FSF".
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>10.</b></font>
+Si usted desea incorporar partes del Programa en otros Programas de
+software libre cuyas condiciones de distribuci&oacute;n sean
+distintas, deber&aacute;
+escribir al autor solicitando su autorizaci&oacute;n. Para
+programas de
+software protegidas por la "FSF Free Software Foundation",
+deber&aacute;
+escribir a la "FSF" solicitando autorizaci&oacute;n, en ocasiones
+hacemos
+excepciones. Nuestra decisi&oacute;n ser&aacute; guiada por dos
+metas principales:
+</font></font></p>
+
+<ul>
+
+ <font face="Verdana" size="1"><font face="Verdana" size="2"> <li> mantener el
+estado de libertad de todos los derivados de nuestro software libre </li>
+
+ <li> promover el uso comunitario y compartido del software en
+general </li>
+
+ </font></font>
+</ul>
+
+<p><font face="Verdana" size="1"><font face="Verdana" size="2"><br>
+
+<br>
+
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="3"><b>NO
+EXISTE GARANTIA ALGUNA</b></font>
+</font></font></p>
+
+<hr>
+<p><font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>11.</b></font>
+DEBIDO A QUE EL PROGRAMA SE OTORGA LIBRE DE CARGOS Y REGALIAS, NO
+EXISTE NINGUNA GARANTIA PARA EL MISMO HASTA DONDE LO PERMITA LA LEY
+APLICABLE. A EXCEPCION DE QUE SE INDIQUE OTRA COSA, LOS PROPIETARIOS DE
+LOS DERECHOS DE AUTOR PROPORCIONAN EL PROGRAMA "COMO ES" SIN NINGUNA
+GARANTIA DE NINGUN TIPO, YA SEA EXPLICITA O IMPLICITA, INCLUYENDO, PERO
+NO LIMITADA A, LAS GARANTIAS QUE IMPLICA EL MERCADEO Y EJERCICIO DE UN
+PROPOSITO EN PARTICULAR. CUALQUIER RIESGO DEBIDO A LA CALIDAD Y
+DESEMPE&Ntilde;O DEL PROGRAMA ES TOMADO COMPLETAMENTE POR USTED. SI
+EL
+SOFTWARE MUESTRA ALGUN DEFECTO, USTED CUBRIRA LOS COSTOS DE CUALQUIER
+SERVICIO, REPARACION O CORRECCION DE SUS EQUIPOS Y/O SOFTWARE QUE
+REQUIERA.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="4"><b>12.</b></font>
+EN NINGUN CASO NI BAJO NINGUNA CIRCUNSTANCIA EXCEPTO BAJO SOLICITUD DE
+LA LEY O DE COMUN ACUERDO POR ESCRITO, NINGUN PROPIETARIO DE LOS
+DERECHOS DE AUTOR NI TERCERAS PERSONAS QUE PUDIERAN MODIFICAR Y/O
+REDISTRIBUIR EL PROGRAMA COMO SE PERMITE ARRIBA, SERAN RESPONSABLES DE
+LOS DA&Ntilde;OS CORRESPONDIENTES AL USO O IMPOSIBILIDAD DE USAR EL
+PROGRAMA,
+SIN IMPORTAR SI SON DA&Ntilde;OS GENERALES, ESPECIALES,
+INCIDENTALES O
+CONSEQUENTES CORRESPONDIENTES AL USO O IMPOSIBILIDAD DE USAR EL
+PROGRAMA (INCLUYENDO PERO NO LIMITADO A LA PERDIDA DE INFORMACION O
+DETERIORO DE LA MISMA AFECTANDOLO A USTED, A TERCERAS PERSONAS QUE SEA
+POR FALLAS EN LA OPERACION DEL PROGRAMA O SU INTERACCION CON OTROS
+PROGRAMAS) INCLUSIVE SI TAL PROPIETARIO U OTRAS PERSONAS HAYAN SIDO
+NOTIFICADAS DE TALES FALLAS Y DE LA POSIBILIDAD DE TALES
+DA&Ntilde;OS.
+</font></font></p>
+
+<p>
+</p>
+
+<hr>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><font size="3"><b>FIN
+DE TERMINOS Y CONDICIONES</b></font>
+</font></font>
+<p><font face="Verdana" size="1"><font face="Verdana" size="2"><br>
+
+<br>
+
+<br>
+
+<br>
+
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2"><a name="aplicacion"></a><font size="3"><b>C&oacute;mo aplicar estos
+t&eacute;rminos a sus nuevos programas?</b></font>
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Si usted
+desarrolla un nuevo Programa y desea que sea lo m&aacute;s
+p&uacute;blico posible, el
+mejor modo de hacerlo es haciendolo Software Libre donde toda persona
+lo puede redistribui y cambiar bajo estos t&eacute;rminos.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Para hacer
+esto, agregue las siguientes notas al programa. Es m&aacute;s
+seguro
+agregarlas al inicio de cada archivo del c&oacute;digo fuente para
+notificar
+de manera m&aacute;s efectiva la ausencia de garant&iacute;a; y
+cada archivo debe de
+contener al menos la l&iacute;nea de "Copyright" o derechos de
+autor y una
+referencia de donde se puede encontrar la nota completa.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">ejemplo:
+</font></font></p>
+
+<p>
+</p>
+
+<dir>
+<pre><font face="Verdana" size="1"><font face="Verdana" size="2"><font size="3"><i>esta l&iacute;nea que contenga el nombre del programa y una idea de lo que hace.<br>Copyright (C) A&Ntilde;O nombre del autor<br><br>Este programa es Software Libre; usted puede redistribuirlo<br>y/o modificarlo bajo los t&eacute;rminos de la "GNU General Public<br>License" como lo publica la "FSF Free Software Foundation",<br>o (a su elecci&oacute;n) de cualquier versi&oacute;n posterior.<br><br>Este programa es distribuido con la esperanza de que le ser&aacute;<br>&uacute;til, pero SIN NINGUNA GARANTIA; incluso sin la garant&iacute;a<br>impl&iacute;cita por el MERCADEO o EJERCICIO DE ALGUN PROPOSITO en<br>particular. Vea la "GNU General Public License" para m&aacute;s<br>detalles.<br><br>Usted debe haber recibido una copia de la "GNU General Public<br>License" junto con este programa, si no, escriba a la "FSF<br>Free Software Foundation, Inc.", 59 Temple Place - Suite 330,<br>Boston, MA 02111-1307, USA.<br></i></font></font></font></pre>
+
+</dir>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Adicionalmente agregue
+informaci&oacute;n de c&oacute;mo contactarle por correo
+electr&oacute;nico y convencional.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Si el programa es
+interactivo, ponga en la salida del programa una nota corta al iniciar
+el modo interactivo:
+</font></font></p>
+
+<p>
+</p>
+
+<dir>
+<pre><font face="Verdana" size="1"><font face="Verdana" size="2"><font size="3"><i>Gnomovision version 69, Copyright (C) A&Ntilde;O nombre del autor<br>Gnomovision no tiene NINGUNA GARANTIA, para m&aacute;s detalles<br>escriba 'show w'. Este es Software Libre, y usted est&aacute;<br>permitido para redistribuirlo bajo ciertas condiciones;<br>escriba 'show c' para m&aacute;s detalles.<br></i></font></font></font></pre>
+
+</dir>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Estos
+supuestos comandos 'show w' y 'show c' deber&aacute;n mostrar las
+partes
+apropiadas de la "GPL General Public License". Por supuesto, los
+comandos que utilice pueden ser distintos, pueden ser incluso "clicks"
+del rat&oacute;n, opciones de men&uacute;s etc, lo
+m&aacute;s apropiado para su programa.
+</font></font></p>
+
+<p>
+<font face="Verdana" size="1"><font face="Verdana" size="2">Usted
+deber&iacute;a hacer que su jefe de proyecto (si trabaja como
+programador) o
+su escuela, si aplica, firme una "declaraci&oacute;n de derechos de
+autor"
+para el programa, si se necesita. Aqu&iacute; hay un ejemplo,
+modifique los
+nombres:
+</font></font></p>
+
+<p>
+</p>
+
+<dir>
+<pre><font face="Verdana" size="1"><font face="Verdana" size="2"><font size="3"><i>Yoyodyne, Inc., aqu&iacute; vienen las declaraciones de derechos de autor<br>inter&eacute;s en el programa 'Gnomovision'<br>(lo que make pasa al compilador)<br>escrito por James Hacker.<br><br>firma de Ty Coon, 1 de Abril 1989<br>Ty Coon, Presidente<br></i></font></font></font></pre>
+
+</dir>
+
+<font face="Verdana" size="1"><font face="Verdana" size="2">Esta
+Licencia P&uacute;blica General no permite incorporar su programa
+en programas
+propietarios. Si su programa es una librer&iacute;a de subrutinas,
+puede ser
+m&aacute;s &uacute;til permitir que se ligue en tiempo de
+compilaci&oacute;n o ejecuci&oacute;n a
+aplicaciones propietarias. Si esto es lo que quiere hacer, use la
+licencia P&uacute;blica General para Librer&iacute;as en lugar
+de esta licencia.
+</font></font>
+</body>
+</html>
diff --git a/logo/src/xlogo/gpl/gpl-fr.html b/logo/src/xlogo/gpl/gpl-fr.html
new file mode 100644
index 0000000..33cef97
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-fr.html
@@ -0,0 +1,580 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+ <title>Licence</title>
+</head>
+<body>
+<em>
+<p>
+ Ceci est une traduction non officielle de la GNU General Public
+ License en fran&ccedil;ais. Elle n'a pas &eacute;t&eacute; publi&eacute;e par la Free
+ Software Foundation, et ne d&eacute;termine pas les termes de
+ distribution pour les logiciels qui utilisent la GNU GPL, seul
+ le texte anglais original de la GNU GPL d&eacute;terminent ces
+ termes. Cependant, nous esp&eacute;rons que cette traduction aidera les
+ francophones &agrave; mieux comprendre la GNU GPL.
+
+ </p>
+
+ </em>
+
+
+<h2>Licence Publique G&eacute;n&eacute;rale GNU</h2>
+
+
+<p>Les licences de la plupart des logiciels sont
+ con&ccedil;ues pour vous enlever toute libert&eacute; de les partager et de les
+ modifier. </p>
+
+
+<p>A contrario, la Licence Publique G&eacute;n&eacute;rale est destin&eacute;e &agrave; garantir
+ votre libert&eacute; de partager et de modifier les logiciels libres,
+ et &agrave; assurer que ces logiciels soient libres pour tous leurs
+ utilisateurs. </p>
+
+
+<p>La pr&eacute;sente Licence Publique G&eacute;n&eacute;rale s'applique &agrave; la
+ plupart des logiciels de la Free Software Foundation, ainsi
+ qu'&agrave; tout autre programme pour lequel ses auteurs s'engagent &agrave;
+ l'utiliser.</p>
+
+
+<p>(Certains autres logiciels de la Free Software Foundation
+ sont couverts par la GNU Lesser General Public License &agrave; la place.)</p>
+
+
+<p>Vous pouvez aussi l'appliquer aux programmes qui sont les v&ocirc;tres.</p>
+
+
+<p>Quand nous parlons de logiciels libres, nous parlons de
+ libert&eacute;, non de prix.</p>
+
+
+<p>Nos licences publiques g&eacute;n&eacute;rales sont con&ccedil;ues pour vous donner
+ l'assurance d'&ecirc;tre libres de distribuer des copies des
+ logiciels libres (et de facturer ce service, si vous le
+ souhaitez), de recevoir le code source ou de pouvoir
+ l'obtenir si vous le souhaitez, de pouvoir modifier les
+ logiciels ou en utiliser des &eacute;l&eacute;ments dans de nouveaux
+ programmes libres et de savoir que vous pouvez le
+ faire. </p>
+
+
+<p>Pour prot&eacute;ger vos droits, il nous est n&eacute;cessaire d'imposer
+ des limitations qui interdisent &agrave; quiconque de vous refuser
+ ces droits ou de vous demander d'y renoncer.</p>
+
+
+<p>Certaines responsabilit&eacute;s vous incombent en raison de ces
+ limitations si vous distribuez des copies de ces logiciels, ou
+ si vous les modifiez.</p>
+
+
+<p>Par exemple, si vous distribuez des copies d'un tel
+ programme, &agrave; titre gratuit ou contre une r&eacute;mun&eacute;ration, vous devez
+ accorder aux destinataires tous les droits dont vous disposez.
+ </p>
+
+
+<p>Vous devez vous assurer qu'eux aussi re&ccedil;oivent ou puissent
+ disposer du code source.</p>
+
+
+<p>Et vous devez leur montrer les pr&eacute;sentes conditions afin
+ qu'ils aient connaissance de leurs droits.</p>
+
+
+<p>Nous prot&eacute;geons vos droits en deux &eacute;tapes : (1) nous sommes
+ titulaires des droits d'auteur du logiciel, et
+ (2) nous vous d&eacute;livrons cette licence, qui vous donne
+ l'autorisation l&eacute;gale de copier, distribuer et/ou modifier
+ le logiciel.</p>
+
+
+<p>En outre, pour la protection de chaque auteur ainsi que la n&ocirc;tre,
+ nous voulons nous assurer que chacun comprenne que ce logiciel libre ne fait l'objet
+ d'aucune garantie.</p>
+
+
+<p>Si le logiciel est modifi&eacute; par quelqu'un d'autre puis
+ transmis &agrave; des tiers, nous voulons que les destinataires
+ soient mis au courant que ce qu'ils ont re&ccedil;u n'est pas le logiciel d'origine, de sorte
+ que tout probl&egrave;me introduit par d'autres ne puisse entacher
+ la r&eacute;putation de
+ l'auteur originel.</p>
+
+
+<p>En d&eacute;finitive, un programme libre restera &agrave; la merci des
+ brevets de logiciels.</p>
+
+
+<p>Nous souhaitons &eacute;viter le risque que les redistributeurs
+ d'un programme libre fassent des demandes individuelles de
+ licence de brevet, ceci ayant pour effet de rendre le programme
+ propri&eacute;taire.
+ </p>
+
+
+<p>Pour &eacute;viter cela, nous &eacute;tablissons clairement que toute licence de brevet
+ doit &ecirc;tre conc&eacute;d&eacute;e de fa&ccedil;on &agrave; ce que l'usage en soit libre pour tous
+ ou bien qu'aucune licence ne soit conc&eacute;d&eacute;e.</p>
+
+
+<p>Les termes exacts et les conditions de copie, distribution et
+ modification sont les suivants:</p>
+
+
+<h2>Conditions de copie, distribution
+ et modification de la Licence Publique G&eacute;n&eacute;rale GNU.</h2>
+
+
+<p>0. La pr&eacute;sente Licence s'applique &agrave; tout programme
+ ou tout autre ouvrage contenant un avis, appos&eacute; par le titulaire des
+ droits d'auteur, stipulant qu'il peut &ecirc;tre distribu&eacute; au titre
+ des conditions de la pr&eacute;sente Licence Publique
+ G&eacute;n&eacute;rale.</p>
+
+
+<p>Ci-apr&egrave;s, le "Programme" d&eacute;signe l'un quelconque de ces
+ programmes ou ouvrages, et un "ouvrage fond&eacute; sur le Programme"
+ d&eacute;signe soit le Programme, soit un ouvrage qui en d&eacute;rive au
+ titre des lois sur le droit d'auteur : en d'autres termes,
+ un ouvrage contenant le Programme ou une partie de
+ ce dernier, soit &agrave; l'identique, soit avec des modifications et/ou
+ traduit dans un autre langage.</p>
+
+
+<p>(Ci-apr&egrave;s, le terme "modification" implique, sans s'y r&eacute;duire, le terme traduction)</p>
+
+
+<p>Chaque concessionaire sera d&eacute;sign&eacute; par "vous".</p>
+
+
+<p>Les activit&eacute;s autres que la copie, la distribution et
+ la modification ne sont pas couvertes par la pr&eacute;sente Licence
+ ; elles sont hors de son champ d'application.</p>
+
+
+<p>L'op&eacute;ration consistant &agrave; ex&eacute;cuter le Programme n'est soumise &agrave; aucune limitation
+ et les sorties du programme ne sont couvertes que si leur
+ contenu constitue un ouvrage fond&eacute; sur le Programme
+ (ind&eacute;pendamment du fait qu'il ait &eacute;t&eacute; r&eacute;alis&eacute; par l'ex&eacute;cution
+ du Programme).</p>
+
+
+<p>La validit&eacute; de ce qui pr&eacute;c&egrave;de d&eacute;pend de ce que fait le
+ Programme.</p>
+
+
+<p>1. Vous pouvez copier et distribuer des copies &agrave;
+ l'identique du code source du Programme tel que vous l'avez
+ re&ccedil;u, sur n'importe quel support, du moment que vous apposiez
+ sur chaque copie, de mani&egrave;re ad hoc et parfaitement
+ visible, l'avis de droit d'auteur ad&eacute;quat et
+ une exon&eacute;ration de garantie ; que vous gardiez intacts tous les
+ avis faisant r&eacute;f&eacute;rence &agrave; la pr&eacute;sente Licence et &agrave; l'absence de
+ toute garantie ; et que vous fournissiez &agrave; tout destinataire du
+ Programme autre que vous-m&ecirc;me un exemplaire de la pr&eacute;sente
+ Licence en m&ecirc;me temps que le Programme.</p>
+
+
+<p>Vous pouvez faire payer l'acte physique de
+ transmission d'une copie, et vous pouvez, &agrave; votre discr&eacute;tion,
+ proposer une garantie contre r&eacute;mun&eacute;ration.</p>
+
+
+<p>2. Vous pouvez modifier votre copie ou des copies du Programme
+ ou n'importe quelle partie de celui-ci, cr&eacute;ant ainsi un ouvrage
+ fond&eacute; sur le Programme, et copier et distribuer de telles modifications ou ouvrage selon les
+ termes de l'Article 1 ci-dessus, &agrave; condition de vous conformer &eacute;galement
+ &agrave; chacune des obligations suivantes :</p>
+
+
+<p>a) Vous devez munir les fichiers modifi&eacute;s d'avis bien visibles
+ stipulants que vous avez modifi&eacute; ces
+ fichiers, ainsi que la date de chaque modification ;</p>
+
+
+<p>b) Vous devez prendre les dispositions n&eacute;cessaires pour que
+ tout ouvrage
+ que vous distribuez ou publiez, et qui, en totalit&eacute; ou en partie,
+ contient ou est fond&eacute; sur le Programme - ou une partie
+ quelconque de ce dernier - soit conc&eacute;d&eacute; comme un tout,
+ &agrave; titre gratuit, &agrave; n'importe quel tiers, au titre des conditions
+ de la pr&eacute;sente Licence.</p>
+
+
+<p>c) Si le programme modifi&eacute; lit habituellement des instructions
+ de fa&ccedil;on interactive lorsqu'on l'ex&eacute;cute, vous devez,
+ quand il commence son ex&eacute;cution pour ladite
+ utilisation interactive de la mani&egrave;re la plus usuelle, faire
+ en sorte qu'il imprime ou affiche une annonce
+ comprenant un avis de droit d'auteur ad hoc, et un avis
+ stipulant qu'il n'y a pas de garantie (ou bien indiquant que
+ c'est vous qui fournissez la garantie), et que
+ les utilisateurs peuvent redistribuer le programme en respectant
+ les pr&eacute;sentes obligations, et expliquant &agrave; l'utilisateur comment
+ voir une copie de la pr&eacute;sente Licence.</p>
+
+
+<p>(Exception : si le Programme est lui-m&ecirc;me interactif
+ mais n'imprime pas habituellement une telle annonce, votre
+ ouvrage fond&eacute; sur le Programme n'est pas oblig&eacute;
+ d'imprimer une annonce).</p>
+
+
+<p>Ces obligations s'appliquent &agrave; l'ouvrage modifi&eacute;
+ pris comme un tout.
+ </p>
+
+
+<p>Si des &eacute;l&eacute;ments identifiables
+ de cet ouvrage ne sont pas fond&eacute;s sur le Programme et peuvent
+ raisonnablement &ecirc;tre consid&eacute;r&eacute;s comme des ouvrages ind&eacute;pendants
+ distincts en eux m&ecirc;mes, alors la pr&eacute;sente Licence et
+ ses conditions ne
+ s'appliquent pas &agrave; ces &eacute;l&eacute;ments lorsque vous les distribuez
+ en tant qu'ouvrages distincts.</p>
+
+
+<p>
+ Mais lorsque vous distribuez ces m&ecirc;mes &eacute;l&eacute;ments comme partie
+ d'un tout, lequel constitue un ouvrage fond&eacute; sur le Programme,
+ la distribution de ce tout doit &ecirc;tre soumise aux conditions de
+ la pr&eacute;sente Licence, et les autorisations qu'elle octroie aux
+ autres concessionnaires s'&eacute;tendent &agrave; l'ensemble de l'ouvrage et
+ par cons&eacute;quent &agrave; chaque et toute partie indiff&eacute;rement de qui
+ l'a &eacute;crite.
+ </p>
+
+
+<p>Par cons&eacute;quent, l'objet du pr&eacute;sent article n'est pas de revendiquer des droits
+ ou de contester vos droits sur un ouvrage enti&egrave;rement &eacute;crit par
+ vous; son objet est plut&ocirc;t d'exercer le droit de contr&ocirc;ler
+ la distribution d'ouvrages d&eacute;riv&eacute;s ou d'ouvrages collectifs fond&eacute;s
+ sur le Programme.
+ </p>
+
+
+<p>
+ De plus, la simple proximit&eacute; du Programme avec un autre
+ ouvrage qui n'est pas fond&eacute; sur le Programme (ou un ouvrage
+ fond&eacute; sur le Programme) sur une partition d'un espace de
+ stockage ou un support de distribution ne place pas cet autre
+ ouvrage dans le champ d'application de la pr&eacute;sente Licence.
+ </p>
+
+
+<p>3. Vous pouvez copier et distribuer le Programme (ou un
+ ouvrage fond&eacute; sur lui, selon l'Article 2) sous forme de code
+ objet ou d'ex&eacute;cutable, selon les termes des Articles 1 et 2
+ ci-dessus, &agrave; condition que vous accomplissiez l'un des points
+ suivants :</p>
+
+
+<p>a) L'accompagner de l'int&eacute;gralit&eacute; du code source correspondant,
+ sous une forme lisible par un ordinateur, lequel doit &ecirc;tre distribu&eacute;
+ au titre
+ des termes des Articles 1 et 2 ci-dessus, sur un support
+ habituellement utilis&eacute; pour l'&eacute;change de logiciels; ou,</p>
+
+
+<p>b) L'accompagner d'une proposition &eacute;crite, valable pendant
+ au moins trois ans, de fournir &agrave; tout tiers, &agrave; un tarif qui
+ ne soit pas sup&eacute;rieur &agrave; ce que vous co&ucirc;te l'acte physique de
+ r&eacute;aliser une distribution source, une copie int&eacute;grale du code
+ source correspondant sous une forme lisible par un
+ ordinateur, qui sera distribu&eacute;e au titre des termes des
+ Articles 1 et 2 ci-dessus, sur un support habituellement
+ utilis&eacute; pour l'&eacute;change de logiciels; ou,
+ </p>
+
+
+<p>c) L'accompagner des informations re&ccedil;ues par vous concernant
+la proposition de distribution du code source correspondant. (Cette
+solution n'est autoris&eacute;e que dans le cas d'une distribution non
+commerciale et seulement si vous avez re&ccedil;u le programme sous
+forme de code objet ou d'ex&eacute;cutable accompagn&eacute; d'une
+telle proposition - en conformit&eacute; avec le sous-Article b
+ci-dessus.) </p>
+
+
+<p>Le code source d'un ouvrage d&eacute;signe la forme favorite pour
+ travailler &agrave; des modifications de cet ouvrage. Pour un
+ ouvrage ex&eacute;cutable, le code source int&eacute;gral d&eacute;signe la
+ totalit&eacute; du code source de la totalit&eacute; des modules qu'il
+ contient, ainsi que les &eacute;ventuels fichiers de d&eacute;finition des
+ interfaces qui y sont associ&eacute;s, ainsi que les scripts utilis&eacute;s
+ pour contr&ocirc;ler la compilation et l'installation de
+ l'ex&eacute;cutable. Cependant, par exception sp&eacute;ciale, le code
+ source distribu&eacute; n'est pas cens&eacute; inclure quoi que ce soit de
+ normalement distribu&eacute; (que ce soit sous forme source ou
+ binaire) avec les composants principaux (compilateur, noyau,
+ et autre) du syst&egrave;me d'exploitation sur lequel l'ex&eacute;cutable
+ tourne, &agrave; moins que ce composant lui-m&ecirc;me n'accompagne
+ l'ex&eacute;cutable.
+ </p>
+
+
+<p>
+ Si distribuer un ex&eacute;cutable ou un code objet consiste &agrave;
+ offrir un acc&egrave;s permettant leur copie depuis un
+ endroit particulier, alors l'offre d'un acc&egrave;s &eacute;quivalent pour
+ copier le code source depuis le m&ecirc;me endroit compte comme une
+ distribution du code source - m&ecirc;me si les tiers ne sont pas
+ contraints de copier le source en m&ecirc;me temps que le code
+ objet.
+ </p>
+
+
+<p>4. Vous ne pouvez copier, modifier, conc&eacute;der en sous-licence, ou distribuer
+ le Programme, sauf tel qu'express&eacute;ment pr&eacute;vu par la pr&eacute;sente Licence.
+ Toute tentative de copier, modifier, conc&eacute;der en sous-licence, ou
+ distribuer le Programme d'une autre mani&egrave;re est r&eacute;put&eacute;e non valable, et met
+ imm&eacute;diatement fin &agrave; vos droits au titre de la pr&eacute;sente Licence.
+ Toutefois, les tiers ayant re&ccedil;u de vous des copies, ou des droits,
+ au titre de la pr&eacute;sente Licence ne verront pas leurs autorisations r&eacute;sili&eacute;es
+ aussi longtemps que ledits tiers se conforment pleinement &agrave; elle.
+ </p>
+
+
+<p>5. Vous n'&ecirc;tes pas oblig&eacute; d'accepter la pr&eacute;sente Licence &eacute;tant donn&eacute; que vous ne
+ l'avez pas sign&eacute;e. Cependant, rien d'autre
+ ne vous accorde l'autorisation de modifier ou
+ distribuer le Programme ou les ouvrages fond&eacute;s sur lui. Ces actions sont
+ interdites par la loi si vous n'acceptez pas la pr&eacute;sente Licence.
+ En cons&eacute;quence, en modifiant ou
+ distribuant le Programme (ou un ouvrage quelconque fond&eacute; sur
+ le Programme), vous signifiez votre acceptation de la pr&eacute;sente Licence en le faisant,
+ et de toutes ses conditions concernant la copie, la
+ distribution ou la modification du Programme ou d'ouvrages fond&eacute;s
+ sur lui.</p>
+
+
+<p>
+ 6. Chaque fois que vous redistribuez le Programme (ou n'importe quel
+ ouvrage fond&eacute; sur le Programme), une licence est automatiquement
+ conc&eacute;d&eacute;e au destinataire par le conc&eacute;dant originel de la licence,
+ l'autorisant &agrave;
+ copier, distribuer ou modifier le Programme, sous r&eacute;serve
+ des pr&eacute;sentes conditions. Vous ne pouvez imposer
+ une quelconque limitation suppl&eacute;mentaire &agrave; l'exercice des
+ droits octroy&eacute;s au titre des pr&eacute;sentes par le destinataire. Vous
+ n'avez pas la responsabilit&eacute; d'imposer le respect de
+ la pr&eacute;sente Licence &agrave; des tiers. </p>
+
+
+<p>
+7. Si, cons&eacute;quement &agrave; une d&eacute;cision de justice ou l'all&eacute;gation d'une
+transgression de brevet ou pour toute autre raison (non limit&eacute;e &agrave; un
+probleme de brevet), des obligations vous sont impos&eacute;es (que ce soit
+par jugement, conciliation ou autre) qui contredisent les conditions de
+la pr&eacute;sente Licence, elles ne vous excusent pas des conditions de la
+pr&eacute;sente Licence. Si vous ne pouvez distribuer de mani&egrave;re &agrave; satisfaire
+simultan&eacute;ment vos obligations au titre de la pr&eacute;sente Licence et toute
+autre obligation pertinente, alors il en d&eacute;coule que vous ne pouvez pas
+du tout distribuer le Programme. Par exemple, si une licence de brevet
+ne permettait pas une redistribution sans redevance du Programme par
+tous ceux qui re&ccedil;oivent une copie directement ou indirectement par
+votre interm&eacute;diaire, alors la seule fa&ccedil;on pour vous de satisfaire &agrave; la
+fois &agrave; la licence du brevet et &agrave; la pr&eacute;sente Licence serait de vous
+abstenir totalement de toute distribution du Programme. </p>
+
+
+<p>Si une partie quelconque de cet article est tenue pour
+ nulle ou inopposable dans une circonstance particuli&egrave;re
+ quelconque, l'intention est que le reste de l'article
+ s'applique. La totalit&eacute; de la section s'appliquera dans toutes les
+ autres circonstances.</p>
+
+
+<p>Cet article n'a pas pour but de vous induire &agrave; transgresser un
+ quelconque brevet ou d'autres revendications &agrave; un droit de propri&eacute;t&eacute; ou
+ &agrave; contester la validit&eacute; de la moindre de ces revendications ; cet article a pour
+ seul objectif de prot&eacute;ger l'int&eacute;grit&eacute; du syst&egrave;me de distribution
+ du logiciel libre, qui est mis en oeuvre par la pratique des licenses
+ publiques. De nombreuses personnes ont fait de g&eacute;n&eacute;reuses contributions
+ au large spectre de logiciels distribu&eacute;s par ce syst&egrave;me en se fiant &agrave;
+ l'application coh&eacute;rente de ce syst&egrave;me ; il appartient &agrave; chaque auteur/donateur
+ de d&eacute;cider si il ou elle veut distribuer du logiciel par l'interm&eacute;diaire
+ d'un quelconque autre syst&egrave;me et un concessionaire ne peut imposer ce
+ choix.
+ </p>
+
+
+<p>Cet article a pour but de rendre totalement limpide ce que l'on pense
+ &ecirc;tre une cons&eacute;quence du reste de la pr&eacute;sente Licence.</p>
+
+
+<p>8. Si la distribution et/ou l'utilisation du Programme est limit&eacute;e
+ dans certains pays que ce soit par des brevets ou par des interfaces soumises
+ au droit d'auteur, le titulaire originel des droits d'auteur
+ qui d&eacute;cide de couvrir le Programme par la pr&eacute;sente Licence peut ajouter
+ une limitation g&eacute;ographique de distribution explicite qui exclue ces pays afin
+ que la distribution soit permise seulement dans ou entre les pays qui ne sont
+ pas ainsi exclus. Dans ce cas, la pr&eacute;sente Licence incorpore la limitation
+ comme si elle &eacute;tait &eacute;crite dans le corps de la pr&eacute;sente Licence.
+ </p>
+
+
+<p>9. La Free Software Foundation peut, de temps &agrave; autre, publier des
+ versions r&eacute;vis&eacute;es et/ou nouvelles de la Licence Publique G&eacute;n&eacute;rale. De telles
+ nouvelles versions seront similaires &agrave; la pr&eacute;sente version dans l'esprit
+ mais pourront diff&eacute;rer dans le d&eacute;tail pour prendre en compte
+ de nouvelles probl&eacute;matiques ou inqui&eacute;tudes.
+ </p>
+
+
+<p>Chaque version poss&egrave;de un num&eacute;ro de version la distinguant. Si le Programme
+ pr&eacute;cise le num&eacute;ro de version de la pr&eacute;sente Licence qui s'y applique
+ et "une version ult&eacute;rieure quelconque", vous avez le choix
+ de suivre les
+ conditions de
+ la pr&eacute;sente version ou de toute autre version ult&eacute;rieure publi&eacute;e par la
+ Free Software Foundation. Si le Programme ne sp&eacute;cifie aucun num&eacute;ro de
+ version de la pr&eacute;sente Licence, vous pouvez choisir une version quelconque
+ publi&eacute;e par la Free Software Foundation &agrave; quelque moment que ce soit.</p>
+
+
+<p>10. Si vous souhaitez incorporer des parties du Programme
+ dans d'autres programmes libres dont les conditions de
+ distribution sont diff&eacute;rentes, &eacute;crivez &agrave; l'auteur pour lui en demander
+ l'autorisation. Pour les logiciels dont la Free Software Foundation est
+ titulaire des droits d'auteur, &eacute;crivez &agrave; la Free Software Foundation ;
+ nous faisons parfois des exceptions dans ce sens. Notre d&eacute;cision sera guid&eacute;e
+ par le double objectif de pr&eacute;server le statut libre de tous les d&eacute;riv&eacute;s de nos
+ logiciels libres et de promouvoir le partage et la r&eacute;utilisation des logiciels
+ en g&eacute;n&eacute;ral.
+ </p>
+
+
+<h2>ABSENCE DE GARANTIE</h2>
+
+
+<p>11. COMME LA LICENCE DU PROGRAMME EST CONCEDEE A TITRE GRATUIT,
+ AUCUNE GARANTIE NE S'APPLIQUE AU PROGRAMME, DANS LES LIMITES
+ AUTORISEES PAR LA LOI APPLICABLE. SAUF MENTION CONTRAIRE ECRITE,
+ LES TITULAIRES DU DROIT D'AUTEUR ET/OU LES AUTRES PARTIES FOURNISSENT
+ LE PROGRAMME "EN L'ETAT", SANS AUCUNE GARANTIE DE
+ QUELQUE NATURE QUE CE SOIT, EXPRESSE OU IMPLICITE, Y COMPRIS, MAIS
+ SANS Y ETRE LIMITE, LES GARANTIES IMPLICITES DE COMMERCIABILITE ET
+ DE LA CONFORMITE A UNE UTILISATION PARTICULIERE. VOUS
+ ASSUMEZ LA TOTALITE DES RISQUES LIES A LA QUALITE ET AUX PERFORMANCES DU PROGRAMME.
+ SI LE PROGRAMME SE REVELAIT DEFECTUEUX, LE COUT DE L'ENTRETIEN,
+ DES REPARATIONS OU DES CORRECTIONS NECESSAIRES
+ VOUS INCOMBENT INTEGRALEMENT.</p>
+
+
+<p>12.
+EN AUCUN CAS, SAUF LORSQUE LA LOI APPLICABLE OU UNE CONVENTION ECRITE
+L'EXIGE, UN TITULAIRE DE DROIT D'AUTEUR QUEL QU'IL SOIT, OU TOUTE
+PARTIE QUI POURRAIT MODIFIER ET/OU REDISTRIBUER LE PROGRAMME COMME
+PERMIS CI-DESSUS, NE POURRAIT ETRE TENU POUR RESPONSABLE A VOTRE EGARD
+DES DOMMAGES, INCLUANT LES DOMMAGES GENERIQUES, SPECIFIQUES,
+SECONDAIRES OU CONSECUTIFS, RESULTANT DE L'UTILISATION OU DE
+L'INCAPACITE D'UTILISER LE PROGRAMME (Y COMPRIS, MAIS SANS Y ETRE
+LIMITE, LA PERTE DE DONNEES, OU LE FAIT QUE DES DONNEES SOIENT RENDUES
+IMPRECISES, OU LES PERTES EPROUVEES PAR VOUS OU PAR DES TIERS, OU LE
+FAIT QUE LE PROGRAMME ECHOUE A INTEROPERER AVEC UN AUTRE PROGRAMME QUEL
+QU'IL SOIT) MEME SI LE DIT TITULAIRE DU DROIT D'AUTEUR OU LE PARTIE
+CONCERNEE A ETE AVERTI DE L'EVENTUALITE DE TELS DOMMAGES.
+</p>
+
+
+<h2>FIN DES CONDITIONS</h2>
+
+
+<h2>Comment appliquer ces conditions &agrave; vos nouveaux programmes</h2>
+
+
+<p>
+Si vous d&eacute;veloppez un nouveau programme, et si vous voulez qu'il soit
+de la plus grande utilit&eacute; possible pour le public, le meilleur moyen
+d'y parvenir est d'en faire un logiciel libre que chacun peut
+redistribuer et modifier au titre des pr&eacute;sentes conditions.</p>
+
+
+<p>Pour ce faire, munissez le programme des avis qui suivent.
+ Le plus s&ucirc;r est de les ajouter au d&eacute;but de chaque fichier source
+ pour v&eacute;hiculer le plus efficacement possible l'absence
+ de toute garantie ; chaque fichier devrait aussi contenir au moins
+ la ligne "copyright" et une indication de l'endroit o&ugrave; se trouve l'avis
+ complet.
+ </p>
+
+
+<p> [Une ligne donnant le nom du programme et une courte id&eacute;e de ce qu'il fait.]
+
+ Copyright (C) [ann&eacute;e] [nom de l'auteur]
+
+ Ce programme est un logiciel libre ; vous pouvez le
+ redistribuer et/ou le modifier au titre des clauses de la
+ Licence Publique G&eacute;n&eacute;rale GNU, telle que publi&eacute;e par la Free
+ Software Foundation ; soit la version 2 de la Licence, ou (&agrave;
+ votre discr&eacute;tion) une version ult&eacute;rieure quelconque.
+
+ Ce programme est distribu&eacute; dans l'espoir qu'il sera utile,
+ mais SANS AUCUNE GARANTIE ; sans m&ecirc;me une garantie implicite de
+ COMMERCIABILITE ou DE CONFORMITE A UNE UTILISATION
+ PARTICULIERE. Voir la Licence Publique
+ G&eacute;n&eacute;rale GNU pour plus de d&eacute;tails.
+
+ Vous devriez avoir re&ccedil;u un exemplaire de la Licence Publique
+ G&eacute;n&eacute;rale GNU avec ce programme ; si ce n'est pas le cas,
+ &eacute;crivez &agrave; la Free Software Foundation Inc., 51 Franklin
+ Street, Fifth Floor, Boston, MA 02110-1301, USA.</p>
+
+
+<p>Ajoutez aussi des informations sur la mani&egrave;re de vous contacter
+ par courrier &eacute;lectronique et courrier postal.</p>
+
+
+<p>Si le programme est interactif, faites en sorte qu'il
+ affiche un court avis tel que celui-ci lorsqu'il d&eacute;marre en
+ mode interactif :
+ </p>
+
+
+<p>Gnomovision version 69, Copyright (C) ann&eacute;e nom de l'auteur
+ Gnomovision n'est accompagn&eacute; d'ABSOLUMENT AUCUNE GARANTIE ; pour plus de
+ d&eacute;tails tapez "show w". Ceci est un logiciel libre et vous &ecirc;tes
+ invit&eacute; &agrave; le redistribuer en respectant certaines obligations ; pour plus
+ de d&eacute;tails tapez "show c".</p>
+
+
+<p>Les instructions hypoth&eacute;tiques "show w" et "show c" sont suppos&eacute;es montrer
+ les parties ad hoc de la Licence Publique G&eacute;n&eacute;rale. Bien
+ entendu, les instructions que vous utilisez peuvent porter d'autres
+ noms que "show w" et "show c" ; elles peuvent m&ecirc;me &ecirc;tre des
+ clics de souris ou des &eacute;l&eacute;ments d'un menu ou tout ce qui convient &agrave;
+ votre programme.</p>
+
+
+<p>Vous devriez aussi obtenir de votre employeur (si vous
+ travaillez en tant que d&eacute;veloppeur) ou de votre &eacute;cole, si c'est
+ le cas, qu'il (ou elle) signe une "renonciation aux droits
+ d'auteur" concernant le programme, si n&eacute;cessaire. Voici un
+ exemple (changez les noms) :
+ </p>
+
+
+<p>Yoyodyne, Inc., d&eacute;clare par la pr&eacute;sente renoncer &agrave; toute pr&eacute;tention
+ sur les droits d'auteur du programme "Gnomovision" (qui fait des
+ avances aux compilateurs) &eacute;crit par James Hacker.
+
+ [signature de Ty Coon], 1er avril 1989
+
+ Ty Coon, Pr&eacute;sident du Vice</p>
+
+
+<p>La pr&eacute;sente Licence Publique G&eacute;n&eacute;rale n'autorise pas l'incorporation de votre
+ programme dans des programmes
+ propri&eacute;taires. Si votre programme est une biblioth&egrave;que de
+ sous-programmes, vous pouvez consid&eacute;rer plus utile
+ d'autoriser l'&eacute;dition de liens d'applications propri&eacute;taires avec
+ la biblioth&egrave;que. Si c'est ce que vous voulez faire, utilisez la
+ GNU Lesser General Public License au lieu de la pr&eacute;sente Licence.
+ </p>
+</body>
+</html>
diff --git a/logo/src/xlogo/gpl/gpl-gl.html b/logo/src/xlogo/gpl/gpl-gl.html
new file mode 100644
index 0000000..e49d21b
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-gl.html
@@ -0,0 +1,140 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+ <TITLE></TITLE>
+ <META NAME="GENERATOR" CONTENT="OpenOffice.org 2.3 (Linux)">
+ <META NAME="CREATED" CONTENT="0;0">
+ <META NAME="CHANGED" CONTENT="0;0">
+ <STYLE TYPE="text/css">
+ <!--
+ @page { size: 21cm 29.7cm; margin: 2cm }
+ P { margin-bottom: 0.21cm }
+ -->
+ </STYLE>
+</HEAD>
+<BODY LANG="fr-FR" DIR="LTR">
+<PRE>This is an unofficial translation of the GNU General Public License into galician. It was not published by the Free Software Foundation, and does not legally state the distribution terms for software that uses the GNU GPL—only the original English text of the GNU GPL does that. However, we hope that this translation will help galician speakers understand the GNU GPL better.
+
+Esta &eacute; unha traduci&oacute;n non oficial da Licenza P&uacute;blica Xeral GNU ao galego. Esta licenza non foi publicada pola Free Software Foundation e non establece legalmente os termos de distribuci&oacute;n para software que utiliza a Free Software Foundation, s&oacute; a licenza orixinal en ingl&eacute;s GNU GPL os establece. No entanto, esperamos que esta traduci&oacute;n axude aos galegos a entender mellor a GNU GPL.
+
+LICENZA P&Uacute;BLICA XERAL DE GNU
+Versi&oacute;n 2 (xu&ntilde;o de 1991)
+
+Copyright &copy; 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Perm&iacute;tese a copia e distribuci&oacute;n de copias textuais desta licenza, sempre que non se introduza ningunha modificaci&oacute;n.
+
+Pre&aacute;mbulo
+
+A maior&iacute;a das licenzas de software est&aacute;n concibidas para privar o usuario da s&uacute;a liberdade de compartir e modificar ese software. Pola contra, a licenza p&uacute;blica xeral de GNU busca garantir a s&uacute;a liberdade para compartir e modificar software libre, para se asegurar de que o software &eacute; libre para todos os seus usuarios. Esta licenza p&uacute;blica xeral &eacute; de aplicaci&oacute;n &aacute; maior&iacute;a do software da Free Software Foundation, as&iacute; como a calquera outro programa cuxos autores se comprometan a utilizala (hai outro software da Free Software Foundation que se rexe, no seu lugar, pola licenza p&uacute;blica xeral reducida de GNU). Tam&eacute;n pode aplicala aos seus propios programas.
+
+Cando se fala de software libre f&aacute;lase de liberdade, non de prezo. As nosas licenzas p&uacute;blicas xerais est&aacute;n dese&ntilde;adas co obxectivo de asegurar a s&uacute;a liberdade para distribu&iacute;r copias de software libre (e cobrar por este servizo, se as&iacute; o desexa), que recibe o c&oacute;digo fonte ou pode recibilo se as&iacute; o desexa, que ten a posibilidade de modificar o software ou empregar partes del en novos programas libres e que, ademais, &eacute; consciente de que pode facer todo isto.
+
+Para protexer os seus dereitos, c&oacute;mpre introducir certas restrici&oacute;ns que impidan que calquera outro lle poida negar estes dereitos ou pedirlle que renuncie a eles. Estas restrici&oacute;ns implican determinadas responsabilidades para vostede, no caso de distribu&iacute;r copias do software ou modificalo.
+
+Por exemplo, se distrib&uacute;e copias dun programa libre, sexa gratis ou non, tenlle que ceder aos receptores todos os dereitos que ten vostede. Terase que asegurar de que eses receptores tam&eacute;n reciben ou poden recibir o c&oacute;digo fonte e ter&aacute; que mostrarlles os termos desta licenza para informalos dos seus dereitos.
+
+Dous son os pasos para protexer os seus dereitos: (1) p&oacute;r o software baixo copyright e (2) ofrecerlle esta licenza, que o autoriza legalmente para copiar, distribu&iacute;r e/ou modificar o software.
+
+Ademais, como medida de protecci&oacute;n para todos os autores e para n&oacute;s mesmos, &eacute; importante deixarlles claro a todas as partes que non existe garant&iacute;a para este software libre. No caso de que calquera usuario modifique o software e logo o distrib&uacute;a a un terceiro, os receptores deben saber que o que se lles entrega non &eacute; o software orixinal, de xeito que calquera problema introducido pola outra parte non afecte a reputaci&oacute;n dos autores orixinais.
+
+Por &uacute;ltimo, calquera programa libre est&aacute; constantemente ameazado polas patentes de software. A nosa intenci&oacute;n &eacute; evitar o perigo de que os redistribuidores dun programa libre obte&ntilde;an licenzas de patente pola s&uacute;a conta, convertendo as&iacute; o seu programa en software rexistrado. Para evitar que isto suceda, deixamos ben claro que calquera patente debe de garantir a cesi&oacute;n da licenza a calquera usuario posible, de maneira que todo o mundo poida usar o programa libremente ou, en caso contrario, non debe de garantir licenza ningunha a ningu&eacute;n.
+
+A seguir det&aacute;llanse os termos e condici&oacute;ns exactos para a copia, distribuci&oacute;n e modificaci&oacute;n.
+
+LICENZA P&Uacute;BLICA XERAL DE GNU
+TERMOS E CONDICI&Oacute;NS PARA A COPIA, DISTRIBUCI&Oacute;N E MODIFICACI&Oacute;N
+
+0. Esta licenza &eacute; aplicable a calquera programa ou produto doutro tipo no que figure un aviso inserido polo titular do copyright que especifique que se pode distribu&iacute;r baixo os termos desta licenza p&uacute;blica xeral. De aqu&iacute; en adiante, o termo &quot;Programa&quot; referirase a calquera programa ou produto deste tipo, mentres que o termo &quot;produto baseado no Programa&quot; referirase tanto ao programa coma a calquera produto derivado consonte a lei de propiedade intelectual, &eacute; dicir, calquera produto que conte&ntilde;a o programa ou unha parte del, sexa textual ou con modificaci&oacute;ns e/ou traducida a outra lingua. (De aqu&iacute; en adiante, a traduci&oacute;n incl&uacute;ese sen l&iacute;mite ning&uacute;n no termo &quot;modificaci&oacute;n&quot;.) As licenzas est&aacute;n redactadas na forma de cortes&iacute;a &quot;vostede&quot;.
+
+Calquera actividade distinta da copia, distribuci&oacute;n e modificaci&oacute;n non est&aacute; cuberta por esta licenza, sen&oacute;n que queda f&oacute;ra do seu &aacute;mbito de aplicaci&oacute;n. Non se restrinxe a acci&oacute;n de executar o Programa, e os resultados tirados do Programa s&oacute; est&aacute;n cubertos se o seu contido constit&uacute;e un produto baseado no Programa (sen importar se foi realizado mediante a execuci&oacute;n do Programa). Que isto sexa certo ou non depender&aacute; do que faga o Programa.
+
+1. Pode copiar e distribu&iacute;r copias textuais do c&oacute;digo fonte do Programa tal e como o recibe, en calquera medio, sempre que incl&uacute;a en cada copia, de forma axeitada e suficientemente visible, as indicaci&oacute;ns necesarias relativas &aacute; propiedade intelectual e &aacute; renuncia &aacute; concesi&oacute;n de garant&iacute;a, que mante&ntilde;a intactos todos os avisos referentes a esta licenza e &aacute; ausencia de toda garant&iacute;a e que lles entregue a todos os demais receptores do Programa unha copia desta licenza xunto co Programa.
+
+Pode cobrar unha taxa polo acto f&iacute;sico de transferir unha copia e, se vostede quixer, pode tam&eacute;n ofrecer unha determinada garant&iacute;a a cambio do pagamento dunha taxa.
+
+2. Pode modificar a s&uacute;a copia ou copias do Programa, ou calquera parte del, e obter as&iacute; un produto baseado no Programa, as&iacute; como copiar e distribu&iacute;r as devanditas modificaci&oacute;ns ou produto de acordo cos termos do apartado 1 anterior, sempre que cumpra tam&eacute;n con todas as condici&oacute;ns seguintes:
+
+ a) Debe facer que calquera ficheiro modificado incl&uacute;a indicaci&oacute;ns ben visibles nas que se especifique que o correspondente ficheiro foi modificado, as&iacute; como a data de calquera modificaci&oacute;n.
+ b) Debe facer que calquera produto distribu&iacute;do ou publicado por vostede, que en todo ou en parte conte&ntilde;a ou derive do Programa ou de calquera parte do Programa, obte&ntilde;a unha licenza no seu conxunto sen que isto repercuta en ning&uacute;n gasto para calquera terceira parte de acordo cos termos desta licenza.
+ c) Se o programa modificado adoita ler comandos de forma interactiva ao executalo, debe facer que, cando se execute para tal uso interactivo na forma m&aacute;is habitual, mostre ou exhiba un aviso no que se incl&uacute;an as oportunas informaci&oacute;ns relativas &aacute; propiedade intelectual do programa e &aacute; ausencia de toda garant&iacute;a (ou, se for o caso, ao feito de que &eacute; vostede quen proporciona a garant&iacute;a), a indicaci&oacute;n de que os usuarios poden redistribu&iacute;r o programa consonte estas condici&oacute;ns e a informaci&oacute;n necesaria para que o usuario saiba como acceder a unha copia desta licenza. (Excepci&oacute;n: se o Programa en si mesmo &eacute; interactivo pero non adoita mostrar avisos deste tipo, non &eacute; necesario que o seu produto baseado no Programa exhiba ning&uacute;n aviso.)
+
+Estes requisitos apl&iacute;canse ao produto modificado no seu conxunto. Se o produto cont&eacute;n secci&oacute;ns facilmente identificables non derivadas do Programa, que razoablemente se poidan considerar como produtos separados e independentes por si mesmos, esta licenza e mais os seus termos non ser&aacute;n aplicables &aacute;s devanditas secci&oacute;ns cando se distrib&uacute;an como produtos separados. Pero se esas mesmas secci&oacute;ns se distrib&uacute;en como parte dun todo consistente nun produto baseado no Programa, a distribuci&oacute;n do todo deber&aacute; de se acoller aos termos desta licenza, cuxas disposici&oacute;ns, aplicables a todos os demais titulares da licenza, abranguen o conxunto completo e, en consecuencia, todas e cada unha das partes do conxunto independentemente de quen as escribise.
+
+As&iacute;, o obxectivo deste apartado non &eacute; reclamar dereitos ou rebater os seus dereitos sobre calquera produto escrito por vostede na s&uacute;a totalidade; m&aacute;is ben ao contrario, o seu obxectivo &eacute; exercer o dereito de controlar a distribuci&oacute;n de produtos derivados ou colectivos baseados no Programa.
+Ademais, a simple agregaci&oacute;n ao Programa (ou a un produto baseado no Programa) doutro produto non baseado no Programa nun volume dun medio de distribuci&oacute;n ou almacenamento non implica a inclusi&oacute;n do outro produto no &aacute;mbito de aplicaci&oacute;n desta licenza.
+
+3. Pode copiar e distribu&iacute;r o Programa (ou un produto baseado nel, segundo os termos do apartado 2) en c&oacute;digo obxecto ou forma executable de acordo co establecido nos apartados 1 e 2 anteriores, sempre que cumpra tam&eacute;n con un dos seguintes requisitos:
+
+ a) Acompa&ntilde;alo do correspondente c&oacute;digo fonte completo nun formato lexible por m&aacute;quina, que se ha de distribu&iacute;r de acordo co estipulado nos apartados 1 e 2 anteriores nun soporte tipicamente utilizado para o intercambio de software.
+ b) Acompa&ntilde;alo dunha oferta por escrito, cunha validez m&iacute;nima de tres anos, na que se entregue a calquera terceiro interesado, por un custo nunca superior aos gastos nos que vostede poida incorrer pola execuci&oacute;n f&iacute;sica da distribuci&oacute;n do c&oacute;digo fonte, unha copia completa en formato lexible por m&aacute;quina do c&oacute;digo fonte correspondente, que se ha de distribu&iacute;r de acordo co estipulado nos apartados 1 e 2 anteriores nun soporte tipicamente utilizado para o intercambio de software.
+ c) Acompa&ntilde;alo da informaci&oacute;n que recibiu vostede verbo da oferta de distribu&iacute;r o c&oacute;digo fonte correspondente. (Esta opci&oacute;n s&oacute; est&aacute; permitida para a distribuci&oacute;n non-comercial, e unicamente no caso de que vostede recibise o programa en c&oacute;digo obxecto ou forma executable cunha oferta como a que se acaba de explicar, de acordo co subapartado b anterior.)
+
+Ent&eacute;ndese por c&oacute;digo fonte dun traballo o seu formato m&aacute;is id&oacute;neo &aacute; hora de realizar modificaci&oacute;ns nel. No caso dun traballo executable, ent&eacute;ndese por c&oacute;digo fonte completo todo o c&oacute;digo fonte para todos os m&oacute;dulos que incl&uacute;e, ademais de todos os ficheiros de definici&oacute;n de interfaces asociados e os scripts utilizados para controlar a compilaci&oacute;n e a instalaci&oacute;n do executable. Por&eacute;n, como caso excepcional, o c&oacute;digo fonte distribu&iacute;do non precisa inclu&iacute;r nada que se distrib&uacute;a normalmente (xa sexa de forma binaria ou fonte) cos compo&ntilde;entes principais (compilador, n&uacute;cleo etc.) do sistema operativo en que se executa o executable, ag&aacute;s que ese propio compo&ntilde;ente acompa&ntilde;e o executable.
+
+Se a distribuci&oacute;n do c&oacute;digo obxecto ou executable se realiza concedendo acceso de copia desde un lugar determinado, ent&oacute;n a concesi&oacute;n de acceso equivalente para copiar o c&oacute;digo fonte do mesmo lugar consid&eacute;rase como distribuci&oacute;n do c&oacute;digo fonte, a&iacute;nda que ning&uacute;n terceiro estea obrigado a copiar o c&oacute;digo fonte xunto co obxecto.
+
+4. Non lle est&aacute; permitido copiar, modificar, emitir unha sublicenza nin distribu&iacute;r o Programa ag&aacute;s nos termos especificamente estipulados nesta licenza. Calquera outro intento de copiar, modificar, emitir unha sublicenza ou distribu&iacute;r o Programa ser&aacute; considerado nulo, e implicar&aacute; a cancelaci&oacute;n autom&aacute;tica dos dereitos que lle concede esta licenza. No entanto, as partes &aacute;s que vostede concedera copias ou dereitos consonte os termos desta licenza non ver&aacute;n resoltas as s&uacute;as respectivas licenzas, sempre que cumpran plenamente con todo o que nelas se estipula.
+
+5. Dado que a&iacute;nda non a asinou, non est&aacute; obrigado a aceptar os termos desta licenza. Por&eacute;n, ela &eacute; o &uacute;nico que o autoriza a modificar ou distribu&iacute;r o Programa ou calquera outro produto derivado del. Estas acci&oacute;ns est&aacute;n prohibidas por lei mentres non acepte esta licenza. Xa que logo, mediante a modificaci&oacute;n ou distribuci&oacute;n do Programa (ou de calquera produto baseado no Programa) vostede expresa a s&uacute;a aceptaci&oacute;n desta licenza para a realizaci&oacute;n das devanditas acci&oacute;ns, as&iacute; como de todos os termos e condici&oacute;ns estipulados nela para a copia, distribuci&oacute;n ou modificaci&oacute;n do Programa ou de calquera produto baseado nel.
+
+6. Cada vez que redistrib&uacute;a o Programa (ou calquera produto baseado no Programa), o receptor recibir&aacute; automaticamente unha licenza por parte do emisor da licenza orixinal que lle permitir&aacute; copiar, distribu&iacute;r ou modificar o Programa consonte estes termos e condici&oacute;ns. Vostede non poder&aacute; impor ningunha outra restrici&oacute;n sobre o exercicio dos dereitos nela estipulados por parte do receptor. Vostede non &eacute; responsable &aacute; hora de esixirlle a un terceiro o cumprimento dos termos estipulados nesta licenza.
+
+7. Se, como consecuencia dun proceso xudicial ou dunha acusaci&oacute;n por violaci&oacute;n de patentes, ou por calquera outra causa (sen restrinxirse aos temas de patentes), resultase a imposici&oacute;n de calquera condici&oacute;n sobre vostede (xa sexa por orde xudicial, por acordo ou por calquera outra causa) que contradiga as condici&oacute;ns desta licenza, iso non o escusa do cumprimento das condici&oacute;ns desta licenza. De non lle ser posible distribu&iacute;r o Programa respectando as obrigas contra&iacute;das baixo esta licenza e, asemade, calquera outra obriga pertinente, ent&oacute;n ter&aacute; que deixar de distribu&iacute;lo en ning&uacute;n modo. Por exemplo, no caso de existir unha licenza de patente que non permitise a redistribuci&oacute;n do Programa exenta de dereitos de autor por parte de todos aqueles que recibisen copias de maneira directa ou indirecta de vostede, o &uacute;nico xeito de cumprir cos termos da devandita licenza de patente e mais desta licenza ao mesmo tempo ser&iacute;a evitando por completo a distribuci&oacute;n do Programa.
+
+No caso de que calquera secci&oacute;n deste apartado se considerase non v&aacute;lida ou non executable baixo calquera circunstancia concreta ser&aacute; de aplicaci&oacute;n o resto do apartado e, en calquera outras circunstancias, aplicarase o apartado no seu conxunto.
+
+O obxectivo deste apartado non &eacute; inducilo a infrinxir ningunha reivindicaci&oacute;n de patente nin de calquera outro dereito de propiedade intelectual, as&iacute; como tampouco impugnar a validez deste tipo de reivindicaci&oacute;ns. O &uacute;nico que pretende este apartado &eacute; protexer a integridade do sistema de distribuci&oacute;n do software libre, levado &aacute; pr&aacute;ctica mediante o desenvolvemento de licenzas p&uacute;blicas.
+
+Moitas persoas contribu&iacute;ron xenerosamente ao desenvolvemento do amplo abano de software distribu&iacute;do a trav&eacute;s deste sistema, coa confianza de que se aplicar&aacute; consistentemente. S&oacute; o autor/doador pode decidir se est&aacute; disposto a distribu&iacute;r o seu software mediante calquera outro sistema; o titular dunha licenza non pode impor este tipo de elecci&oacute;n.
+
+Este apartado pretende deixar ben claro o que considera que &eacute; consecuencia do resto desta licenza.
+
+8. No caso de que nalg&uacute;ns pa&iacute;ses se restrinxa a distribuci&oacute;n e/ou o uso do Programa pola execuci&oacute;n dalgunha patente ou interface protexida por copyright, o titular orixinal do copyright que somete o Programa aos termos desta licenza poder&aacute; engadir unha limitaci&oacute;n expl&iacute;cita &aacute; distribuci&oacute;n xeogr&aacute;fica que excl&uacute;a aqueles pa&iacute;ses, de xeito que s&oacute; se permita a distribuci&oacute;n nos ou entre os pa&iacute;ses non suxeitos &aacute; devandita exclusi&oacute;n. Se este for o caso, a licenza incorporar&iacute;a a limitaci&oacute;n citada nos mesmos termos que se fose redactada como parte do corpo desta licenza.
+
+9. A Free Software Foundation poder&aacute; publicar versi&oacute;ns revisadas e/ou novas desta licenza p&uacute;blica xeral no momento en que o estime oportuno. As novas versi&oacute;ns manter&aacute;n o mesmo esp&iacute;rito ca a actual, se ben poden diferir nalg&uacute;ns detalles do contido co obxectivo de facer fronte a novos problemas ou preocupaci&oacute;ns.
+
+Cada versi&oacute;n leva un n&uacute;mero identificativo diferente. Se o Programa especifica que lle &eacute; aplicable unha versi&oacute;n espec&iacute;fica desta licenza e &quot;calquera versi&oacute;n posterior&quot;, vostede poder&aacute; escoller entre cinguirse aos termos e condici&oacute;ns ben da primeira versi&oacute;n ou ben de calquera versi&oacute;n posterior publicada pola Free Software Foundation. Se o Programa non especifica ning&uacute;n n&uacute;mero de versi&oacute;n desta licenza, vostede ter&aacute; a opci&oacute;n de escoller calquera versi&oacute;n de entre todas as publicadas pola Free Software Foundation.
+
+10. Se desexa inclu&iacute;r partes do Programa noutros programas libres cunhas condici&oacute;ns de distribuci&oacute;n distintas, deber&aacute; de se dirixir ao autor por escrito para lle pedir a s&uacute;a autorizaci&oacute;n. No caso do software co copyright da Free Software Foundation, dir&iacute;xase por escrito &aacute; Free Software Foundation, que en ocasi&oacute;ns fai excepci&oacute;ns nestes casos. A decisi&oacute;n que tomemos basearase nos nosos dous obxectivos de preservar o estatus libre de calquera produto derivado do noso software libre e de promover o compartimento e a reutilizaci&oacute;n do software en xeral.
+
+AUSENCIA DE GARANT&Iacute;A
+
+11. DADO QUE SE TRATA DUN PROGRAMA ACOMPA&Ntilde;ADO DUNHA LICENZA GRATU&Iacute;TA, DENTRO DOS L&Iacute;MITES PERMITIDOS POLA LEXISLACI&Oacute;N APLICABLE CONSID&Eacute;RASE UN PROGRAMA EXENTO DE TODA GARANT&Iacute;A. AG&Aacute;S QUE SE ESPECIFIQUE O CONTRARIO POR ESCRITO, OS TITULARES DO COPYRIGHT E/OU OUTRAS PARTES CEDEN O PROGRAMA &quot;TAL CAL&quot; SEN NING&Uacute;N TIPO DE GARANT&Iacute;A, NIN EXPL&Iacute;CITA NIN IMPL&Iacute;CITA, INCLU&Iacute;DAS, A&Iacute;NDA QUE SEN EXCLUSIVIDADE, AS GARANT&Iacute;AS IMPL&Iacute;CITAS DE COMERCIABILIDADE E IDONEIDADE PARA UN DETERMINADO FIN. A TOTALIDADE DOS RISCOS ASOCIADOS &Aacute; CALIDADE E AO RENDEMENTO DO PROGRAMA RECAE SOBRE VOSTEDE. SE O PROGRAMA RESULTASE DEFECTUOSO, VOSTEDE TER&Aacute; QUE ASUMIR OS CUSTOS DE CALQUERA REPARACI&Oacute;N, ARRANXO OU EMENDA.
+
+12. EN NING&Uacute;N CASO, AG&Aacute;S QUE AS&Iacute; O ESIXA A LEXISLACI&Oacute;N APLICABLE OU QUE EXISTA UN ACORDO POR ESCRITO ENTRE AS PARTES, SE LLE ESIXIR&Aacute; RESPONSABILIDADE AO TITULAR DO COPYRIGHT, OU A CALQUERA OUTRA PARTE AUTORIZADA PARA MODIFICAR E/OU REDISTRIBU&Iacute;R O PROGRAMA CONSONTE OS TERMOS DESCRITOS ANTERIORMENTE, POLOS DANOS E PERDAS OCASIONADOS, INCLU&Iacute;DOS OS DANOS XERAIS, ESPECIAIS, INCIDENTAIS OU DE IMPORTANCIA DE CALQUERA TIPO QUE POIDAN DERIVAR DO USO OU DA INCAPACIDADE DE USO DO PROGRAMA (O QUE INCL&Uacute;E, A&Iacute;NDA QUE SEN RESTRICI&Oacute;N, BEN A PERDA DE DATOS, OU BEN A INTERPRETACI&Oacute;N IMPRECISA DE DATOS, OU BEN AS PERDAS OCASIONADAS A VOSTEDE OU A UN TERCEIRO, OU BEN A IMPOSIBILIDADE DE EXECUTAR O PROGRAMA CON CALQUERA OUTRO PROGRAMA), MESMO SE O DEVANDITO TITULAR OU A OUTRA PARTE FOSEN ADVERTIDOS DA POSIBILIDADE DE QUE SE PRODUCISEN OS DANOS DESCRITOS ANTERIORMENTE.
+
+
+FIN DOS TERMOS E CONDICI&Oacute;NS
+
+
+Como aplicar os termos desta licenza aos seus novos programas
+
+Se vostede desenvolve un programa novo e quere que sexa da maior utilidade posible para o p&uacute;blico, o mellor xeito de facelo &eacute; convertelo en software libre, de xeito que todo o mundo poida redistribu&iacute;lo e modificalo segundo os termos desta licenza.
+
+Para iso, ten que acompa&ntilde;ar o programa dos avisos que se especifican a seguir. O mellor &eacute; inserilos ao comezo de cada ficheiro fonte para transmitir do xeito m&aacute;is eficaz e seguro posible a informaci&oacute;n sobre a exclusi&oacute;n de garant&iacute;a. Ademais, cada ficheiro deber&iacute;a ter polo menos a li&ntilde;a do &quot;copyright&quot; e unha referencia ou indicaci&oacute;n que remita ao lugar onde se pode ler o aviso completo.
+
+&lt;unha li&ntilde;a na que se indiquen o nome do programa e unha breve descrici&oacute;n do que fai.&gt;
+Copyright &copy; &lt;ano&gt; &lt;nome do autor&gt;
+
+Este &eacute; un programa de software libre e, xa que logo, vostede pode redistribu&iacute;lo e/ou modificalo consonte os termos establecidos na licenza p&uacute;blica xeral de GNU segundo a s&uacute;a publicaci&oacute;n pola Free Software Foundation. Pode escoller a versi&oacute;n 2 da licenza ou, se prefire, calquera outra versi&oacute;n posterior.
+
+Este programa distrib&uacute;ese coa esperanza de que resulte &uacute;til, se ben NON TEN NINGUNHA GARANT&Iacute;A, nin sequera a garant&iacute;a impl&iacute;cita de COMERCIABILIDADE ou IDONEIDADE PARA UN DETERMINADO FIN. Para obter informaci&oacute;n m&aacute;is detallada pode consultar a licenza p&uacute;blica xeral de GNU.
+
+D&aacute;se por sentado que recibiu unha copia da licenza p&uacute;blica xeral de GNU xunto con este programa. Se non for as&iacute;, pode solicitarlla por escrito &aacute; Free Software Foundation, Inc. no enderezo seguinte: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+No seu escrito debe inclu&iacute;r o seu correo postal e electr&oacute;nico.
+
+De se tratar dun programa interactivo, ter&aacute; que asegurarse de que ao arrancar en modo interactivo amose un breve aviso do estilo do seguinte:
+
+Gnomovision versi&oacute;n 69, Copyright &copy; ano nome do autor Gnomovision non ofrece GARANT&Iacute;A DE NING&Uacute;N TIPO. Para m&aacute;is informaci&oacute;n introduza 'amosar g'. Este &eacute; un programa de software libre, de xeito que pode redistribu&iacute;lo tranquilamente sempre que cumpra determinadas condici&oacute;ns. Para m&aacute;is informaci&oacute;n introduza 'amosar c'.
+
+Os comandos hipot&eacute;ticos 'amosar g' e 'amosar c' deber&iacute;an levalo aos respectivos par&aacute;grafos correspondentes da licenza p&uacute;blica xeral. Por suposto, os comandos utilizados por vostede poden ser distintos a 'amosar g' e 'amosar c'; mesmo poden ser ligaz&oacute;ns nas que premer ou elementos do men&uacute;, segundo o que resulte m&aacute;is axeitado para o seu programa.
+En caso necesario, tam&eacute;n deber&aacute; pedirlle &aacute; s&uacute;a empresa (se traballa como programador) ou ao seu centro de ensino, se for o caso, que asine unha carta de renuncia a todos os dereitos de propiedade intelectual que puideran estar asociados ao programa. Velaqu&iacute; un exemplo (s&oacute; c&oacute;mpre modificar os nomes):
+
+Yoyodyne, Inc. renuncia a calquera dereito de propiedade intelectual sobre o programa 'Gnomovision' (que realiza pasadas nos compiladores), desenvolvido por James Hacker.
+&lt;sinatura de Xan Perill&aacute;n&gt;, 1 de abril de 1989
+Xan Perill&aacute;n, Presidente de XXX
+
+Consonte os termos desta licenza p&uacute;blica xeral non lle est&aacute; permitido inclu&iacute;r o seu programa noutros programas con dereitos rexistrados. No caso de que o seu programa sexa unha biblioteca de subrutinas, poida que lle resulte m&aacute;is &uacute;til permitir que se establezan ligaz&oacute;ns entre aplicaci&oacute;ns rexistradas e a biblioteca. Se &eacute; isto o que lle interesa facer, utilice a licenza p&uacute;blica xeral reducida de GNU no canto desta.</PRE>
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/logo/src/xlogo/gpl/gpl-hu.html b/logo/src/xlogo/gpl/gpl-hu.html
new file mode 100644
index 0000000..5f3e023
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-hu.html
@@ -0,0 +1,428 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+ <TITLE></TITLE>
+ <META NAME="GENERATOR" CONTENT="OpenOffice.org 2.4 (Linux)">
+ <META NAME="AUTHOR" CONTENT="Loïc">
+ <META NAME="CREATED" CONTENT="20090422;15283600">
+ <META NAME="CHANGEDBY" CONTENT="Loïc">
+ <META NAME="CHANGED" CONTENT="20090422;15292500">
+ <STYLE TYPE="text/css">
+ <!--
+ @page { size: 21cm 29.7cm; margin: 2cm }
+ P { margin-bottom: 0.21cm }
+ H1 { margin-bottom: 0.21cm }
+ H1.western { font-family: "Times New Roman", serif }
+ H1.cjk { font-family: "DejaVu Sans" }
+ H1.ctl { font-family: "DejaVu Sans" }
+ H2 { margin-bottom: 0.21cm }
+ -->
+ </STYLE>
+</HEAD>
+<BODY LANG="fr-FR" DIR="LTR">
+<P><STRONG>This is an unofficial translation of the GNU General
+Public License into Hungarian. It was not published by the Free
+Software Foundation, and does not legally state the distribution
+terms for software that uses the GNU GPL—only the original English
+text of the GNU GPL does that. However, we hope that this translation
+will help Hungarian speakers understand the GNU GPL better.</STRONG></P>
+<DIV ID="content" DIR="LTR">
+ <P><STRONG>Ez a GNU General Public License nem hivatalos magyar
+ fordítása. A fordítást nem a Free Software Foundation adta ki,
+ és jogi értelemben nem határozza meg a GNU GPL-t felhasználó
+ szoftverek terjesztési feltételeit – e tekintetben csak a GNU
+ GPL angol nyelvű verziója irányadó. Mindazonáltal reméljük,
+ hogy ez a fordítás segít a GNU GPL jobb megértésében.</STRONG></P>
+ <H1 CLASS="western">GNU General Public License (GPL)
+ </H1>
+ <P>Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple
+ Place, Suite 330, Boston, MA 02111-1307, USA
+ </P>
+ <P>Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass
+ Ave, Cambridge, MA 02139, USA
+ </P>
+ <H2>Előszó
+ </H2>
+ <P>A legtöbb szoftver licencei azzal a szándékkal készültek,
+ hogy minél kevesebb lehetőséget adjanak a szoftver
+ megváltoztatására és terjesztésére. Ezzel szemben a GNU GPL
+ célja, hogy garantálja a szabad szoftver másolásának és
+ terjesztésének szabadságát, ezáltal biztosítva a szoftver
+ szabad felhasználhatóságát minden felhasználó számára. A GPL
+ szabályai vonatkoznak a Free Software Foundation legtöbb
+ szoftverére, illetve minden olyan programra, melynek szerzője úgy
+ dönt, hogy ezt használja a szerzői jog megjelölésekor. (A Free
+ Software Foundation egyes szoftvereire a GNU LGPL érvényes.) Bárki
+ használhatja a programjaiban a GPL-t a szerzői jogi megjegyzésnél.
+ </P>
+ <P>A szabad szoftver megjelölés nem jelenti azt, hogy a
+ szoftvernek nem lehet ára. A GPL licencek célja, hogy garantálja
+ a szabad szoftver másolatainak szabad terjesztését (és e
+ szolgáltatásért akár díj felszámítását), a forráskód
+ elérhetőségét, hogy bárki szabadon módosíthassa a szoftvert,
+ vagy felhasználhassa a részeit új szabad programokban; és hogy
+ mások megismerhessék ezt a lehetőséget.
+ </P>
+ <P>A szerző jogainak védelmében korlátozásokat kell hozni,
+ amelyek megtiltják, hogy bárki megtagadhassa ezeket a jogokat
+ másoktól, vagy ezekről való lemondásra kényszerítsen bárki
+ mást. Ezek a megszorítások bizonyos felelősségeket jelentenek
+ azok számára, akik a szoftver másolatait terjesztik vagy
+ módosítják.
+ </P>
+ <P>Ha valaki például ilyen program másolatait terjeszti, akár
+ ingyen vagy bizonyos összeg fejében, a szoftverre vonatkozó
+ minden jogot tovább kell adnia a fogadó feleknek. Biztosítani
+ kell továbbá, hogy megkapják vagy legalábbis megkaphassák a
+ forráskódot is. És persze ezeket a licencfeltételeket is el kell
+ juttatni, hogy tisztában legyenek a jogaikkal.
+ </P>
+ <P>A jogok védelme két lépésből áll:
+ </P>
+ <P STYLE="margin-left: 1.06cm">(1) a szoftver szerzői jogainak
+ védelméből és
+ </P>
+ <P STYLE="margin-left: 1.06cm">(2) a jelen licenc biztosításából,
+ amely jogalapot biztosít a szoftver másolására, terjesztésére
+ és/vagy módosítására.
+ </P>
+ <P>Az egyes szerzők és a magunk védelmében biztosítani akarjuk,
+ hogy mindenki megértse: a jelen szabad szoftverre nincs jótállás.
+ Ha a szoftvert módosították és továbbadták, akkor mindenkinek,
+ aki a módosított változatot kapja, tudnia kell, hogy az nem az
+ eredeti, így a mások által okozott hibáknak nem lehet hatása az
+ eredeti szerző hírnevére.
+ </P>
+ <P>Végül, a szabad szoftver létét állandóan fenyegetik a
+ szoftverszabadalmak. El szeretnénk kerülni annak veszélyét, hogy
+ a szabad program terjesztői szabadalmat jegyezhessenek be rá,
+ ezáltal saját szellemi tulajdont képezővé tegyék a programot.
+ Ennek megelőzéséhez tisztázni kívánjuk: szabadalom szabad
+ szoftverrel kapcsolatban csak mindenki általi szabad használatra
+ jegyezhető be, vagy egyáltalán nem jegyezhető be.
+ </P>
+ <P>A másolásra, terjesztésre, módosításra vonatkozó pontos
+ szabályok és feltételek:
+ </P>
+ <H2>A MÁSOLÁSRA, TERJESZTÉSRE ÉS MÓDOSÍTÁSRA VONATKOZÓ
+ FELTÉTELEK ÉS KIKÖTÉSEK
+ </H2>
+ <P><B>0.</B> Ez a licenc minden olyan programra vagy munkára
+ vonatkozik, amelynek a szerzői jogi megjegyzésében a jog
+ tulajdonosa a következő szöveget helyezte el: a GPL-ben foglaltak
+ alapján terjeszthető. Az alábbiakban a Program kifejezés bármely
+ ilyen programra vagy munkára vonatkozik, a Programon alapuló munka
+ pedig magát a programot vagy egy szerzői joggal védett munkát
+ jelenti: vagyis olyan munkát, amely tartalmazza a programot vagy
+ annak egy részletét, módosítottan vagy módosítatlanul és/vagy
+ más nyelvre fordítva. (Az alábbiakban a fordítás minden egyéb
+ megkötés nélkül beletartozik a módosítás fogalmába.) Minden
+ engedélyezés címzettje Ön.
+ </P>
+ <P>A jelen licenc a másoláson, terjesztésen és módosításon
+ kívül más tevékenységre nem vonatkozik, azok a hatályán kívül
+ esnek. A Program futtatása nincs korlátozva, illetve a Program
+ kimenetére is csak abban az esetben vonatkozik ez a szabályozás,
+ ha az tartalmazza a Programon alapuló munka egy részletét
+ (függetlenül attól, hogy ez a Program futtatásával jött-e
+ létre). Ez tehát a Program működésétől függ.
+ </P>
+ <P><B>1.</B> A Program forráskódja módosítás nélkül másolható
+ és bármely adathordozón terjeszthető, feltéve, hogy minden
+ egyes példányon pontosan szerepel a megfelelő szerzői jogi
+ megjegyzés, illetve a garanciavállalás elutasítása;
+ érintetlenül kell hagyni minden erre a szabályozásra és a
+ garancia teljes hiányára utaló szöveget és a jelen
+ licencdokumentumot is el kell juttatni mindazokhoz, akik a Programot
+ kapják.
+ </P>
+ <P>Felszámítható díj a másolat fizikai továbbítása fejében,
+ illetve ellenszolgáltatás fejében a Programhoz garanciális
+ támogatás is biztosítható.
+ </P>
+ <P><B>2.</B> A Program vagy annak egy része módosítható, így a
+ Programon alapuló munka jön létre. A módosítás ezután az 1.
+ szakaszban adott feltételek szerint tovább terjeszthető, ha az
+ alábbi feltételek is teljesülnek:
+ </P>
+ <DL>
+ <DD STYLE="margin-bottom: 0.5cm">a) A módosított fájlokat el
+ kell látni olyan megjegyzéssel, amely feltünteti a módosítást
+ végző nevét és a módosítások dátumát.
+ </DD><DD STYLE="margin-bottom: 0.5cm">
+ b) Minden olyan munkát, amely részben vagy egészben tartalmazza
+ a Programot vagy a Programon alapul, olyan szabályokkal kell
+ kiadni vagy terjeszteni, hogy annak használati joga harmadik
+ személy részére licencdíjmentesen hozzáférhető legyen, a
+ jelen dokumentumban található feltételeknek megfelelően.
+ </DD><DD STYLE="margin-bottom: 0.5cm">
+ c) Ha a módosított Program interaktívan olvassa a parancsokat
+ futás közben, akkor úgy kell elkészíteni, hogy a megszokott
+ módon történő indításkor megjelenítsen egy üzenetet a
+ megfelelő szerzői jogi megjegyzéssel és a garancia hiányára
+ utaló közléssel (vagy éppen azzal az információval, hogy
+ miként juthat valaki garanciához), illetve azzal az
+ információval, hogy bárki terjesztheti a Programot a jelen
+ feltételeknek megfelelően, és arra is utalást kell tenni, hogy
+ a felhasználó miként tekintheti meg a licenc egy példányát.
+ (Kivétel: ha a Program interaktív ugyan, de nem jelenít meg
+ hasonló üzenetet, akkor a Programon alapuló munkának sem kell
+ ezt tennie.)
+ </DD></DL>
+ <P>
+ Ezek a feltételek a módosított munkára, mint egészre
+ vonatkoznak. Ha a munka azonosítható részei nem a Programon
+ alapulnak és független munkákként elkülönülten azonosíthatók,
+ akkor ez a szabályozás nem vonatkozik ezekre a részekre, ha azok
+ külön munkaként kerülnek terjesztésre. Viszont, ha ugyanez a
+ rész az egész részeként kerül terjesztésre, amely a Programon
+ alapuló munka, akkor az egész terjesztése csak a jelen dokumentum
+ alapján lehetséges, amely ebben az esetben a jogokat minden egyes
+ felhasználó számára kiterjeszti az egészre tekintet nélkül
+ arra, hogy melyik részt ki írta.
+ </P>
+ <P>E szövegrésznek tehát nem az a célja, hogy mások jogait
+ elvegye vagy korlátozza a kizárólag saját maga által írt
+ munkákra; a cél az, hogy a jogok gyakorlása szabályozva legyen a
+ Programon alapuló illetve a gyűjteményes munkák terjesztése
+ esetében.
+ </P>
+ <P>Ezenkívül más munkáknak, amelyek nem a Programon alapulnak, a
+ Programmal (vagy a Programon alapuló munkával) közös
+ adathordozón vagy adattárolón szerepeltetése nem jelenti a jelen
+ szabályok érvényességét azokra is.
+ </P>
+ <P><B>3.</B> A Program (vagy a Programon alapuló munka a 2.
+ szakasznak megfelelően) másolható és terjeszthető tárgykódú
+ vagy végrehajtható kódú formájában az 1. és 2. szakaszban
+ foglaltak szerint, amennyiben az alábbi feltételek is teljesülnek:
+ </P>
+ <DL>
+ <DD STYLE="margin-bottom: 0.5cm">a) a teljes, gép által
+ értelmezhető forráskód kíséri az anyagot, amelynek
+ terjesztése az 1. és 2. szakaszban foglaltak szerint történik,
+ jellemzően szoftverterjesztésre használt adathordozón; vagy,
+ </DD><DD STYLE="margin-bottom: 0.5cm">
+ b) legalább három évre szólóan írásban vállalja, hogy
+ bármely külső személynek rendelkezésre áll a teljes gép
+ által értelmezhető forráskód, a fizikai továbbítást fedező
+ összegnél nem nagyobb díjért az 1. és 2. szakaszban foglaltak
+ szerint szoftverterjesztésre használt adathordozón; vagy,
+ </DD><DD STYLE="margin-bottom: 0.5cm">
+ c) a megfelelő forráskód terjesztésére vonatkozóan megkapott
+ tájékoztatás kíséri az anyagot. (Ez az alternatíva csak nem
+ kereskedelmi terjesztés esetén alkalmazható abban az esetben, ha
+ a terjesztő a Programhoz a tárgykódú vagy forráskódú
+ formájában jutott hozzá az ajánlattal együtt a fenti b.
+ cikkelynek megfelelően.)
+ </DD></DL>
+ <P>
+ Egy munka forráskódja a munkának azt a formáját jelenti,
+ amelyben a módosításokat elsődlegesen végezni szokás. Egy
+ végrehajtható program esetében a teljes forráskód a
+ tartalmazott összes modul forráskódját jelenti, továbbá a
+ kapcsolódó felületdefiníciós fájlokat és a fordítást
+ vezérlő parancsfájlokat. Egy speciális kivételként a
+ forráskódnak nem kell tartalmaznia normál esetben a végrehajtható
+ kód futtatására szolgáló operációs rendszer főbb részeiként
+ (kernel, fordítóprogram stb.) terjesztett részeit (forrás vagy
+ bináris formában), kivéve, ha a komponens maga a végrehajtható
+ állományt kíséri.
+ </P>
+ <P>Ha a végrehajtható program vagy tárgykód terjesztése a
+ forráskód hozzáférését egy megadott helyen biztosító írásban
+ vállalja, akkor ez egyenértékű a forráskód terjesztésével,
+ bár másoknak nem kell a forrást lemásolniuk a tárgykóddal
+ együtt.
+ </P>
+ <P><B>4.</B> A Programot csak a jelen Licencben leírtaknak
+ megfelelően szabad lemásolni, terjeszteni, módosítani és
+ allicencbe adni. Az egyéb módon történő másolás, módosítás,
+ terjesztés és allicencbe adás érvénytelen, és azonnal
+ érvényteleníti a dokumentumban megadott jogosultságokat.
+ Mindazonáltal azok, akik a Licencet megszegőtől kaptak
+ példányokat vagy jogokat, tovább gyakorolhatják a Licenc által
+ meghatározott jogaikat mindaddig, amíg teljesen megfelelnek a
+ Licenc feltételeinek.
+ </P>
+ <P><B>5.</B> Önnek nem kötelező elfogadnia ezt a szabályozást,
+ hiszen nem írta alá. Ezen kívül viszont semmi más nem ad
+ jogokat a Program terjesztésére és módosítására. Ezeket a
+ cselekedeteket a törvény bünteti, ha nem a jelen szerzői jogi
+ szabályozás keretei között történnek. Mindezek miatt a Program
+ (vagy a Programon alapuló munka) terjesztése vagy módosítása a
+ jelen dokumentum szabályainak, és azon belül a Program vagy a
+ munka módosítására, másolására vagy terjesztésére vonatkozó
+ összes feltételének elfogadását jelenti.
+ </P>
+ <P><B>6.</B> Minden alkalommal, amikor a Program (vagy az azon
+ alapuló munka) továbbadása történik, a Programot megkapó
+ személy automatikusan hozzájut az eredeti licenctulajdonostól
+ származó licenchez, amely a jelen szabályok szerint biztosítja a
+ jogot a Program másolására, terjesztésére és módosítására.
+ Nem lehet semmilyen módon tovább korlátozni a fogadó félnek az
+ itt megadott jogait. A Program továbbadója nem felelős harmadik
+ személyekkel betartatni a jelen szabályokat.
+ </P>
+ <P><B>7.</B> Ha bírósági határozat, szabadalomsértés vélelme,
+ vagy egyéb (nem kizárólag szabadalmakkal kapcsolatos) okból
+ olyan feltételeknek kell megfelelnie (akár bírósági határozat,
+ akár megállapodás, akár bármi más eredményeképp), amelyek
+ ellentétesek a jelen feltételekkel, az nem menti fel a terjesztőt
+ a jelen feltételek figyelembevétele alól. Ha a terjesztés nem
+ lehetséges a jelen Licenc és az egyéb feltételek
+ kötelezettségeinek együttes betartásával, akkor tilos a Program
+ terjesztése. Ha például egy szabadalmi szerződés nem engedi meg
+ egy program jogdíj nélküli továbbterjesztését azok számára,
+ akik közvetve vagy közvetlenül megkapják, akkor az egyetlen
+ módja, hogy eleget tegyen valaki mindkét feltételnek az, hogy
+ eláll a Program terjesztésétől.
+ </P>
+ <P>Ha ennek a szakasznak bármely része érvénytelen, vagy nem
+ érvényesíthető valamely körülmény folytán, akkor a szakasz
+ maradék részét kell alkalmazni, egyéb esetekben pedig a szakasz
+ egésze alkalmazandó.
+ </P>
+ <P>Ennek a szakasznak nem az a célja, hogy a szabadalmak vagy egyéb
+ hasonló jogok megsértésére ösztönözzön bárkit is; mindössze
+ meg szeretné védeni a szabad szoftver terjesztési rendszerének
+ egységét, amelyet a szabad közreadást szabályozó
+ feltételrendszerek teremtenek meg. Sok ember nagymértékben járult
+ hozzá az e rendszer keretében terjesztett, különféle
+ szoftverekhez, és számít a rendszer következetes alkalmazására;
+ azt a szerző/adományozó dönti el, hogy a szoftverét más
+ rendszer szerint is közzé kívánja-e tenni, és a licencet kapók
+ ezt nem befolyásolhatják.
+ </P>
+ <P>E szakasz célja, hogy pontosan tisztázza azt, ami elgondolásunk
+ szerint a jelen licenc többi részének a következménye.
+ </P>
+ <P><B>8.</B> Ha a Program terjesztése és/vagy használata egyes
+ országokban nem lehetséges akár szabadalmak, akár szerzői
+ jogokkal védett felületek miatt, akkor a Program szerzői jogainak
+ eredeti tulajdonosa, aki a Programot ezen szabályozás alapján
+ adja közre, egy explicit földrajzi megkötést adhat a
+ terjesztésre, és egyes országokat kizárhat. Ebben az esetben úgy
+ tekintendő, hogy a jelen licenc ezt a megkötést is tartalmazza,
+ ugyanúgy mintha csak a fő szövegében lenne leírva.
+ </P>
+ <P><B>9.</B> A Free Software Foundation időről időre kiadja a
+ General Public License dokumentum felülvizsgált és/vagy újabb
+ változatait. Ezek az újabb dokumentumok az előzőek szellemében
+ készülnek, de részletekben különbözhetnek, hogy új
+ problémákat vagy aggályokat is kezeljenek.
+ </P>
+ <P>A dokumentum minden változata egy megkülönböztető
+ verziószámmal ellátva jelenik meg. Ha a Program szerzői jogi
+ megjegyzésében egy bizonyos vagy annál újabb verzió van
+ megjelölve, akkor lehetőség van akár a megjelölt, vagy a Free
+ Software Foundation által kiadott későbbi verzióban leírt
+ feltételek követésére. Ha nincs ilyen megjelölt verzió, akkor
+ lehetőség van a Free Software Foundation által valaha kibocsátott
+ bármelyik dokumentum alkalmazására.
+ </P>
+ <P><B>10.</B> A Programot más szabad szoftverbe, amelynek szerzői
+ jogi szabályozása különbözik, csak akkor építheti be, ha a
+ szerzőtől erre engedélyt szerzett. Abban az esetben, ha a program
+ szerzői jogainak tulajdonosa a Free Software Foundation, akkor a
+ Free Software Foundation címére kell írni; néha kivételt
+ teszünk. A döntés a következő két cél szem előtt tartásával
+ fog történni: megmaradjon a szabad szoftveren alapuló munkák
+ szabad állapota, valamint segítse elő a szoftver
+ újrafelhasználását és megosztását.
+ </P>
+ <H2>GARANCIAVÁLLALÁS HIÁNYA
+ </H2>
+ <P><B>11.</B> MIVEL A JELEN PROGRAM HASZNÁLATI JOGA DÍJMENTES, AZ
+ ALKALMAZHATÓ JOGSZABÁLYOK ÁLTAL BIZTOSÍTOTT MAXIMÁLIS MÉRTÉKBEN
+ VISSZAUTASÍTJUK A PROGRAMHOZ A GARANCIA BIZTOSÍTÁSÁT. AMENNYIBEN
+ A SZERZŐI JOGOK TULAJDONOSAI ÍRÁSBAN MÁSKÉNT NEM NYILATKOZNAK,
+ A PROGRAM A &quot;JELEN ÁLLAPOTÁBAN&quot; KERÜL KIADÁSRA,
+ MINDENFÉLE GARANCIAVÁLLALÁS NÉLKÜL, LEGYEN AZ KIFEJEZETT VAGY
+ BELEÉRTETT, BELEÉRTVE, DE NEM KIZÁRÓLAGOSAN A FORGALOMBA
+ HOZHATÓSÁGRA VAGY ALKALMAZHATÓSÁGRA VONATKOZÓ GARANCIÁKAT. A
+ PROGRAM MINŐSÉGÉBŐL ÉS MŰKÖDÉSÉBŐL FAKADÓ ÖSSZES
+ KOCKÁZAT A FELHASZNÁLÓT TERHELI. HA A PROGRAM HIBÁSAN MŰKÖDIK,
+ A FELHASZNÁLÓNAK MAGÁNAK KELL VÁLLALNIA A JAVÍTÁSHOZ SZÜKSÉGES
+ MINDEN KÖLTSÉGET.
+ </P>
+ <P><B>12.</B> AMENNYIBEN A HATÁLYOS JOGSZABÁLYOK VAGY A SZERZŐI
+ JOGOK TULAJDONOSAI ÍRÁSOS MEGÁLLAPODÁSBAN MÁSKÉNT NEM
+ RENDELKEZNEK, SEM A PROGRAM SZERZŐJE, SEM MÁSOK, AKIK MÓDOSÍTOTTÁK
+ ÉS/VAGY TERJESZTETTÉK A PROGRAMOT A FENTIEKNEK MEGFELELŐEN, NEM
+ TEHETŐK FELELŐSSÉ A KÁROKÉRT, BELEÉRTVE MINDEN VÉLETLEN, VAGY
+ KÖVETKEZMÉNYES KÁRT, AMELY A PROGRAM HASZNÁLATÁBÓL VAGY A
+ HASZNÁLAT MEGAKADÁLYOZÁSÁBÓL SZÁRMAZIK (BELEÉRTVE, DE NEM
+ KIZÁRÓLAGOSAN AZ ADATVESZTÉST ÉS A HELYTELEN ADATFELDOLGOZÁST,
+ VALAMINT A MÁS PROGRAMOKKAL VALÓ HIBÁS EGYÜTTMŰKÖDÉST), MÉG
+ AKKOR SEM, HA EZEN FELEK TUDATÁBAN VOLTAK, HOGY ILYEN KÁROK
+ KELETKEZHETNEK.
+ </P>
+ <P><B>FELTÉTELEK ÉS SZABÁLYOK VÉGE</B>
+ </P>
+ <H2>Hogyan alkalmazhatók ezek a szabályok egy új programra?
+ </H2>
+ <P>Ha valaki egy új programot készít és szeretné, hogy az bárki
+ számára a lehető leginkább hasznos legyen, akkor a legjobb
+ módszer, hogy azt szabad szoftverré teszi, megengedve mindenkinek
+ a szabad másolást és módosítást a jelen feltételeknek
+ megfelelően.
+ </P>
+ <P>Ehhez a következő megjegyzést kell csatolni a programhoz. A
+ legbiztosabb ezt minden egyes forrásfájl elejére beírni, így
+ közölve leghatásosabban a garancia visszautasítását; ezenkívül
+ minden fájl kell, hogy tartalmazzon egy copyright sort és egy
+ mutatót arra a helyre, ahol a teljes szöveg található.
+ </P>
+ <PRE> <I>Egy sor, amely megadja a program nevét és funkcióját</I>
+ Copyright (C) év; szerző neve;
+
+ Ez a program szabad szoftver; terjeszthető illetve módosítható a
+ Free Software Foundation által kiadott GNU General Public License
+ dokumentumában leírtak; akár a licenc 2-es, akár (tetszőleges) későbbi
+ változata szerint.
+ Ez a program abban a reményben kerül közreadásra, hogy hasznos lesz,
+ de minden egyéb GARANCIA NÉLKÜL, az ELADHATÓSÁGRA vagy VALAMELY CÉLRA
+ VALÓ ALKALMAZHATÓSÁGRA való származtatott garanciát is beleértve.
+ További részleteket a GNU General Public License tartalmaz.
+ A felhasználónak a programmal együtt meg kell kapnia a GNU General
+ Public License egy példányát; ha mégsem kapta meg, akkor
+ ezt a Free Software Foundationnak küldött levélben jelezze
+ (cím: Free Software Foundation Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307, USA.)</PRE><P>
+ A programhoz csatolni kell azt is, hogy miként lehet kapcsolatba
+ lépni a szerzővel, elektronikus vagy hagyományos levél
+ küldésével.
+ </P>
+ <P>Ha a program interaktív, a következőhöz hasonló üzenettel
+ lehet ezt megtenni a program indulásakor:</P>
+ <PRE> Gnomovision version 69, Copyright (C) <I>év, a szerző neve</I>.
+ A Gnomovision programhoz SEMMILYEN GARANCIA NEM JÁR; részletekért
+ írja be a 'show w' parancsot. Ez egy szabad szoftver, bizonyos
+ feltételek mellett terjeszthető, illetve módosítható; részletekért
+ írja be a 'show c' parancsot.</PRE><P>
+ A show w és show c képzeletbeli parancsok, és a GPL megfelelő
+ részeit kell megjeleníteniük. Természetesen a valódi parancsok
+ a show w és show c parancstól különbözhetnek; lehetnek akár
+ egérkattintások vagy menüpontok is, ami a programnak megfelel.
+ </P>
+ <P>Ha szükséges, meg kell szerezni a munkáltatótól (ha a szerző
+ programozóként dolgozik) vagy az iskolától a program szerzői
+ jogairól való lemondás igazolását. Erre itt egy példa;
+ változtassa meg a neveket:
+ </P>
+ <PRE> A Fiktív Bt. ezennel lemond minden szerzői jogi érdekeltségéről
+ a „Gnomovision” programmal (amelyet több fázisban fordítanak le
+ a fordítóprogramok) kapcsolatban, amelyet H. Ekker János írt.
+
+ Aláírás: Tira Mihály, 1989. április 1. Tira Mihály ügyvezető</PRE><P STYLE="margin-bottom: 0cm">
+ A GNU General Public License nem engedi meg, hogy a program része
+ legyen szellemi tulajdont képező programoknak. Ha a program egy
+ szubrutinkönyvtár, akkor megfontolhatja, hogy nem célszerűbb-e
+ megengedni, hogy szellemi tulajdont képező alkalmazásokkal is
+ összefűzhető legyen a programkönyvtár. Ha ezt szeretné, akkor
+ a GPL helyett a GNU LGPL-t kell használni.</P>
+</DIV>
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/logo/src/xlogo/gpl/gpl-pt.html b/logo/src/xlogo/gpl/gpl-pt.html
new file mode 100644
index 0000000..5edb32d
--- /dev/null
+++ b/logo/src/xlogo/gpl/gpl-pt.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+ <title>GNU General Public Licence</title>
+</head>
+<body>
+<pre> LICEN&Ccedil;A P&Uacute;BLICA GERAL GNU<br> Vers&atilde;o 2, junho de 1991<br><br> This is an unofficial translation of the GNU General Public License<br> into Brazilian Portuguese. It was not published by the Free Software<br> Foundation, and does not legally state the distribution terms for<br> software that uses the GNU GPL -- only the original English text of<br> the GNU GPL does that. However, we hope that this translation will<br> help Brazilian Portuguese speakers understand the GNU GPL better.<br><br> Esta &eacute; uma tradu&ccedil;&atilde;o n&atilde;o-oficial da Licen&ccedil;a P&uacute;blica Geral GNU ("GPL<br> GNU") para o portugu&ecirc;s do Brasil. Ela n&atilde;o foi publicada pela Free<br>Software Foundation, e legalmente n&atilde;o afirma os termos de distribui&ccedil;&atilde;o<br> de software que utiliza a GPL GNU -- apenas o texto original da GPL<br> GNU, em ingl&ecirc;s, faz isso. Contudo, esperamos que esta tradu&ccedil;&atilde;o ajude<br> aos que utilizam o portugu&ecirc;s do Brasil a entender melhor a GPL GNU.<br><br> Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,<br> Cambridge, MA 02139, USA<br><br>A qualquer pessoa &eacute; permitido copiar e distribuir c&oacute;pias desse<br>documento de licen&ccedil;a, desde que sem qualquer altera&ccedil;&atilde;o.<br><br> Introdu&ccedil;&atilde;o<br><br> As licen&ccedil;as de muitos software s&atilde;o desenvolvidas para restringir sua<br>liberdade de compartilh&aacute;-lo e mud&aacute;-lo. Contr&aacute;ria a isso, a Licen&ccedil;a<br>P&uacute;blica Geral GNU pretende garantir sua liberdade de compartilhar e<br>alterar software livres -- garantindo que o software ser&aacute; livre e<br>gratuito para os seus usu&aacute;rios. Esta Licen&ccedil;a P&uacute;blica Geral aplica-se &agrave;<br>maioria dos software da Free Software Foundation e a qualquer outro<br>programa cujo autor decida aplic&aacute;-la. (Alguns outros software da FSF<br>s&atilde;o cobertos pela Licen&ccedil;a P&uacute;blica Geral de Bibliotecas, no entanto.)<br>Voc&ecirc; pode aplic&aacute;-la tamb&eacute;m aos seus programas.<br><br> Quando nos referimos a software livre, estamos nos referindo a<br>liberdade e n&atilde;o a pre&ccedil;o. Nossa Licen&ccedil;a P&uacute;blica Geral foi desenvolvida<br>para garantir que voc&ecirc; tenha a liberdade de distribuir c&oacute;pias de<br>software livre (e cobrar por isso, se quiser); que voc&ecirc; receba o<br>c&oacute;digo-fonte ou tenha acesso a ele, se quiser; que voc&ecirc; possa mudar o<br>software ou utilizar partes dele em novos programas livres e<br>gratuitos; e que voc&ecirc; saiba que pode fazer tudo isso.<br><br> Para proteger seus direitos, precisamos fazer restri&ccedil;&otilde;es que impe&ccedil;am<br>a qualquer um negar estes direitos ou solicitar que voc&ecirc; deles<br>abdique. Estas restri&ccedil;&otilde;es traduzem-se em certas responsabilidades para<br>voc&ecirc;, se voc&ecirc; for distribuir c&oacute;pias do software ou modific&aacute;-lo.<br><br> Por exemplo, se voc&ecirc; distribuir c&oacute;pias de um programa, gratuitamente<br>ou por alguma quantia, voc&ecirc; tem que fornecer aos recebedores todos os<br>direitos que voc&ecirc; possui. Voc&ecirc; tem que garantir que eles tamb&eacute;m<br>recebam ou possam obter o c&oacute;digo-fonte. E voc&ecirc; tem que mostrar-lhes<br>estes termos para que eles possam conhecer seus direitos.<br><br> N&oacute;s protegemos seus direitos em dois passos: (1) com copyright do<br>software e (2) com a oferta desta licen&ccedil;a, que lhe d&aacute; permiss&atilde;o legal<br>para copiar, distribuir e/ou modificar o software.<br><br> Al&eacute;m disso, tanto para a prote&ccedil;&atilde;o do autor quanto a nossa,<br>gostar&iacute;amos de certificar-nos que todos entendam que n&atilde;o h&aacute; qualquer<br>garantia nestes software livres. Se o software &eacute; modificado por algu&eacute;m<br>mais e passado adiante, queremos que seus recebedores saibam que o que<br>eles obtiveram n&atilde;o &eacute; original, de forma que qualquer problema<br>introduzido por terceiros n&atilde;o interfira na reputa&ccedil;&atilde;o do autor<br>original.<br><br> Finalmente, qualquer programa &eacute; amea&ccedil;ado constantemente por patentes<br>de software. Queremos evitar o perigo de que distribuidores de<br>software livre obtenham patentes individuais, o que tem o efeito de<br>tornar o programa propriet&aacute;rio. Para prevenir isso, deixamos claro que<br>qualquer patente tem que ser licenciada para uso livre e gratuito por<br>qualquer pessoa, ou ent&atilde;o que nem necessite ser licenciada.<br><br> Os termos e condi&ccedil;&otilde;es precisas para c&oacute;pia, distribui&ccedil;&atilde;o e<br>modifica&ccedil;&atilde;o se encontram abaixo:<br> <br> LICEN&Ccedil;A P&Uacute;BLICA GERAL GNU<br> TERMOS E CONDI&Ccedil;&Otilde;ES PARA C&Oacute;PIA, DISTRIBUI&Ccedil;&Atilde;O E MODIFICA&Ccedil;&Atilde;O<br><br> 0. Esta licen&ccedil;a se aplica a qualquer programa ou outro trabalho que<br>contenha um aviso colocado pelo detentor dos direitos autorais<br>informando que aquele pode ser distribu&iacute;do sob as condi&ccedil;&otilde;es desta<br>Licen&ccedil;a P&uacute;blica Geral. O "Programa" abaixo refere-se a qualquer<br>programa ou trabalho, e "trabalho baseado no Programa" significa tanto<br>o Programa em si como quaisquer trabalhos derivados, de acordo com a<br>lei de direitos autorais: isto quer dizer um trabalho que contenha o<br>Programa ou parte dele, tanto originalmente ou com modifica&ccedil;&otilde;es, e/ou<br>tradu&ccedil;&atilde;o para outros idiomas. (Doravante o processo de tradu&ccedil;&atilde;o est&aacute;<br>inclu&iacute;do sem limites no termo "modifica&ccedil;&atilde;o".) Cada licenciado &eacute;<br>mencionado como "voc&ecirc;".<br><br>Atividades outras que a c&oacute;pia, a distribui&ccedil;&atilde;o e modifica&ccedil;&atilde;o n&atilde;o est&atilde;o<br>cobertas por esta Licen&ccedil;a; elas est&atilde;o fora de seu escopo. O ato de<br>executar o Programa n&atilde;o &eacute; restringido e o resultado do Programa &eacute;<br>coberto apenas se seu conte&uacute;do contenha trabalhos baseados no Programa<br>(independentemente de terem sido gerados pela execu&ccedil;&atilde;o do<br>Programa). Se isso &eacute; verdadeiro depende do que o programa faz.<br><br> 1. Voc&ecirc; pode copiar e distribuir c&oacute;pias fi&eacute;is do c&oacute;digo-fonte do<br>Programa da mesma forma que voc&ecirc; o recebeu, usando qualquer meio,<br>deste que voc&ecirc; consp&iacute;cua e apropriadamente publique em cada c&oacute;pia um<br>aviso de direitos autorais e uma declara&ccedil;&atilde;o de inexist&ecirc;ncia de<br>garantias; mantenha intactas todos os avisos que se referem a esta<br>Licen&ccedil;a e &agrave; aus&ecirc;ncia total de garantias; e forne&ccedil;a a outros<br>recebedores do Programa uma c&oacute;pia desta Licen&ccedil;a, junto com o Programa.<br><br>Voc&ecirc; pode cobrar pelo ato f&iacute;sico de transferir uma c&oacute;pia e pode,<br>opcionalmente, oferecer garantia em troca de pagamento.<br><br> 2. Voc&ecirc; pode modificar sua c&oacute;pia ou c&oacute;pias do Programa, ou qualquer<br>parte dele, assim gerando um trabalho baseado no Programa, e copiar e<br>distribuir essas modifica&ccedil;&otilde;es ou trabalhos sob os termos da se&ccedil;&atilde;o 1<br>acima, desde que voc&ecirc; tamb&eacute;m se enquadre em todas estas condi&ccedil;&otilde;es:<br><br> a) Voc&ecirc; tem que fazer com que os arquivos modificados levem avisos<br> proeminentes afirmando que voc&ecirc; alterou os arquivos, incluindo a<br> data de qualquer altera&ccedil;&atilde;o.<br><br> b) Voc&ecirc; tem que fazer com que quaisquer trabalhos que voc&ecirc;<br> distribua ou publique, e que integralmente ou em partes contenham<br> ou sejam derivados do Programa ou de suas partes, sejam<br> licenciados, integralmente e sem custo algum para quaisquer<br> terceiros, sob os termos desta Licen&ccedil;a.<br><br> c) Se qualquer programa modificado normalmente l&ecirc; comandos<br> interativamente quando executados, voc&ecirc; tem que fazer com que,<br> quando iniciado tal uso interativo da forma mais simples, seja<br> impresso ou mostrado um an&uacute;ncio de que n&atilde;o h&aacute; qualquer garantia<br> (ou ent&atilde;o que voc&ecirc; fornece a garantia) e que os usu&aacute;rios podem<br> redistribuir o programa sob estas condi&ccedil;&otilde;es, ainda informando os<br> usu&aacute;rios como consultar uma c&oacute;pia desta Licen&ccedil;a. (Exce&ccedil;&atilde;o: se o<br> Programa em si &eacute; interativo mas normalmente n&atilde;o imprime estes<br> tipos de an&uacute;ncios, seu trabalho baseado no Programa n&atilde;o precisa<br> imprimir um an&uacute;ncio.)<br><br>Estas exig&ecirc;ncias aplicam-se ao trabalho modificado como um todo. Se<br>se&ccedil;&otilde;es identific&aacute;veis de tal trabalho n&atilde;o s&atilde;o derivadas do Programa, e<br>podem ser razoavelmente consideradas trabalhos independentes e<br>separados por si s&oacute;, ent&atilde;o esta Licen&ccedil;a, e seus termos, n&atilde;o se aplicam<br>a estas se&ccedil;&otilde;es quando voc&ecirc; distribui-las como trabalhos em<br>separado. Mas quando voc&ecirc; distribuir as mesmas se&ccedil;&otilde;es como parte de um<br>todo que &eacute; trabalho baseado no Programa, a distribui&ccedil;&atilde;o como um todo<br>tem que se enquadrar nos termos desta Licen&ccedil;a, cujas permiss&otilde;es para<br>outros licenciados se estendem ao todo, portanto tamb&eacute;m para cada e<br>toda parte independente de quem a escreveu.<br><br>Desta forma, esta se&ccedil;&atilde;o n&atilde;o tem a inten&ccedil;&atilde;o de reclamar direitos os<br>contestar seus direitos sobre o trabalho escrito completamente por<br>voc&ecirc;; ao inv&eacute;s disso, a inten&ccedil;&atilde;o &eacute; a de exercitar o direito de<br>controlar a distribui&ccedil;&atilde;o de trabalhos, derivados ou coletivos,<br>baseados no Programa.<br><br>Adicionalmente, a mera adi&ccedil;&atilde;o ao Programa de outro trabalho n&atilde;o<br>baseado no Programa (ou de trabalho baseado no Programa) em um volume<br>de armazenamento ou meio de distribui&ccedil;&atilde;o n&atilde;o faz o outro trabalho<br>parte do escopo desta Licen&ccedil;a.<br><br> 3. Voc&ecirc; pode copiar e distribuir o Programa (ou trabalho baseado<br>nele, conforme descrito na Se&ccedil;&atilde;o 2) em c&oacute;digo-objeto ou em forma<br>execut&aacute;vel sob os termos das Se&ccedil;&otilde;es 1 e 2 acima, desde que voc&ecirc;<br>fa&ccedil;a um dos seguintes:<br><br> a) O acompanhe com o c&oacute;digo-fonte completo e em forma acess&iacute;vel<br> por m&aacute;quinas, que tem que ser distribu&iacute;do sob os termos das Se&ccedil;&otilde;es<br> 1 e 2 acima e em meio normalmente utilizado para o interc&acirc;mbio de<br> software; ou,<br><br> b) O acompanhe com uma oferta escrita, v&aacute;lida por pelo menos tr&ecirc;s<br> anos, de fornecer a qualquer um, com um custo n&atilde;o superior ao<br> custo de distribui&ccedil;&atilde;o f&iacute;sica do material, uma c&oacute;pia do<br> c&oacute;digo-fonte completo e em forma acess&iacute;vel por m&aacute;quinas, que tem<br> que ser distribu&iacute;do sob os termos das Se&ccedil;&otilde;es 1 e 2 acima e em meio<br> normalmente utilizado para o interc&acirc;mbio de software; ou,<br><br> c) O acompanhe com a informa&ccedil;&atilde;o que voc&ecirc; recebeu em rela&ccedil;&atilde;o &agrave;<br> oferta de distribui&ccedil;&atilde;o do c&oacute;digo-fonte correspondente. (Esta<br> alternativa &eacute; permitida somente em distribui&ccedil;&atilde;o n&atilde;o comerciais, e<br> apenas se voc&ecirc; recebeu o programa em forma de c&oacute;digo-objeto ou<br> execut&aacute;vel, com oferta de acordo com a Subse&ccedil;&atilde;o b acima.)<br><br>O c&oacute;digo-fonte de um trabalho corresponde &agrave; forma de trabalho<br>preferida para se fazer modifica&ccedil;&otilde;es. Para um trabalho em forma<br>execut&aacute;vel, o c&oacute;digo-fonte completo significa todo o c&oacute;digo-fonte de<br>todos os m&oacute;dulos que ele cont&eacute;m, mais quaisquer arquivos de defini&ccedil;&atilde;o<br>de "interface", mais os "scripts" utilizados para se controlar a<br>compila&ccedil;&atilde;o e a instala&ccedil;&atilde;o do execut&aacute;vel. Contudo, como exce&ccedil;&atilde;o<br>especial, o c&oacute;digo-fonte distribu&iacute;do n&atilde;o precisa incluir qualquer<br>componente normalmente distribu&iacute;do (tanto em forma original quanto<br>bin&aacute;ria) com os maiores componentes (o compilador, o "kernel" etc.) do<br>sistema operacional sob o qual o execut&aacute;vel funciona, a menos que o<br>componente em si acompanhe o execut&aacute;vel.<br><br>Se a distribui&ccedil;&atilde;o do execut&aacute;vel ou c&oacute;digo-objeto &eacute; feita atrav&eacute;s da<br>oferta de acesso a c&oacute;pias de algum lugar, ent&atilde;o ofertar o acesso<br>equivalente a c&oacute;pia, do mesmo lugar, do c&oacute;digo-fonte equivale &agrave;<br>distribui&ccedil;&atilde;o do c&oacute;digo-fonte, mesmo que terceiros n&atilde;o sejam compelidos<br>a copiar o c&oacute;digo-fonte com o c&oacute;digo-objeto.<br><br> 4. Voc&ecirc; n&atilde;o pode copiar, modificar, sub-licenciar ou distribuir o<br>Programa, exceto de acordo com as condi&ccedil;&otilde;es expressas nesta<br>Licen&ccedil;a. Qualquer outra tentativa de c&oacute;pia, modifica&ccedil;&atilde;o,<br>sub-licenciamento ou distribui&ccedil;&atilde;o do Programa n&atilde;o &eacute; valida, e<br>cancelar&aacute; automaticamente os direitos que lhe foram fornecidos por<br>esta Licen&ccedil;a. No entanto, terceiros que de voc&ecirc; receberam c&oacute;pias ou<br>direitos, fornecidos sob os termos desta Licen&ccedil;a, n&atilde;o ter&atilde;o suas<br>licen&ccedil;as terminadas, desde que permane&ccedil;am em total concord&acirc;ncia com<br>ela.<br><br> 5. Voc&ecirc; n&atilde;o &eacute; obrigado a aceitar esta Licen&ccedil;a j&aacute; que n&atilde;o a<br>assinou. No entanto, nada mais o dar&aacute; permiss&atilde;o para modificar ou<br>distribuir o Programa ou trabalhos derivados deste. Estas a&ccedil;&otilde;es s&atilde;o<br>proibidas por lei, caso voc&ecirc; n&atilde;o aceite esta Licen&ccedil;a. Desta forma, ao<br>modificar ou distribuir o Programa (ou qualquer trabalho derivado do<br>Programa), voc&ecirc; estar&aacute; indicando sua total aceita&ccedil;&atilde;o desta Licen&ccedil;a<br>para faz&ecirc;-los, e todos os seus termos e condi&ccedil;&otilde;es para copiar,<br>distribuir ou modificar o Programa, ou trabalhos baseados nele.<br><br> 6. Cada vez que voc&ecirc; redistribuir o Programa (ou qualquer trabalho<br>baseado nele), os recebedores adquirir&atilde;o automaticamente do<br>licenciador original uma licen&ccedil;a para copiar, distribuir ou modificar<br>o Programa, sujeitos a estes termos e condi&ccedil;&otilde;es. Voc&ecirc; n&atilde;o poder&aacute; impor<br>aos recebedores qualquer outra restri&ccedil;&atilde;o ao exerc&iacute;cio dos direitos<br>ent&atilde;o adquiridos. Voc&ecirc; n&atilde;o &eacute; respons&aacute;vel em garantir a concord&acirc;ncia de<br>terceiros a esta Licen&ccedil;a.<br><br> 7. Se, em conseq&uuml;&ecirc;ncia de decis&otilde;es judiciais ou alega&ccedil;&otilde;es de<br>infringimento de patentes ou quaisquer outras raz&otilde;es (n&atilde;o limitadas a<br>assuntos relacionados a patentes), condi&ccedil;&otilde;es forem impostas a voc&ecirc;<br>(por ordem judicial, acordos ou outras formas) e que contradigam as<br>condi&ccedil;&otilde;es desta Licen&ccedil;a, elas n&atilde;o o livram das condi&ccedil;&otilde;es desta<br>Licen&ccedil;a. Se voc&ecirc; n&atilde;o puder distribuir de forma a satisfazer<br>simultaneamente suas obriga&ccedil;&otilde;es para com esta Licen&ccedil;a e para com as<br>outras obriga&ccedil;&otilde;es pertinentes, ent&atilde;o como conseq&uuml;&ecirc;ncia voc&ecirc; n&atilde;o poder&aacute;<br>distribuir o Programa. Por exemplo, se uma licen&ccedil;a de patente n&atilde;o<br>permitir&aacute; a redistribui&ccedil;&atilde;o, livre de "royalties", do Programa, por<br>todos aqueles que receberem c&oacute;pias direta ou indiretamente de voc&ecirc;,<br>ent&atilde;o a &uacute;nica forma de voc&ecirc; satisfazer a ela e a esta Licen&ccedil;a seria a<br>de desistir completamente de distribuir o Programa.<br><br>Se qualquer parte desta se&ccedil;&atilde;o for considerada inv&aacute;lida ou n&atilde;o<br>aplic&aacute;vel em qualquer circunst&acirc;ncia particular, o restante da se&ccedil;&atilde;o se<br>aplica, e a se&ccedil;&atilde;o como um todo se aplica em outras circunst&acirc;ncias.<br><br>O prop&oacute;sito desta se&ccedil;&atilde;o n&atilde;o &eacute; o de induzi-lo a infringir quaisquer<br>patentes ou reivindica&ccedil;&atilde;o de direitos de propriedade outros, ou a<br>contestar a validade de quaisquer dessas reivindica&ccedil;&otilde;es; esta se&ccedil;&atilde;o<br>tem como &uacute;nico prop&oacute;sito proteger a integridade dos sistemas de<br>distribui&ccedil;&atilde;o de software livres, o que &eacute; implementado pela pr&aacute;tica de<br>licen&ccedil;as p&uacute;blicas. V&aacute;rias pessoas t&ecirc;m contribu&iacute;do generosamente e em<br>grande escala para os software distribu&iacute;dos usando este sistema, na<br>certeza de que sua aplica&ccedil;&atilde;o &eacute; feita de forma consistente; fica a<br>crit&eacute;rio do autor/doador decidir se ele ou ela est&aacute; disposto a<br>distribuir software utilizando outro sistema, e um licenciado n&atilde;o pode<br>impor qualquer escolha.<br><br>Esta se&ccedil;&atilde;o destina-se a tornar bastante claro o que se acredita ser<br>conseq&uuml;&ecirc;ncia do restante desta Licen&ccedil;a.<br><br> 8. Se a distribui&ccedil;&atilde;o e/ou uso do Programa s&atilde;o restringidos em certos<br>pa&iacute;ses por patentes ou direitos autorais, o detentor dos direitos<br>autorais original, e que colocou o Programa sob esta Licen&ccedil;a, pode<br>incluir uma limita&ccedil;&atilde;o geogr&aacute;fica de distribui&ccedil;&atilde;o, excluindo aqueles<br>pa&iacute;ses de forma a tornar a distribui&ccedil;&atilde;o permitida apenas naqueles ou<br>entre aqueles pa&iacute;ses ent&atilde;o n&atilde;o exclu&iacute;dos. Nestes casos, esta Licen&ccedil;a<br>incorpora a limita&ccedil;&atilde;o como se a mesma constasse escrita nesta Licen&ccedil;a.<br><br> 9. A Free Software Foundation pode publicar vers&otilde;es revisadas e/ou<br>novas da Licen&ccedil;a P&uacute;blica Geral de tempos em tempos. Estas novas<br>vers&otilde;es ser&atilde;o similares em esp&iacute;rito &agrave; vers&atilde;o atual, mas podem diferir<br>em detalhes que resolvem novos problemas ou situa&ccedil;&otilde;es.<br><br>A cada vers&atilde;o &eacute; dada um n&uacute;mero distinto. Se o Programa especifica um<br>n&uacute;mero de vers&atilde;o espec&iacute;fico desta Licen&ccedil;a que se aplica a ele e a<br>"qualquer nova vers&atilde;o", voc&ecirc; tem a op&ccedil;&atilde;o de aceitar os termos e<br>condi&ccedil;&otilde;es daquela vers&atilde;o ou de qualquer outra vers&atilde;o publicada pela<br>Free Software Foundation. Se o programa n&atilde;o especifica um n&uacute;mero de<br>vers&atilde;o desta Licen&ccedil;a, voc&ecirc; pode escolher qualquer vers&atilde;o j&aacute; publicada<br>pela Free Software Foundation.<br><br> 10. Se voc&ecirc; pretende incorporar partes do Programa em outros<br>programas livres cujas condi&ccedil;&otilde;es de distribui&ccedil;&atilde;o s&atilde;o diferentes,<br>escreva ao autor e solicite permiss&atilde;o. Para o software que a Free<br>Software Foundation det&eacute;m direitos autorais, escreva &agrave; Free Software<br>Foundation; &agrave;s vezes n&oacute;s permitimos exce&ccedil;&otilde;es a este caso. Nossa<br>decis&atilde;o ser&aacute; guiada pelos dois objetivos de preservar a condi&ccedil;&atilde;o de<br>liberdade de todas as deriva&ccedil;&otilde;es do nosso software livre, e de<br>promover o compartilhamento e reutiliza&ccedil;&atilde;o de software em aspectos<br>gerais.<br><br> AUS&Ecirc;NCIA DE GARANTIAS<br><br> 11. UMA VEZ QUE O PROGRAMA &Eacute; LICENCIADO SEM &Ocirc;NUS, N&Atilde;O H&Aacute; QUALQUER<br>GARANTIA PARA O PROGRAMA, NA EXTENS&Atilde;O PERMITIDA PELAS LEIS<br>APLIC&Aacute;VEIS. EXCETO QUANDO EXPRESSADO DE FORMA ESCRITA, OS DETENTORES<br>DOS DIREITOS AUTORAIS E/OU TERCEIROS DISPONIBILIZAM O PROGRAMA "NO<br>ESTADO", SEM QUALQUER TIPO DE GARANTIAS, EXPRESSAS OU IMPL&Iacute;CITAS,<br>INCLUINDO, MAS N&Atilde;O LIMITADO A, AS GARANTIAS IMPL&Iacute;CITAS DE<br>COMERCIALIZA&Ccedil;&Atilde;O E AS DE ADEQUA&Ccedil;&Atilde;O A QUALQUER PROP&Oacute;SITO. O RISCO TOTAL<br>COM A QUALIDADE E DESEMPENHO DO PROGRAMA &Eacute; SEU. SE O PROGRAMA SE<br>MOSTRAR DEFEITUOSO, VOC&Ecirc; ASSUME OS CUSTOS DE TODAS AS MANUTEN&Ccedil;&Otilde;ES,<br>REPAROS E CORRE&Ccedil;&Otilde;ES.<br><br> 12. EM NENHUMA OCASI&Atilde;O, A MENOS QUE EXIGIDO PELAS LEIS APLIC&Aacute;VEIS OU<br>ACORDO ESCRITO, OS DETENTORES DOS DIREITOS AUTORAIS, OU QUALQUER OUTRA<br>PARTE QUE POSSA MODIFICAR E/OU REDISTRIBUIR O PROGRAMA CONFORME<br>PERMITIDO ACIMA, SER&Atilde;O RESPONSABILIZADOS POR VOC&Ecirc; POR DANOS, INCLUINDO<br>QUALQUER DANO EM GERAL, ESPECIAL, ACIDENTAL OU CONSEQ&Uuml;ENTE,<br>RESULTANTES DO USO OU INCAPACIDADE DE USO DO PROGRAMA (INCLUINDO, MAS<br>N&Atilde;O LIMITADO A, A PERDA DE DADOS OU DADOS TORNADOS INCORRETOS, OU<br>PERDAS SOFRIDAS POR VOC&Ecirc; OU POR OUTRAS PARTES, OU FALHAS DO PROGRAMA<br>AO OPERAR COM QUALQUER OUTRO PROGRAMA), MESMO QUE TAL DETENTOR OU<br>PARTE TENHAM SIDO AVISADOS DA POSSIBILIDADE DE TAIS DANOS.<br><br> FIM DOS TERMOS E CONDI&Ccedil;&Otilde;ES<br><br> Como Aplicar Estes Termos aos Seus Novos Programas<br><br> Se voc&ecirc; desenvolver um novo programa, e quer que ele seja utilizado<br>amplamente pelo p&uacute;blico, a melhor forma de alcan&ccedil;ar este objetivo &eacute;<br>torn&aacute;-lo software livre que qualquer um pode redistribuir e alterar,<br>sob estes termos.<br><br> Para isso, anexe os seguintes avisos ao programa. &Eacute; mais seguro<br>anex&aacute;-los logo no in&iacute;cio de cada arquivo-fonte para refor&ccedil;arem mais<br>efetivamente a inexist&ecirc;ncia de garantias; e cada arquivo deve possuir<br>pelo menos a linha de "copyright" e uma indica&ccedil;&atilde;o de onde o texto<br>completo se encontra.<br><br> &lt;uma linha que forne&ccedil;a o nome do programa e uma id&eacute;ia do que ele faz.&gt;<br> Copyright (C) &lt;ano&gt; &lt;nome do autor&gt;<br> <br> Este programa &eacute; software livre; voc&ecirc; pode redistribu&iacute;-lo e/ou<br> modific&aacute;-lo sob os termos da Licen&ccedil;a P&uacute;blica Geral GNU, conforme<br> publicada pela Free Software Foundation; tanto a vers&atilde;o 2 da<br> Licen&ccedil;a como (a seu crit&eacute;rio) qualquer vers&atilde;o mais nova.<br><br> Este programa &eacute; distribu&iacute;do na expectativa de ser &uacute;til, mas SEM<br> QUALQUER GARANTIA; sem mesmo a garantia impl&iacute;cita de<br> COMERCIALIZA&Ccedil;&Atilde;O ou de ADEQUA&Ccedil;&Atilde;O A QUALQUER PROP&Oacute;SITO EM<br> PARTICULAR. Consulte a Licen&ccedil;a P&uacute;blica Geral GNU para obter mais<br> detalhes.<br> <br> Voc&ecirc; deve ter recebido uma c&oacute;pia da Licen&ccedil;a P&uacute;blica Geral GNU<br> junto com este programa; se n&atilde;o, escreva para a Free Software<br> Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA<br> 02111-1307, USA.<br><br>Inclua tamb&eacute;m informa&ccedil;&otilde;es sobre como contact&aacute;-lo eletronicamente e por<br>carta.<br><br>Se o programa &eacute; interativo, fa&ccedil;a-o mostrar um aviso breve como este,<br>ao iniciar um modo interativo:<br><br> Gnomovision vers&atilde;o 69, Copyright (C) ano nome do autor<br> O Gnomovision n&atilde;o possui QUALQUER GARANTIA; para obter mais<br> detalhes digite `show w'. Ele &eacute; software livre e voc&ecirc; est&aacute;<br> convidado a redistribui-lo sob certas condi&ccedil;&otilde;es; digite `show c'<br> para obter detalhes.<br><br>Os comandos hipot&eacute;ticos `show w' e `show c' devem mostrar as partes<br>apropriadas da Licen&ccedil;a P&uacute;blica Geral. Claro, os comandos que voc&ecirc; usar<br>podem ser ativados de outra forma que `show w' e `show c'; eles podem<br>at&eacute; ser cliques do mouse ou itens de um menu -- o que melhor se<br>adequar ao programa.<br><br>Voc&ecirc; tamb&eacute;m deve obter do seu empregador (se voc&ecirc; trabalha como<br>programador) ou escola, se houver, uma "declara&ccedil;&atilde;o de aus&ecirc;ncia de<br>direitos autorais" sobre o programa, se necess&aacute;rio. Aqui est&aacute; um<br>exemplo; altere os nomes:<br><br> Yoyodyne, Inc., aqui declara a aus&ecirc;ncia de quaisquer direitos<br> autorais sobre o programa `Gnomovision' (que executa interpreta&ccedil;&otilde;es<br> em compiladores) escrito por James Hacker.<br><br> &lt;assinatura de Ty Coon&gt;, 1o. de abril de 1989<br> Ty Con, Vice-presidente<br><br>Esta Licen&ccedil;a P&uacute;blica Geral n&atilde;o permite incorporar seu programa em<br>programas propriet&aacute;rios. Se seu programa &eacute; uma biblioteca de<br>sub-rotinas, voc&ecirc; deve considerar mais &uacute;til permitir ligar aplica&ccedil;&otilde;es<br>propriet&aacute;rias com a biblioteca. Se isto &eacute; o que voc&ecirc; deseja, use a<br>Licen&ccedil;a P&uacute;blica Geral de Bibliotecas GNU, ao inv&eacute;s desta Licen&ccedil;a.<br></pre>
+</body>
+</html>
diff --git a/logo/src/xlogo/gpl/x4s_info.html b/logo/src/xlogo/gpl/x4s_info.html
new file mode 100644
index 0000000..ff7458e
--- /dev/null
+++ b/logo/src/xlogo/gpl/x4s_info.html
@@ -0,0 +1,42 @@
+<html>
+ <head>
+ <title>HTML Online Editor Sample</title>
+ </head>
+ <body>
+ <h2>
+ XLogo4Schools</h2>
+ <p>
+ XLogo4Schools is a modified, extended and reengineered version of Lo&#239;c Le Coq&#39;s XLogo. It is the result of a six month Bachelor&#39;s thesis conducted by Marko &#381;ivkovi&#263; at ETHZ, D-INFK department from March 2013 until September 2013.</p>
+ <p>
+ I want to thank <a href="http://www.ite.ethz.ch/people/host/jhromkov">Prof. Juraj Hromkovi&#269;</a> for giving me the opportunity to dive into the world of Logo and for letting me do the reengineering of XLogo for my Bachelor&#39;s Thesis. Very special thanks go to <a href="http://www.ite.ethz.ch/people/fachdidaktik/serafing/curriculum">Giovanni Serafini</a> who acted as an excellent adviser and mentor during this project. I also want to thank <a href="http://www.inf.ethz.ch/personal/gebauerh/">Dr. Heidi Gebauer</a>, <a href="http://www.ite.ethz.ch/people/postdocs/ivanak">Dr. Ivana Kosirova</a>, <a href="http://www.ite.ethz.ch/people/researchassistants/lkeller">Lucia Keller</a>,&nbsp; and <a href="http://www.ite.ethz.ch/people/researchassistants/steffenb">Bj&ouml;rn Steffen</a> from Prof. Hromkovi&#269;&#39;s chair for their intensive contributions during the requirements analysis phase. I enjoyed the many talks and discussions and appreciated every new idea from you. And finally I want to thank the other Logo tutors, Lukas H&auml;fliger, Samuel Bryner,&nbsp; Alexander Viand, Petra Hromkovi&#269;ova, Stefan Dietiker, and Christine Zeller, who contributed in numerous inspiring talks during the many Logo projects.</p>
+ <h3>
+ What was reengineered?</h3>
+ <p>
+ The new main features introduced are</p>
+ <ul>
+ <li>
+ <strong>Multiple active files</strong>. That implies an ambiguity check among several files. The internal workspace had to be changed completely.</li>
+ <li>
+ <strong>Automatic saving</strong> to the file system</li>
+ <li>
+ <strong>Backup system</strong> - the teacher&#39;s specify how many old version of the pupil&#39;s files should be kept in a backup folder</li>
+ <li>
+ <strong>Competition/record mode</strong> - a predefined number of files, one for each exercise is created, each having a clock. The children&#39;s inputs are recorded in a file for later analysis</li>
+ <li>
+ More accurate error messages when parsing the procedure&#39;s signatures.</li>
+ <li>
+ A simple workspace management system. Workspaces consist of multiple projects or users. Certain settings apply to the whole workspace, others are per user or project.</li>
+ </ul>
+ <p>
+ What was changed?</p>
+ <ul>
+ <li>
+ The <strong>user interface</strong> has completely changed. I reduced the complexity of customizability to the minimum. Very necessary settings can be defined by the teachers, but not by the children. There is a simple password mechanism to hide these settings. The main window now includes everything that is needed. A command line, the drawing area, the history panel, and the list of files to the left. There is also a procedue search, that lets one more easily find the desired procedures amongst the files. The editor is now integrated in the same window. When a file is opened, the editor is shown instead of the drawing area and the command line. All the options menus and preferences have been removed. The turtle speed and the turtle icon can be changed in the main window directly. I removed these border-images that were used by XLogo, instead providing a larger view onto the drawing area.</li>
+ <li>
+ The core of the application, the interpreter and drawing engine are left almost untouched. Things that I had to alter a lot have become replaced by loosely coupled components that can work independently of each other. The communication between different parts of the application is now event driven instead of static variables and similar anti-patterns.</li>
+ <li>
+ The Graphics are much smoother due to another rendering configuration.</li>
+ <li>
+ Measured on a Sierpinski Triangle of depth 9, XLogo4Schools is around 25% faster than XLogo (16s vs. 21s on my machine). It is even faster, when many procedures are added to the workspace. XLogo uses a stack to manage its procedures and it uses O(N) string comparisons to find a procedure. Interestingly, the hash operations push and pop are not really used. XLogo4Schools uses a HashMap that usually works in O(1).</li>
+ </ul></body>
+</html> \ No newline at end of file
diff --git a/logo/src/xlogo/gui/AImprimer.java b/logo/src/xlogo/gui/AImprimer.java
new file mode 100644
index 0000000..7f20ecb
--- /dev/null
+++ b/logo/src/xlogo/gui/AImprimer.java
@@ -0,0 +1,90 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+package xlogo.gui;
+
+import java.awt.print.*;
+import javax.swing.*;
+
+import java.awt.Image;
+import java.awt.Graphics;
+
+public class AImprimer extends JPanel implements Printable, Runnable {
+ private static final long serialVersionUID = 1L;
+ private Image image;
+
+ public AImprimer(Image image) {
+ this.image = image;
+ }
+
+ public void run() {
+ Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
+ PrinterJob job = PrinterJob.getPrinterJob();
+ job.setPrintable(this);
+ if (job.printDialog()) {
+ try {
+ job.print();
+ } catch (PrinterException ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+ }
+
+ public int print(Graphics g, PageFormat pf, int pi) throws PrinterException {
+ double largeur = image.getWidth(this);
+ double hauteur = image.getHeight(this);
+ double facteur = pf.getImageableWidth() / largeur; // largeur
+ // imprimable sur la
+ // feuille
+ double facteur2 = pf.getImageableHeight() / hauteur; // hauteur
+ // imprimable
+ // sur la
+ // feuille
+
+ if (facteur < 1 | facteur2 < 1) {
+ facteur = Math.min(facteur, facteur2);
+ image = image.getScaledInstance((int) (largeur * facteur),
+ (int) (hauteur * facteur), Image.SCALE_SMOOTH);
+ }
+ largeur = image.getWidth(this); // permet d'attendre que l'image soit
+ // bien créée
+ hauteur = image.getHeight(this);
+ if (pi < 1) {
+ g.drawImage(this.image, (int) pf.getImageableX(), (int) pf
+ .getImageableY(), this);
+ return (Printable.PAGE_EXISTS);
+ } else
+ return Printable.NO_SUCH_PAGE;
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/gui/Editor.java b/logo/src/xlogo/gui/Editor.java
new file mode 100644
index 0000000..6da4cd8
--- /dev/null
+++ b/logo/src/xlogo/gui/Editor.java
@@ -0,0 +1,396 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyListener;
+
+import javax.swing.GroupLayout;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+import xlogo.Application;
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.Logo;
+
+/**
+ * Title : XLogo Description : XLogo is an interpreter for the Logo programming
+ * language
+ * <p>
+ * <p>
+ * Changes made in July 2013 by Marko Zivkovic: All the procedure analyzing is
+ * moved to class 'Workspace' which was then renamed to {@link UserSpace}.
+ * Reason: This is a GUI component and it should not do much model manipulation
+ * things. As often in XLogo this was an issue of weak separation of concerns,
+ * leading to bad modifiability, extensibility, changeability, and
+ * maintainability. Better have the Workspace deal with all the workspace
+ * management. For the editor, it's enough to edit text, do syntax highlighting,
+ * and provide the input text to some other specialized class.
+ *
+ * @author Loïc Le Coq, Marko Zivkovic
+ *
+ */
+
+/*
+ * The main class for the Editor windows
+ */
+public class Editor implements ActionListener
+{
+ private JPanel mainPanel = new JPanel();
+ private JPanel menu = new JPanel();
+ private JButton chercher, undo, redo;
+ private JScrollPane scroll;
+ private EditorTextZone textZone;
+ // private ZoneEdition zonedition;
+
+ private Application app;
+ private ReplaceFrame sf;
+ UserConfig uc;
+
+ private KeyListener logoTextAnalyzerTrigger;
+ //private ArrayList<ProcedureErrorMessage> errors = new ArrayList<ProcedureErrorMessage>();
+
+
+ public Editor(Application app)
+ {
+ super();
+ mainPanel.setOpaque(true);
+ this.app = app;
+ uc = WSManager.getUserConfig();
+
+ try
+ {
+ initGui();
+ }
+ catch (Exception e)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.GENERAL_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ERROR_WHILE_CREATING_EDITOR));
+ }
+
+ }
+
+ public String getText()
+ {
+ return textZone.getText();
+ }
+
+ public JComponent getComponent()
+ {
+ return mainPanel;
+ }
+
+ /**
+ * Set the text of this component and displays it. <br>
+ * If the Editor was already open, it will store the old file and then
+ * display the new file.
+ *
+ * @param file
+ */
+ public void setText(String text)
+ {
+ textZone.clearText();
+ setEditorStyledText(text);
+ textZone.requestFocus();
+ }
+
+ /**
+ * @author Marko Zivkovic
+ */
+ public void displayProcedure(String procedureName)
+ {
+ String to = Logo.messages.getString("pour");
+ if (!textZone.find(to + " " + procedureName, false))
+ textZone.find(to + " " + procedureName, true);
+
+ }
+
+ private void initGui() throws Exception
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+
+ // Init All other components
+ scroll = new JScrollPane();
+ if (wc.isSyntaxHighlightingEnabled())
+ {
+ textZone = new EditorTextPane(this);
+ textZone.getTextComponent().addKeyListener(logoTextAnalyzerTrigger);
+ }
+ else
+ textZone = new EditorTextArea(this);
+
+ sf = new ReplaceFrame(app.getFrame(), textZone);
+
+ scroll.setPreferredSize(new Dimension(500, 500));
+ scroll.getViewport().add(textZone.getTextComponent(), null);
+ sf = new ReplaceFrame(app.getFrame(), textZone);
+
+ applyLayout();
+ initToolbar();
+ }
+
+ private void applyLayout()
+ {
+ GroupLayout groupLayout = new GroupLayout(mainPanel);
+ mainPanel.setLayout(groupLayout);
+
+ groupLayout.setAutoCreateContainerGaps(true);
+ groupLayout.setAutoCreateGaps(true);
+
+ groupLayout.setVerticalGroup(
+ groupLayout.createSequentialGroup()
+ .addComponent(menu)
+ .addComponent(scroll));
+
+ groupLayout.setHorizontalGroup(
+ groupLayout.createParallelGroup()
+ .addComponent(menu)
+ .addComponent(scroll));
+
+ FlowLayout flowLayout = new FlowLayout();
+ menu.setLayout(flowLayout);
+ mainPanel.add(menu);
+ mainPanel.add(scroll);
+ }
+
+ /*
+ * Below everything is inherited from XLogo, except for minor changes due to refactoring
+ */
+
+
+ private void initToolbar()
+ {
+ menu.setMaximumSize(new Dimension(Integer.MAX_VALUE, 40));
+
+ chercher = getIconButton("chercher.png","find");
+ undo = getIconButton("undo.png","editor.undo");
+ redo = getIconButton("redo.png","editor.redo");
+
+ undo.setEnabled(false);
+ redo.setEnabled(false);
+
+ menu.add(chercher);
+ menu.add(undo);
+ menu.add(redo);
+
+ }
+
+ private JButton getIconButton(String iconName, String description)
+ {
+ JButton btn = new JButton();
+ if (iconName != null)
+ btn.setIcon(Utils.dimensionne_image(iconName, mainPanel));
+
+ btn.setToolTipText(Logo.messages.getString(description));
+ btn.setActionCommand(Logo.messages.getString(description));
+ btn.addActionListener(this);
+ return btn;
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String cmd = e.getActionCommand();
+ if (cmd.equals(Logo.messages.getString("find")))
+ {
+ if (!sf.isVisible())
+ {
+ sf.setSize(350, 350);
+ sf.setVisible(true);
+ }
+ }
+ // Undo Action
+ else if (cmd.equals(Logo.messages.getString("editor.undo")))
+ {
+ textZone.getUndoManager().undo();
+ updateUndoRedoButtons();
+ }
+ // Redo Action
+ else if (cmd.equals(Logo.messages.getString("editor.redo")))
+ {
+ textZone.getUndoManager().redo();
+ updateUndoRedoButtons();
+ }
+ }
+
+ // Change Syntax Highlighting for the editor
+ public void initStyles(int c_comment, int sty_comment, int c_primitive, int sty_primitive, int c_parenthese,
+ int sty_parenthese, int c_operande, int sty_operande)
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+
+ if (textZone.supportHighlighting())
+ {
+ ((EditorTextPane) textZone).getDsd().initStyles(wc.getCommentColor(), wc.getCommentStyle(),
+ wc.getPrimitiveColor(), wc.getPrimitiveStyle(), wc.getBraceColor(),
+ wc.getBraceStyle(), wc.getOperatorColor(), wc.getOperatorStyle());
+ }
+ }
+
+ // Enable or disable Syntax Highlighting
+ /*
+ * public void setColoration(boolean b){ if (textZone.supportHighlighting())
+ * ((EditorTextPane)textZone).getDsd().setColoration(b); }
+ */
+ public void setEditorFont(Font f)
+ {
+ textZone.setFont(f);
+ }
+
+ /**
+ * Erase all text
+ */
+ public void clearText()
+ {
+ textZone.clearText();
+ }
+
+ /**
+ * Convert the textZone from a JTextArea to a JTextPane To allow Syntax
+ * Highlighting
+ */
+ public void toTextPane()
+ {
+ textZone.getTextComponent().removeKeyListener(logoTextAnalyzerTrigger);
+ scroll.getViewport().removeAll();// .remove(textZone.getTextComponent());
+ String s = textZone.getText();
+ textZone = new EditorTextPane(this);
+ sf = new ReplaceFrame(app.getFrame(), textZone);
+ textZone.ecris(s);
+ textZone.getTextComponent().addKeyListener(logoTextAnalyzerTrigger);
+ scroll.getViewport().add(textZone.getTextComponent());
+ scroll.revalidate();
+ }
+
+ /**
+ * Convert the textZone from a JTextPane to a JTextArea Cause could be that:
+ * - Syntax Highlighting is disabled - Large text to display in the editor
+ */
+ public void toTextArea()
+ {
+ /*
+ * Marko : for large text or disabled highlighting, we also don't want error messages to be produced while typing.
+ *
+ */
+ textZone.getTextComponent().removeKeyListener(logoTextAnalyzerTrigger);
+ String s = textZone.getText();
+ scroll.getViewport().removeAll();// .remove(textZone.getTextComponent());
+ textZone = new EditorTextArea(this);
+ sf = new ReplaceFrame(app.getFrame(), textZone);
+ textZone.ecris(s);
+ scroll.getViewport().add(textZone.getTextComponent());
+ scroll.revalidate();
+ }
+
+ /**
+ * Inserts the text at the current caret position.
+ *
+ * @param txt
+ */
+ public void setEditorStyledText(String txt)
+ {
+ if (txt.length() < 100000)
+ {
+ textZone.ecris(txt);
+ }
+ else
+ {
+ if (textZone instanceof EditorTextPane)
+ {
+ WSManager.getWorkspaceConfig().setSyntaxHighlightingEnabled(false);
+ toTextArea();
+ textZone.ecris(txt);
+ }
+ else
+ textZone.ecris(txt);
+ }
+ }
+
+ /**
+ * append a procedure to the end of the document.
+ *
+ * @param program
+ * @author Marko Zivkovic
+ */
+ public void append(String procedure)
+ {
+ if (procedure.length() < 100000)
+ {
+ textZone.append(procedure);
+ }
+ else
+ {
+ if (textZone instanceof EditorTextPane)
+ {
+ WSManager.getWorkspaceConfig().setSyntaxHighlightingEnabled(false);
+ toTextArea();
+ textZone.append(procedure);
+ }
+ else
+ textZone.append(procedure);
+ }
+ }
+
+ public void focus_textZone()
+ {
+ textZone.requestFocus();
+ }
+
+ public void discardAllEdits()
+ {
+ textZone.getUndoManager().discardAllEdits();
+ updateUndoRedoButtons();
+ }
+
+ protected void updateUndoRedoButtons()
+ {
+ if (textZone.getUndoManager().canRedo())
+ redo.setEnabled(true);
+ else
+ redo.setEnabled(false);
+ if (textZone.getUndoManager().canUndo())
+ undo.setEnabled(true);
+ else
+ undo.setEnabled(false);
+ }
+
+
+}
diff --git a/logo/src/xlogo/gui/EditorTextArea.java b/logo/src/xlogo/gui/EditorTextArea.java
new file mode 100644
index 0000000..387b30f
--- /dev/null
+++ b/logo/src/xlogo/gui/EditorTextArea.java
@@ -0,0 +1,58 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.JTextArea;
+
+public class EditorTextArea extends EditorTextZone {
+ EditorTextArea(Editor editor){
+ super(editor);
+ jtc=new JTextArea();
+ initGui();
+ }
+ public void setActive(boolean b){}
+
+ protected void ecris(String s){
+ int deb=jtc.getCaretPosition();
+ ((JTextArea)jtc).insert(s,deb);
+ jtc.setCaretPosition(deb+s.length());
+ }
+
+ /**
+ * Added 21.6.2013
+ * <p> this method is used to append a program to the end of the editor.
+ * It is used for the Logo command "define".
+ * @author Marko Zivkovic
+ */
+ @Override
+ protected void append(String program) {
+ int deb= jtc.getText().length();
+ ((JTextArea)jtc).insert(program,deb);
+ }
+}
diff --git a/logo/src/xlogo/gui/EditorTextPane.java b/logo/src/xlogo/gui/EditorTextPane.java
new file mode 100644
index 0000000..fd069fe
--- /dev/null
+++ b/logo/src/xlogo/gui/EditorTextPane.java
@@ -0,0 +1,83 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.text.BadLocationException;
+import xlogo.StyledDocument.DocumentLogo;
+
+
+public class EditorTextPane extends EditorTextZone{
+ private DocumentLogo dsd;
+ EditorTextPane(Editor editor){
+ super(editor);
+ dsd=new DocumentLogo();
+ jtc=new ZoneEdition(this);
+ initGui();
+ initHighlight();
+ }
+ protected void initHighlight(){
+ jtc.setDocument(dsd);
+ dsd.addUndoableEditListener(new MyUndoableEditListener());
+ }
+ protected DocumentLogo getDsd(){
+ return dsd;
+ }
+ protected void ecris(String mot){
+ try{
+ int deb=jtc.getCaretPosition();
+ dsd.insertString(deb,mot,null);
+ jtc.setCaretPosition(deb+mot.length());
+ }
+ catch(BadLocationException e){}
+ }
+ /**
+ * Added 21.6.2013
+ * <p> this method is used to append a program to the end of the editor.
+ * It is used for the Logo command "define".
+ * @author Marko Zivkovic
+ */
+ @Override
+ protected void append(String program) {
+ try{
+ int deb= dsd.getLength();
+ dsd.insertString(deb,program,null);
+ }
+ catch(BadLocationException e){}
+
+ }
+
+ public void setActive(boolean b){
+ ((ZoneEdition)jtc).setActive(b);
+ }
+
+ protected boolean supportHighlighting(){
+ return true;
+ }
+
+}
diff --git a/logo/src/xlogo/gui/EditorTextZone.java b/logo/src/xlogo/gui/EditorTextZone.java
new file mode 100644
index 0000000..b14749b
--- /dev/null
+++ b/logo/src/xlogo/gui/EditorTextZone.java
@@ -0,0 +1,303 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.event.UndoableEditListener;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.undo.UndoManager;
+import javax.swing.undo.UndoableEdit;
+import xlogo.Popup;
+/**
+ *
+ * @author loic
+ * This class is the generic component to display the text in the editor
+ * If there are too many characters, it is a JTextArea <br>
+ * Else it's a JtextPane that allows Syntax Highlighting
+ */
+public abstract class EditorTextZone implements Searchable, Printable {
+ private Editor editor;
+ private StringBuffer text;
+ protected JTextComponent jtc;
+ private Popup jpop;
+
+ private boolean highlightWasSet = false;
+
+ // When printing the text area, this stack stores each page
+ private Stack<String> pages=null;
+ // To remember undoable edits
+ private UndoManager undoManager;
+ private int startOffset,endOffset;
+ /**
+ * Default constructor for this generic constructor
+ *
+ * @param editor The parent editor
+ */
+ EditorTextZone(Editor editor){
+ this.editor=editor;
+
+ }
+ protected void initGui(){
+ // Adds the JPopup Menu
+ jpop=new Popup(editor,jtc);
+ MouseListener popupListener = new PopupListener();
+ jtc.addMouseListener(popupListener);
+
+ jtc.addMouseListener(new MouseListener(){
+
+ @Override
+ public void mouseReleased(MouseEvent e) { }
+
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) { }
+
+ @Override
+ public void mouseEntered(MouseEvent e) { }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (highlightWasSet)
+ {
+ removeHighlight();
+ highlightWasSet = false;
+ }
+ }
+ });
+ undoManager=new UndoManager();
+ }
+ /**
+ * Erase all text
+ */
+ protected void clearText(){
+ jtc.setText("");
+ }
+
+ public boolean find(String searchText,boolean forward){
+ try
+ {
+ int index;
+ String element = searchText.toLowerCase();
+ text = new StringBuffer(jtc.getText().toLowerCase());
+ // Find forward
+ if (forward)
+ index = text.indexOf(element, jtc.getCaretPosition());
+ else
+ index = text.lastIndexOf(element, jtc.getCaretPosition());
+ if (index == -1)
+ {
+ startOffset = 0;
+ endOffset = 0;
+ return false;
+ }
+ else
+ {
+ jtc.getHighlighter().removeAllHighlights();
+ startOffset = index;
+ endOffset = index + element.length();
+ jtc.getHighlighter().addHighlight(startOffset, endOffset, DefaultHighlighter.DefaultPainter);
+ if (forward)
+ jtc.setCaretPosition(index + element.length());
+ else if (index > 1)
+ jtc.setCaretPosition(index - 1);
+
+ highlightWasSet = true;
+ return true;
+ }
+ }
+ catch(NullPointerException e){} // If the combo is empty
+ catch(BadLocationException e){}
+ return false;
+ }
+ public void replace(String element, boolean forward){
+ text.delete(startOffset, endOffset);
+ try{
+ text.insert(startOffset,element );
+
+ }
+ catch(NullPointerException err){}
+ jtc.setText(text.toString());
+ if (forward) jtc.setCaretPosition(endOffset);
+ else if (startOffset>1) jtc.setCaretPosition(startOffset-1);
+
+ }
+ public void replaceAll(String element, String substitute){
+
+ try {
+ String string=jtc.getText().toString();
+ string=string.replaceAll(element, substitute);
+ jtc.setText(string);
+ }
+ catch(NullPointerException e2){}
+
+ }
+ public void removeHighlight(){
+ jtc.getHighlighter().removeAllHighlights();
+ }
+
+ class MyUndoableEditListener implements UndoableEditListener{
+ public void undoableEditHappened(UndoableEditEvent e){
+ UndoableEdit edit = e.getEdit();
+ // Include this method to ignore syntax changes
+ if (edit instanceof AbstractDocument.DefaultDocumentEvent &&
+ ((AbstractDocument.DefaultDocumentEvent)edit).getType() ==
+ AbstractDocument.DefaultDocumentEvent.EventType.CHANGE) {
+ return;
+ }
+ // Remember the edit
+ undoManager.addEdit(edit);
+// System.out.println(e.getEdit().getPresentationName());
+ editor.updateUndoRedoButtons();
+ }
+}
+ protected UndoManager getUndoManager(){
+ return undoManager;
+ }
+ protected String getText(){
+ return jtc.getText();
+ }
+ public void requestFocus(){
+ jtc.requestFocus();
+ }
+ protected JTextComponent getTextComponent(){
+ return jtc;
+ }
+ protected boolean supportHighlighting(){
+ return false;
+ }
+
+ protected void setFont(Font f){
+ jtc.setFont(f);
+ }
+ protected Font getFont(){
+ return jtc.getFont();
+ }
+ // To print the text Area
+
+ public int print(Graphics g,PageFormat pf, int pi) throws PrinterException{
+ if(pi<pages.size()){
+ jtc.setText(pages.get(pi));
+ g.translate((int)pf.getImageableX(),(int)pf.getImageableY());
+ jtc.paint(g);
+ return(Printable.PAGE_EXISTS);
+ }
+ else return Printable.NO_SUCH_PAGE;
+ }
+ protected void actionPrint(){
+ Font font=jtc.getFont();
+ String txt=jtc.getText();
+ jtc.setFont(new Font(font.getFontName(),Font.PLAIN,10));
+ jtc.setText(txt);
+ PrinterJob job = PrinterJob.getPrinterJob();
+ job.setPrintable(this,job.defaultPage());
+ double h_imp=job.defaultPage().getImageableHeight();
+ java.awt.FontMetrics fm = jtc.getFontMetrics(jtc.getFont());
+ pages=new Stack<String>();
+ StringTokenizer st = new StringTokenizer(txt, "\n");
+ String page="";
+ // System.out.println("hauteur "+fm.getHeight()+" "+h_imp);
+ int compteur=0;
+ while (st.hasMoreTokens()) {
+ String element = st.nextToken();
+ compteur+=fm.getHeight();
+ if (compteur>h_imp) {
+ pages.push(page);
+ page = element;
+ compteur=fm.getHeight();
+ }
+ else page+= element+"\n";
+ }
+ if (!page.equals("")) pages.push(page);
+ if (job.printDialog()) {
+ try {
+ job.print();
+ }
+ catch (PrinterException ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+ font=jtc.getFont();
+ jtc.setFont(new Font(font.getFontName(),Font.PLAIN,12));
+ jtc.setText(txt);
+ }
+ // Edit Actions
+ protected void copy(){
+ jtc.copy();
+ }
+ protected void cut(){
+ jtc.cut();
+ }
+ protected void paste(){
+ jtc.paste();
+ }
+
+ // setText Method for Text Zone
+ protected void setText(String s){
+ jtc.setText(s);
+ }
+ protected abstract void ecris(String s);
+
+ protected abstract void append(String s);
+
+ abstract void setActive(boolean b);
+ class PopupListener extends MouseAdapter {
+
+ public void mousePressed(MouseEvent e) {
+ maybeShowPopup(e);
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ maybeShowPopup(e);
+ }
+
+ private void maybeShowPopup(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ jpop.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+ }
+}
diff --git a/logo/src/xlogo/gui/HistoryPanel.java b/logo/src/xlogo/gui/HistoryPanel.java
new file mode 100644
index 0000000..d1c17ba
--- /dev/null
+++ b/logo/src/xlogo/gui/HistoryPanel.java
@@ -0,0 +1,416 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.*;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
+import javax.swing.text.rtf.RTFEditorKit;
+import javax.swing.text.*;
+
+import java.io.*;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+import xlogo.utils.ExtensionFichier;
+import xlogo.StyledDocument.DocumentLogoHistorique;
+import xlogo.Application;
+import xlogo.kernel.DrawPanel;
+import xlogo.kernel.LogoError;
+import xlogo.messages.async.AsyncMediumAdapter;
+import xlogo.messages.async.AsyncMessage;
+import xlogo.messages.async.AsyncMessenger;
+import xlogo.messages.async.history.HistoryMessenger;
+import xlogo.messages.async.history.HistoryWriter;
+import xlogo.Logo;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+public class HistoryPanel extends JPanel implements HistoryWriter
+{
+
+ private static final long serialVersionUID = 1L;
+ // numéro identifiant la police de
+ // l'historique avec "ecris"
+ public static int fontPrint = GlobalConfig.police_id(WSManager.getWorkspaceConfig().getFont()); // TODO
+ // how
+ // to
+ // remove
+ // fontPrint?
+ // Error@static?
+ private ImageIcon ianimation = Utils.dimensionne_image("animation.png", this);
+ private JLabel label_animation = new JLabel(ianimation);
+ private MouseAdapter mouseAdapt;
+ private Color couleur_texte = Color.BLUE;
+ private int taille_texte = 12;
+ private JPanel jPanel1 = new JPanel();
+ private JScrollPane jScrollPane1 = new JScrollPane();
+ private Historique historique = new Historique();
+ private DocumentLogoHistorique dsd;
+ private BorderLayout borderLayout1 = new BorderLayout();
+ private Application cadre;
+
+ public HistoryPanel()
+ {
+ }
+
+ public HistoryPanel(Application cadre) {
+ historique.setFont(WSManager.getWorkspaceConfig().getFont());
+ this.cadre=cadre;
+ try {
+ jbInit();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ dsd=new DocumentLogoHistorique();
+ historique.setDocument(dsd);
+
+ /*
+ * Added by Marko Zivkovic to decouple HistoryPanel from the rest of the old XLogo classes
+ */
+ HistoryMessenger.getInstance().setMedium(new AsyncMediumAdapter<AsyncMessage<HistoryWriter>, HistoryWriter>(){
+
+ public boolean isReady()
+ {
+ return getThis().historique.isDisplayable();
+ }
+
+ public HistoryWriter getMedium()
+ {
+ return getThis();
+ }
+
+ public void addMediumReadyListener(final AsyncMessenger messenger)
+ {
+ historique.addAncestorListener(new AncestorListener(){
+ public void ancestorRemoved(AncestorEvent event)
+ {
+ maybeMediumReadyEvent();
+ }
+ public void ancestorMoved(AncestorEvent event)
+ {
+ maybeMediumReadyEvent();
+ }
+ public void ancestorAdded(AncestorEvent event)
+ {
+ maybeMediumReadyEvent();
+ }
+
+ private void maybeMediumReadyEvent()
+ {
+ if (isDisplayable())
+ messenger.onMediumReady();
+ }
+
+ });
+ }
+ });
+ }
+
+ private HistoryPanel getThis()
+ {
+ return this;
+ }
+
+ public Color getCouleurtexte()
+ {
+ return couleur_texte;
+ }
+
+ public int police()
+ {
+ return taille_texte;
+ }
+
+ public void vide_texte()
+ {
+ historique.setText("");
+ }
+
+ /**
+ * Made private by Marko Zivkovic.
+ * This functionality is now publicly available through the implementation of {@link HistoryWriter#writeMessage(String, String)}
+ * @param sty
+ * @param texte
+ */
+ private void ecris(String sty, String texte)
+ {
+ try
+ {
+ int longueur = historique.getDocument().getLength();
+ if (texte.length() > 32000)
+ throw new LogoError( Logo.messages.getString("chaine_trop_longue"));
+ if (longueur + texte.length() < 65000)
+ {
+ try
+ {
+ dsd.setStyle(sty);
+ dsd.insertString(dsd.getLength(), texte, null);
+ historique.setCaretPosition(dsd.getLength());
+ }
+ catch (BadLocationException e)
+ {}
+ }
+ else
+ {
+ vide_texte();
+ }
+ }
+ catch (LogoError e2)
+ {}
+ }
+
+ private void jbInit() throws Exception
+ {
+
+ this.setLayout(borderLayout1);
+ this.setMinimumSize(new Dimension(4, 4));
+ this.setPreferredSize(new Dimension(600, 40));
+ historique.setForeground(Color.black);
+ historique.setEditable(false);
+ this.add(jPanel1, BorderLayout.EAST);
+ label_animation.setToolTipText(Logo.messages.getString("animation_active"));
+ this.add(jScrollPane1, BorderLayout.CENTER);
+ jScrollPane1.getViewport().add(historique, null);
+ }
+
+ public void active_animation()
+ {
+ add(label_animation, BorderLayout.WEST);
+ DrawPanel.classicMode = DrawPanel.MODE_ANIMATION;
+ mouseAdapt = new MouseAdapter(){
+ public void mouseClicked(MouseEvent e)
+ {
+ stop_animation();
+ cadre.getDrawPanel().repaint();
+ }
+ };
+ label_animation.addMouseListener(mouseAdapt);
+ validate();
+ }
+
+ public void stop_animation()
+ {
+ DrawPanel.classicMode = DrawPanel.MODE_CLASSIC;
+ remove(label_animation);
+ label_animation.removeMouseListener(mouseAdapt);
+ validate();
+ }
+
+ // Change Syntax Highlighting for the editor
+ public void initStyles(int c_comment, int sty_comment, int c_primitive, int sty_primitive, int c_parenthese,
+ int sty_parenthese, int c_operande, int sty_operande)
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ dsd.initStyles(wc.getCommentColor(), wc.getCommentStyle(), wc.getPrimitiveColor(), wc.getPrimitiveStyle(),
+ wc.getBraceColor(), wc.getBraceStyle(), wc.getOperatorColor(), wc.getOperatorStyle());
+ }
+
+ // Enable or disable Syntax Highlighting
+ public void setColoration(boolean b)
+ {
+ dsd.setColoration(b);
+ }
+
+ public void changeFont(Font f)
+ {
+ historique.setFont(f);
+ }
+
+ public void updateText()
+ {
+ historique.setText();
+ }
+
+ public DocumentLogoHistorique getDsd()
+ {
+ return dsd;
+ }
+
+ public StyledDocument sd_Historique()
+ {
+ return historique.getStyledDocument();
+ }
+
+ class Historique extends JTextPane implements ActionListener
+ {
+ private static final long serialVersionUID = 1L;
+ private JPopupMenu popup = new JPopupMenu();
+ private JMenuItem jpopcopier = new JMenuItem();
+ private JMenuItem jpopselect = new JMenuItem();
+ private JMenuItem jpopsave = new JMenuItem();
+
+ Historique()
+ {
+ // this.setBackground(new Color(255,255,220));
+ popup.add(jpopcopier);
+ popup.add(jpopselect);
+ popup.add(jpopsave);
+ jpopselect.addActionListener(this);
+ jpopcopier.addActionListener(this);
+ jpopsave.addActionListener(this);
+ setText();
+ MouseListener popupListener = new MouseAdapter(){
+ public void mouseClicked(MouseEvent e)
+ {
+ if (e.getButton() == 1)
+ {
+ int i = getCaretPosition();
+ int borneinf = borne(i, -1);
+ int bornesup = borne(i, 1);
+ if (borneinf == 0)
+ borneinf = borneinf - 1;
+ select(borneinf + 1, bornesup - 2);
+ cadre.setCommandText(getSelectedText());
+ // historique.setCaretPosition(historique.getDocument().getLength());
+ cadre.focus_Commande();
+ }
+ }
+
+ public void mouseReleased(MouseEvent e)
+ {
+ maybeShowPopup(e);
+ cadre.focus_Commande();
+ }
+
+ public void mousePressed(MouseEvent e)
+ {
+ maybeShowPopup(e);
+ }
+
+ private void maybeShowPopup(MouseEvent e)
+ {
+ if (e.isPopupTrigger())
+ {
+ popup.show(e.getComponent(), e.getX(), e.getY());
+ }
+ }
+ };
+ addMouseListener(popupListener);
+ }
+
+ int borne(int i, int increment)
+ {
+ boolean continuer = true;
+ while (continuer && i != 0)
+ {
+ select(i - 1, i);
+ String t = historique.getSelectedText();
+ if (t.equals("\n"))
+ {
+ continuer = false;
+ }
+ i = i + increment;
+ }
+ return (i);
+ }
+
+ void setText()
+ {
+ jpopselect.setText(Logo.messages.getString("menu.edition.selectall"));
+ jpopcopier.setText(Logo.messages.getString("menu.edition.copy"));
+ jpopsave.setText(Logo.messages.getString("menu.file.textzone.rtf"));
+ jpopselect.setActionCommand(Logo.messages.getString("menu.edition.selectall"));
+ jpopcopier.setActionCommand(Logo.messages.getString("menu.edition.copy"));
+ jpopsave.setActionCommand(Logo.messages.getString("menu.file.textzone.rtf"));
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ UserConfig uc = WSManager.getUserConfig();
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ String cmd = e.getActionCommand();
+ if (Logo.messages.getString("menu.edition.copy").equals(cmd))
+ { // Copier
+ copy();
+ }
+ else if (Logo.messages.getString("menu.edition.selectall").equals(cmd))
+ { // Selectionner tout
+ requestFocus();
+ selectAll();
+ cadre.focus_Commande();
+ }
+ else if (cmd.equals(Logo.messages.getString("menu.file.textzone.rtf")))
+ {
+ RTFEditorKit myRTFEditorKit = new RTFEditorKit();
+ StyledDocument myStyledDocument = getStyledDocument();
+ try
+ {
+ JFileChooser jf = new JFileChooser(Utils.SortieTexte(uc.getDefaultFolder()));
+ String[] ext = { ".rtf" };
+ jf.addChoosableFileFilter(new ExtensionFichier(Logo.messages.getString("fichiers_rtf"), ext));
+ Utils.recursivelySetFonts(jf, wc.getFont());
+ int retval = jf.showDialog(cadre.getFrame(), Logo.messages.getString("menu.file.save"));
+ if (retval == JFileChooser.APPROVE_OPTION)
+ {
+ String path = jf.getSelectedFile().getPath();
+ String path2 = path.toLowerCase(); // on garde la casse
+ // du path pour les
+ // systèmes
+ // d'exploitation
+ // faisant la
+ // différence
+ if (!path2.endsWith(".rtf"))
+ path += ".rtf";
+ FileOutputStream myFileOutputStream = new FileOutputStream(path);
+ myRTFEditorKit.write(myFileOutputStream, myStyledDocument, 0, myStyledDocument.getLength() - 1);
+ myFileOutputStream.close();
+
+ }
+ }
+ catch (FileNotFoundException e1)
+ {}
+ catch (IOException e2)
+ {}
+ catch (BadLocationException e3)
+ {}
+ catch (NullPointerException e4)
+ {}
+ }
+ }
+ }
+
+ public void writeMessage(String messageType, String message)
+ {
+ ecris(messageType, message);
+ }
+}
diff --git a/logo/src/xlogo/gui/Lis.java b/logo/src/xlogo/gui/Lis.java
new file mode 100644
index 0000000..bd6c3ff
--- /dev/null
+++ b/logo/src/xlogo/gui/Lis.java
@@ -0,0 +1,78 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+// Frame for the primitive "read"
+
+
+import javax.swing.JFrame;
+import javax.swing.JTextField;
+import java.awt.HeadlessException;
+import javax.swing.JButton;
+import java.awt.event.*;
+import java.awt.*;
+import xlogo.utils.Utils;
+import xlogo.Logo;
+public class Lis extends JFrame implements ActionListener{
+ private static final long serialVersionUID = 1L;
+ private JTextField texte=new JTextField();
+ private JButton ok=new JButton(Logo.messages.getString("pref.ok"));
+ public Lis() throws HeadlessException {
+ }
+ public Lis(String titre,int longueur){
+ setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png")));
+ getContentPane().setLayout(new BorderLayout());
+ this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ getContentPane().add(ok,BorderLayout.EAST);
+ getContentPane().add(texte,BorderLayout.CENTER);
+ texte.setPreferredSize(new Dimension(longueur,50));
+ ok.setPreferredSize(new Dimension(75,50));
+ texte.addActionListener(this);
+ ok.addActionListener(this);
+ pack();
+ setTitle(titre);
+ Dimension d=Toolkit.getDefaultToolkit().getScreenSize().getSize();
+ int x=(int)(d.getWidth()/2-longueur/2);
+ int y=(int)(d.getHeight()/2-25);
+ setLocation(x,y);
+ setVisible(true);
+ texte.requestFocus();
+ }
+ public void actionPerformed(ActionEvent e){
+ setVisible(false);
+ }
+ public String getText(){
+ return texte.getText();
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/gui/MyTextAreaDialog.java b/logo/src/xlogo/gui/MyTextAreaDialog.java
new file mode 100644
index 0000000..fb689ce
--- /dev/null
+++ b/logo/src/xlogo/gui/MyTextAreaDialog.java
@@ -0,0 +1,59 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import java.awt.Color;
+
+import javax.swing.JTextArea;
+
+import xlogo.StyledDocument.DocumentLogoHistorique;
+import xlogo.storage.WSManager;
+/**
+ * This class creates the common yellow JTextArea for all dialog box.
+ * @author loic
+ *
+ */
+public class MyTextAreaDialog extends JTextArea {
+
+ private static final long serialVersionUID = 1L;
+ public MyTextAreaDialog(String message){
+ setText(message);
+ setEditable(false);
+ setBackground(new Color(255,255,177));
+ setFont(WSManager.getWorkspaceConfig().getFont());
+ }
+
+ public MyTextAreaDialog(String message, DocumentLogoHistorique dsd){
+ setFont(dsd.getFont());
+ setText(message);
+ setEditable(false);
+ setBackground(new Color(255,255,177));
+ setFont(WSManager.getWorkspaceConfig().getFont());
+ }
+}
diff --git a/logo/src/xlogo/gui/MyToolBar.java b/logo/src/xlogo/gui/MyToolBar.java
new file mode 100644
index 0000000..bb7afa3
--- /dev/null
+++ b/logo/src/xlogo/gui/MyToolBar.java
@@ -0,0 +1,126 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JToolBar;
+import xlogo.MenuListener;
+import xlogo.utils.Utils;
+
+public class MyToolBar extends JToolBar {
+ private static final long serialVersionUID = 1L;
+ private MenuListener menulistener;
+ private ImageIcon izoomin=Utils.dimensionne_image("zoomin.png",this);
+ private ImageIcon izoomout=Utils.dimensionne_image("zoomout.png",this);
+ private ImageIcon icopier=Utils.dimensionne_image("editcopy.png",this);
+ private ImageIcon icoller=Utils.dimensionne_image("editpaste.png",this);
+ private ImageIcon icouper=Utils.dimensionne_image("editcut.png",this);
+ private ImageIcon iplay=Utils.dimensionne_image("play.png",this);
+ //private ImageIcon iturtleProp=new ImageIcon(Utils.dimensionne_image("turtleProp.png", this));
+ private JButton zoomin=new JButton(izoomin);
+ private JButton zoomout=new JButton(izoomout);
+ private JButton copier=new JButton(icopier);
+ private JButton coller=new JButton(icoller);
+ private JButton couper=new JButton(icouper);
+ private JButton play=new JButton(iplay);
+ //private JButton turtleProp=new JButton(iturtleProp);
+
+
+ public MyToolBar(MenuListener menulistener){
+ super(JToolBar.VERTICAL);
+ this.menulistener=menulistener;
+ initGui();
+ }
+
+ private void initGui(){
+ zoomin.addActionListener(menulistener);
+ zoomin.setActionCommand(MenuListener.ZOOMIN);
+ zoomout.addActionListener(menulistener);
+ zoomout.setActionCommand(MenuListener.ZOOMOUT);
+ copier.addActionListener(menulistener);
+ copier.setActionCommand(MenuListener.EDIT_COPY);
+ couper.addActionListener(menulistener);
+ couper.setActionCommand(MenuListener.EDIT_CUT);
+ coller.addActionListener(menulistener);
+ coller.setActionCommand(MenuListener.EDIT_PASTE);
+ play.addActionListener(menulistener);
+ play.setActionCommand(MenuListener.PLAY);
+/* slider= new JSlider(JSlider.VERTICAL);
+ slider.setValue(slider.getMaximum()-Config.turtleSpeed);
+ //Create the label table
+ Hashtable labelTable = new Hashtable();
+ labelTable.put( new Integer( 0 ), new JLabel("Slow") );
+ labelTable.put( new Integer( 100 ), new JLabel("Fast") );
+ slider.setLabelTable( labelTable );
+ slider.setPaintLabels(true);
+ /* slider.setMajorTickSpacing(10);
+ slider.setMinorTickSpacing(5);
+ slider.setPaintTicks(true);
+
+ slider.setSnapToTicks(true);
+ slider.addChangeListener(new ChangeListener(){
+ public void stateChanged(ChangeEvent e) {
+ JSlider source = (JSlider)e.getSource();
+ int value=source.getValue();
+ Config.turtleSpeed=source.getMaximum()-value;
+ }
+ });
+ int width=Toolkit.getDefaultToolkit().getScreenSize().width;
+ width=32*width/1024;
+ slider.setMinimumSize(new java.awt.Dimension(width,200));
+ slider.setMaximumSize(new java.awt.Dimension(width,200));
+ */
+ add(zoomin);
+ addSeparator();
+ add(zoomout);
+ addSeparator();
+ add(couper);
+ addSeparator();
+ add(copier);
+ addSeparator();
+ add(coller);
+ addSeparator();
+ add(play);
+ addSeparator();
+// add(slider);
+ }
+ public void enabledPlay(boolean b){
+ play.setEnabled(b);
+ }
+ /**
+ * Enables or disables the zoom buttons
+ * @param b The boolean
+ */
+ public void setZoomEnabled(boolean b){
+ zoomin.setEnabled(b);
+ zoomout.setEnabled(b);
+// repaint();
+ }
+}
diff --git a/logo/src/xlogo/gui/ReplaceFrame.java b/logo/src/xlogo/gui/ReplaceFrame.java
new file mode 100644
index 0000000..f51229f
--- /dev/null
+++ b/logo/src/xlogo/gui/ReplaceFrame.java
@@ -0,0 +1,265 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.JDialog;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JRadioButton;
+import javax.swing.JPanel;
+import javax.swing.BoxLayout;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.JLabel;
+import javax.swing.ButtonGroup;
+import javax.swing.BorderFactory;
+import javax.swing.border.TitledBorder;
+import javax.swing.text.Highlighter;
+
+import java.awt.event.*;
+
+import javax.swing.JFrame;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+public class ReplaceFrame extends JDialog implements ActionListener{
+ private static final long serialVersionUID = 1L;
+ private final String FIND="find";
+ private final String REPLACE="replace";
+ private final String REPLACEALL="replaceall";
+ private final String FIND_REPLACE="find_replace";
+
+ private JButton find,replace,findReplace,replaceAll;
+ private JRadioButton backward, forward;
+ private JPanel buttonPanel;
+ private JComboBox comboFind,comboReplace;
+ private ButtonGroup bg;
+ private JLabel labelFind, labelReplace,labelResult;
+ private Searchable jtc;
+ Highlighter.HighlightPainter cyanPainter;
+
+ public ReplaceFrame(JFrame jf,Searchable jtc){
+ super(jf);
+ this.jtc=jtc;
+ initGui();
+ }
+
+ public void actionPerformed(ActionEvent e){
+ String cmd=e.getActionCommand();
+ if (cmd.equals(FIND)){
+ find();
+
+ }
+ else if (cmd.equals(REPLACE)){
+ replace();
+ }
+ else if (cmd.equals(FIND_REPLACE)){
+ replace();
+ find();
+ }
+ else if (cmd.equals(REPLACEALL)){
+ replaceAll();
+ }
+
+ }
+ private void replaceAll(){
+ String substitute="";
+ try {
+ substitute=comboReplace.getSelectedItem().toString();
+ addCombo(substitute,comboReplace);
+ }
+ catch(NullPointerException e2){}
+ String element=comboFind.getSelectedItem().toString();
+ addCombo(element,comboFind);
+ jtc.replaceAll(element, substitute);
+ replace.setEnabled(false);
+ findReplace.setEnabled(false);
+
+ }
+ private void replace(){
+ String element=comboReplace.getSelectedItem().toString();
+ addCombo(element,comboReplace);
+ jtc.replace(element,forward.isSelected());
+ replace.setEnabled(false);
+ findReplace.setEnabled(false);
+ }
+ private void find(){
+ String element=comboFind.getSelectedItem().toString();
+ // Add the element to the combobox
+ addCombo(element,comboFind);
+ boolean b=jtc.find(element,forward.isSelected());
+ if (b) {
+ replace.setEnabled(true);
+ findReplace.setEnabled(true);
+ // Found
+ labelResult.setText("");
+ }
+ else {
+ replace.setEnabled(false);
+ findReplace.setEnabled(false);
+ // Not found
+ labelResult.setText(Logo.messages.getString("string_not_found"));
+ }
+ }
+ protected void processWindowEvent(WindowEvent e){
+ super.processWindowEvent(e);
+ if (e.getID()==WindowEvent.WINDOW_CLOSING){
+ jtc.removeHighlight();
+ }
+ }
+ private void addCombo(String element,JComboBox combo){
+ boolean b=false;
+ for (int i=0;i<combo.getItemCount();i++){
+ if (combo.getItemAt(i).equals(element)) {
+ b=true;
+ break;
+ }
+ }
+ if (!b){
+ combo.insertItemAt(element, 0);
+ int n=combo.getItemCount();
+ if (n>10){
+ combo.removeItemAt(n-1);
+ }
+ }
+ }
+ private void setFont(){
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ find.setFont(font);
+ replace.setFont(font);
+ findReplace.setFont(font);
+ replaceAll.setFont(font);
+ backward.setFont(font);
+ forward.setFont(font);
+ comboFind.setFont(font);
+ comboReplace.setFont(font);
+ labelFind.setFont(font);
+ labelReplace.setFont(font);
+ labelResult.setFont(font);
+
+
+ }
+ protected void setText(){
+ setFont();
+ backward=new JRadioButton(Logo.messages.getString("backward"));
+ forward=new JRadioButton(Logo.messages.getString("forward"));
+ TitledBorder tb=BorderFactory.createTitledBorder(Logo.messages.getString("direction"));
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ buttonPanel.setBorder(tb);
+ find=new JButton(Logo.messages.getString("find"));
+ replace=new JButton(Logo.messages.getString("replace"));
+ findReplace=new JButton(Logo.messages.getString("find_replace"));
+ replaceAll=new JButton(Logo.messages.getString("replaceall"));
+ labelFind=new JLabel(Logo.messages.getString("find")+" :");
+ labelReplace=new JLabel(Logo.messages.getString("replacewith"));
+ setTitle(Logo.messages.getString("find_replace"));
+ }
+ private void initGui(){
+
+ setTitle(Logo.messages.getString("find_replace"));
+ // Init the RadioButton for the direction search
+ backward=new JRadioButton(Logo.messages.getString("backward"));
+ forward=new JRadioButton(Logo.messages.getString("forward"));
+ forward.setSelected(true);
+ bg=new ButtonGroup();
+ bg.add(forward);
+ bg.add(backward);
+ buttonPanel=new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.Y_AXIS));
+ buttonPanel.add(forward);
+ buttonPanel.add(backward);
+ TitledBorder tb=BorderFactory.createTitledBorder(Logo.messages.getString("direction"));
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ buttonPanel.setBorder(tb);
+ // Init Buttons
+ find=new JButton(Logo.messages.getString("find"));
+ replace=new JButton(Logo.messages.getString("replace"));
+ findReplace=new JButton(Logo.messages.getString("find_replace"));
+ replaceAll=new JButton(Logo.messages.getString("replaceall"));
+ findReplace.setEnabled(false);
+ replace.setEnabled(false);
+ find.addActionListener(this);
+ findReplace.addActionListener(this);
+ replaceAll.addActionListener(this);
+ replace.addActionListener(this);
+ find.setActionCommand(FIND);
+ replace.setActionCommand(REPLACE);
+ findReplace.setActionCommand(FIND_REPLACE);
+ replaceAll.setActionCommand(REPLACEALL);
+
+ // Init JLabel and JCombobox
+ labelFind=new JLabel(Logo.messages.getString("find")+" :");
+ labelReplace=new JLabel(Logo.messages.getString("replacewith"));
+ labelResult=new JLabel();
+
+ comboFind=new JComboBox();
+ comboReplace=new JComboBox();
+ comboFind.setEditable(true);
+ comboReplace.setEditable(true);
+ setFont();
+ getContentPane().setLayout(new GridBagLayout());
+
+ // Draw all
+
+ getContentPane().add(labelFind, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(comboFind, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(labelReplace, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(comboReplace, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(buttonPanel, new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(labelResult, new GridBagConstraints(1, 2, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(find, new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(replace, new GridBagConstraints(1, 3, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(findReplace, new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(replaceAll, new GridBagConstraints(1, 4, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ }
+}
diff --git a/logo/src/xlogo/gui/SearchFrame.java b/logo/src/xlogo/gui/SearchFrame.java
new file mode 100644
index 0000000..2a177e8
--- /dev/null
+++ b/logo/src/xlogo/gui/SearchFrame.java
@@ -0,0 +1,189 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.JDialog;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JRadioButton;
+import javax.swing.JPanel;
+import javax.swing.BoxLayout;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.JLabel;
+import javax.swing.ButtonGroup;
+import javax.swing.BorderFactory;
+import javax.swing.border.TitledBorder;
+import javax.swing.text.Highlighter;
+
+import java.awt.event.*;
+
+import javax.swing.JFrame;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+public class SearchFrame extends JDialog implements ActionListener{
+ private static final long serialVersionUID = 1L;
+ private final String FIND="find";
+
+ private JButton find;
+ private JRadioButton backward, forward;
+ private JPanel buttonPanel;
+ private JComboBox comboFind;
+ private ButtonGroup bg;
+ private JLabel labelFind,labelResult;
+ private Searchable jtc;
+ Highlighter.HighlightPainter cyanPainter;
+
+ public SearchFrame(JFrame jf,Searchable jtc){
+ super(jf);
+ this.jtc=jtc;
+ initGui();
+ }
+
+ public void actionPerformed(ActionEvent e){
+ String cmd=e.getActionCommand();
+ if (cmd.equals(FIND)){
+ find();
+
+ }
+ }
+ private void find(){
+ String element=comboFind.getSelectedItem().toString();
+ // Add the element to the combobox
+ addCombo(element,comboFind);
+ boolean b=jtc.find(element,forward.isSelected());
+ if (b) {
+ // Found
+ labelResult.setText("");
+ }
+ else {
+ // Not found
+ labelResult.setText(Logo.messages.getString("string_not_found"));
+ }
+ }
+ protected void processWindowEvent(WindowEvent e){
+ super.processWindowEvent(e);
+ if (e.getID()==WindowEvent.WINDOW_CLOSING){
+ jtc.removeHighlight();
+ }
+ }
+ private void addCombo(String element,JComboBox combo){
+ boolean b=false;
+ for (int i=0;i<combo.getItemCount();i++){
+ if (combo.getItemAt(i).equals(element)) {
+ b=true;
+ break;
+ }
+ }
+ if (!b){
+ combo.insertItemAt(element, 0);
+ int n=combo.getItemCount();
+ if (n>10){
+ combo.removeItemAt(n-1);
+ }
+ }
+ }
+ protected void setText(){
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ backward.setFont(font);
+ forward.setFont(font);
+ find.setFont(font);
+ labelFind.setFont(font);
+ setFont(font);
+ backward=new JRadioButton(Logo.messages.getString("backward"));
+ forward=new JRadioButton(Logo.messages.getString("forward"));
+ TitledBorder tb=BorderFactory.createTitledBorder(Logo.messages.getString("direction"));
+ tb.setTitleFont(font);
+ buttonPanel.setBorder(tb);
+ find=new JButton(Logo.messages.getString("find"));
+ labelFind=new JLabel(Logo.messages.getString("find")+" :");
+ setTitle(Logo.messages.getString("find_replace"));
+ }
+ private void initGui(){
+ Font font = WSManager.getWorkspaceConfig().getFont();
+
+ setTitle(Logo.messages.getString("find_replace"));
+ // Init the RadioButton for the direction search
+ backward=new JRadioButton(Logo.messages.getString("backward"));
+ forward=new JRadioButton(Logo.messages.getString("forward"));
+ forward.setSelected(true);
+ bg=new ButtonGroup();
+ bg.add(forward);
+ bg.add(backward);
+ buttonPanel=new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.Y_AXIS));
+ buttonPanel.add(forward);
+ buttonPanel.add(backward);
+ TitledBorder tb=BorderFactory.createTitledBorder(Logo.messages.getString("direction"));
+ tb.setTitleFont(font);
+ buttonPanel.setBorder(tb);
+
+ // Init Buttons
+ find=new JButton(Logo.messages.getString("find"));
+ find.addActionListener(this);
+ find.setActionCommand(FIND);
+
+ // Init JLabel and JCombobox
+ labelFind=new JLabel(Logo.messages.getString("find")+" :");
+ labelResult=new JLabel();
+
+ comboFind=new JComboBox();
+ comboFind.setEditable(true);
+
+ backward.setFont(font);
+ forward.setFont(font);
+ find.setFont(font);
+ labelFind.setFont(font);
+ setFont(font);
+
+ getContentPane().setLayout(new GridBagLayout());
+ // Draw all
+
+ getContentPane().add(labelFind, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(comboFind, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(buttonPanel, new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(labelResult, new GridBagConstraints(0, 3, 2, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ getContentPane().add(find, new GridBagConstraints(1, 2, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(
+ 10, 10, 10, 10), 0, 0));
+ }
+}
diff --git a/logo/src/xlogo/gui/Searchable.java b/logo/src/xlogo/gui/Searchable.java
new file mode 100644
index 0000000..2244de8
--- /dev/null
+++ b/logo/src/xlogo/gui/Searchable.java
@@ -0,0 +1,36 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+public interface Searchable {
+ boolean find(String element, boolean forward);
+ void replace(String element,boolean forward);
+ void replaceAll(String element,String substitute);
+ void removeHighlight();
+}
diff --git a/logo/src/xlogo/gui/Traduc.java b/logo/src/xlogo/gui/Traduc.java
new file mode 100644
index 0000000..1c36acc
--- /dev/null
+++ b/logo/src/xlogo/gui/Traduc.java
@@ -0,0 +1,216 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+import javax.swing.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.StringTokenizer;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.TreeMap;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.Language;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+import xlogo.Logo;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+
+/** Frame For translating Logo Code from a language to another
+ * * */
+
+public class Traduc extends JFrame implements ActionListener {
+ private static final long serialVersionUID = 1L;
+ private JLabel traduire_de=new JLabel(Logo.messages.getString("traduire_de")+" ");
+ private JLabel vers=new JLabel(" "+Logo.messages.getString("vers")+" ");
+
+ private JComboBox combo_origine=new JComboBox(Logo.translationLanguage);
+ private JComboBox combo_destination=new JComboBox(Logo.translationLanguage);
+ private JScrollPane js_source=new JScrollPane();
+ private JScrollPane js_destination=new JScrollPane();
+ private JTextArea origine=new JTextArea();
+ private JTextArea destination=new JTextArea();
+ private JPanel p_nord_origine =new JPanel();
+ private JPanel p_nord_destination =new JPanel();
+ private JPanel p_ouest =new JPanel();
+ private JPanel p_est=new JPanel();
+ private JPanel p_edition_origine=new JPanel();
+ private JPanel p_edition_destination=new JPanel();
+ private JButton traduire=new JButton(Logo.messages.getString("traduire"));
+ private ImageIcon icopier=Utils.dimensionne_image("editcopy.png",this);
+ private ImageIcon icoller=Utils.dimensionne_image("editpaste.png",this);
+ private ImageIcon icouper=Utils.dimensionne_image("editcut.png",this);
+ private JButton copier_origine=new JButton(icopier);
+ private JButton coller_origine=new JButton(icoller);
+ private JButton couper_origine=new JButton(icouper);
+ private JButton copier_destination=new JButton(icopier);
+ private JButton coller_destination=new JButton(icoller);
+ private JButton couper_destination=new JButton(icouper);
+
+ private ResourceBundle primitives_origine=null;
+ private ResourceBundle primitives_destination=null;
+ private TreeMap<String,String> tre=new TreeMap<String,String>();
+
+ public Traduc(){
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ Font font = wc.getFont();
+ Language lang = wc.getLanguage();
+
+ setTitle(Logo.messages.getString("menu.tools.translate"));
+ setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png")));
+ setFont(font);
+ traduire.setFont(font);
+ vers.setFont(font);
+ traduire_de.setFont(font);
+ getContentPane().setLayout(new BorderLayout());
+ combo_origine.setSelectedIndex(lang.getValue());
+ combo_destination.setSelectedIndex(lang.getValue());
+
+ p_nord_origine.add(traduire_de);
+ p_nord_origine.add(combo_origine);
+ p_nord_destination.add(vers);
+ p_nord_destination.add(combo_destination);
+
+ p_edition_origine.add(copier_origine);
+ p_edition_origine.add(couper_origine);
+ p_edition_origine.add(coller_origine);
+
+ p_ouest.setLayout(new BorderLayout());
+ p_ouest.add(js_source,BorderLayout.CENTER);
+ p_ouest.add(p_edition_origine,BorderLayout.SOUTH);
+ p_ouest.add(p_nord_origine,BorderLayout.NORTH);
+
+ getContentPane().add(p_ouest,BorderLayout.WEST);
+
+ p_edition_destination.add(copier_destination);
+ p_edition_destination.add(couper_destination);
+ p_edition_destination.add(coller_destination);
+
+ p_est.setLayout(new BorderLayout());
+ p_est.add(js_destination,BorderLayout.CENTER);
+ p_est.add(p_edition_destination,BorderLayout.SOUTH);
+ p_est.add(p_nord_destination,BorderLayout.NORTH);
+
+ getContentPane().add(p_est,BorderLayout.EAST);
+ getContentPane().add(traduire,BorderLayout.CENTER);
+
+ js_source.getViewport().add(origine);
+ js_destination.getViewport().add(destination);
+ js_source.setPreferredSize(new Dimension(300,300));
+ js_destination.setPreferredSize(new Dimension(300,300));
+
+ traduire.addActionListener(this);
+ copier_destination.addActionListener(this);
+ couper_destination.addActionListener(this);
+ coller_destination.addActionListener(this);
+ copier_origine.addActionListener(this);
+ couper_origine.addActionListener(this);
+ coller_origine.addActionListener(this);
+ copier_origine.setActionCommand("copier_origine");
+ couper_origine.setActionCommand("couper_origine");
+ coller_origine.setActionCommand("coller_origine");
+ coller_destination.setActionCommand("coller_destination");
+ copier_destination.setActionCommand("copier_destination");
+ couper_destination.setActionCommand("couper_destination");
+
+ pack();
+ setVisible(true);
+ }
+
+ public void actionPerformed(ActionEvent e){
+ String cmd=e.getActionCommand();
+ if (Logo.messages.getString("traduire").equals(cmd)){
+ String texte=origine.getText();
+ texte=texte.replaceAll("\\t", " ");
+ primitives_origine=genere_langue(combo_origine);
+ primitives_destination=genere_langue(combo_destination);
+ Enumeration<String> en=primitives_origine.getKeys();
+ while (en.hasMoreElements()){
+ String element=en.nextElement().toString();
+ String primitives=primitives_origine.getString(element);
+ String primitives2=primitives_destination.getString(element);
+ StringTokenizer st=new StringTokenizer(primitives);
+ StringTokenizer st2=new StringTokenizer(primitives2);
+ int compteur=st.countTokens();
+ for (int i=0;i<compteur;i++){
+ while (st2.hasMoreTokens()) element=st2.nextToken();
+ tre.put(st.nextToken(),element);
+ }
+ }
+ // ajout des mots clés pour et fin
+ int id=combo_origine.getSelectedIndex();
+ Locale locale=Language.getLanguage(id).getLocale();
+ ResourceBundle res1=ResourceBundle.getBundle("langage",locale);
+ id=combo_destination.getSelectedIndex();
+ locale=Language.getLanguage(id).getLocale();
+ ResourceBundle res2=ResourceBundle.getBundle("langage",locale);
+ tre.put(res1.getString("pour"),res2.getString("pour"));
+ tre.put(res1.getString("fin"),res2.getString("fin"));
+ StringTokenizer st=new StringTokenizer(texte," */+-\n|&()[]",true);
+ String traduc="";
+ while (st.hasMoreTokens()){
+ String element=st.nextToken().toLowerCase();
+ if (tre.containsKey(element)) traduc+=tre.get(element);
+ else traduc+=element;
+ }
+ destination.setText(traduc);
+ }
+ else if ("copier_origine".equals(cmd)) {
+ origine.copy();
+ }
+ else if ("couper_origine".equals(cmd)) {
+ origine.cut();
+ }
+ else if ("coller_origine".equals(cmd)) {
+ origine.paste();
+ }
+ else if ("copier_destination".equals(cmd)) {
+ destination.copy();
+ }
+ else if ("couper_destination".equals(cmd)) {
+ destination.cut();
+ }
+ else if ("coller_destination".equals(cmd)) {
+ destination.paste();
+ }
+ }
+ private ResourceBundle genere_langue(JComboBox jc){ // fixe la langue utilisée pour les messages
+ Locale locale=null;
+ int id=jc.getSelectedIndex();
+ locale=Language.getLanguage(id).getLocale();
+ return ResourceBundle.getBundle("primitives",locale);
+ }
+}
diff --git a/logo/src/xlogo/gui/ZoneCommande.java b/logo/src/xlogo/gui/ZoneCommande.java
new file mode 100644
index 0000000..0bc5f76
--- /dev/null
+++ b/logo/src/xlogo/gui/ZoneCommande.java
@@ -0,0 +1,271 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.JTextPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.*;
+import javax.swing.text.BadLocationException;
+
+import java.awt.Color;
+
+import xlogo.StyledDocument.DocumentLogoCommande;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.Application;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+public class ZoneCommande extends JTextPane implements CaretListener
+{
+ private static final long serialVersionUID = 1L;
+ private DocumentLogoCommande dlc;
+ // Si la correspondance entre parenthese ou crochets est activée
+ private boolean active = false;
+ // Dernière position allumée
+ private int[] position = new int[2];
+
+ public ZoneCommande(Application cadre)
+ {
+ dlc = new DocumentLogoCommande(cadre);
+ this.setBackground(Color.WHITE);
+ setDocument(dlc);
+ addCaretListener(this);
+ }
+
+ class verif_parenthese implements Runnable
+ {
+ int pos;
+
+ verif_parenthese(int pos)
+ {
+ this.pos = pos;
+ }
+
+ public void run()
+ {
+ if (active)
+ {
+ active = false;
+ int debut = position[0];
+ int fin = position[0];
+ if (debut != -1)
+ {
+ if (debut > 0)
+ debut--;
+ if (fin < dlc.getLength())
+ fin++;
+ try
+ {
+ String content = dlc.getText(0, dlc.getLength());
+ // System.out.println(debut+" "+fin);
+ dlc.colore(content, debut, fin);
+ }
+ catch (BadLocationException e)
+ {}
+ }
+ debut = position[1];
+ fin = position[1];
+ if (debut != -1)
+ {
+ if (debut > 0)
+ debut--;
+ if (fin < dlc.getLength())
+ debut++;
+ if (fin < dlc.getLength())
+ fin++;
+ try
+ {
+ String content = dlc.getText(0, dlc.getLength());
+ // System.out.println(debut+" "+fin);
+ dlc.colore(content, debut, fin);
+ }
+ catch (BadLocationException e)
+ {}
+ }
+ }
+ int length = dlc.getLength();
+ try
+ {
+ String content = dlc.getText(pos, length - pos);
+ int id = -1;
+ if (length > pos)
+ {
+ id = "[]()".indexOf(content.substring(0, 1));
+ }
+ if (id > -1 && !TesteBackslash(dlc.getText(0, pos), pos))
+ {
+ active = true;
+ switch (id)
+ {
+ case 0:
+ chercheApres(content, pos, "[", "]");
+ break;
+ case 1:
+ content = getText(0, pos);
+ chercheAvant(content, pos, "[", "]");
+ break;
+ case 2:
+ chercheApres(content, pos, "(", ")");
+ break;
+ case 3:
+ content = getText(0, pos);
+ chercheAvant(content, pos, "(", ")");
+ break;
+ }
+ }
+ }
+ catch (BadLocationException e1)
+ {}
+
+ }
+ }
+
+ public void caretUpdate(CaretEvent e)
+ {
+ int pos = e.getDot();
+ if (WSManager.getWorkspaceConfig().isSyntaxHighlightingEnabled())
+ SwingUtilities.invokeLater(new verif_parenthese(pos));
+ }
+
+ // Teste si le caractère précédent est un backslash
+ private boolean TesteBackslash(String content, int pos)
+ {
+ String caractere = "";
+ if (pos > 0)
+ caractere = content.substring(pos - 1, pos);
+ // System.out.println(caractere);
+ if (caractere.equals("\\"))
+ return true;
+ return false;
+ }
+
+ void chercheApres(String content, int pos, String ouv, String fer)
+ {
+ boolean continuer = true;
+ int of_ouvrant;
+ int of_fermant = 0;
+ int from_index_ouvrant = 1;
+ int from_index_fermant = 1;
+ while (continuer)
+ {
+ of_ouvrant = content.indexOf(ouv, from_index_ouvrant);
+ while (of_ouvrant != -1 && TesteBackslash(content, of_ouvrant))
+ of_ouvrant = content.indexOf(ouv, of_ouvrant + 1);
+ of_fermant = content.indexOf(fer, from_index_fermant);
+ while (of_fermant != -1 && TesteBackslash(content, of_fermant))
+ {
+ of_fermant = content.indexOf(fer, of_fermant + 1);
+ // System.out.println(of_fermant);
+ }
+ if (of_fermant == -1)
+ break;
+ if (of_ouvrant != -1 && of_ouvrant < of_fermant)
+ {
+ from_index_ouvrant = of_ouvrant + 1;
+ from_index_fermant = of_fermant + 1;
+ }
+ else
+ continuer = false;
+ ;
+ }
+ if (of_fermant != -1)
+ {
+ dlc.Montre_Parenthese(of_fermant + pos);
+ position[1] = of_fermant + pos;
+ }
+ else
+ position[1] = -1;
+ dlc.Montre_Parenthese(pos);
+ position[0] = pos;
+ }
+
+ void chercheAvant(String content, int pos, String ouv, String fer)
+ {
+ boolean continuer = true;
+ int of_fermant = 0;
+ int of_ouvrant = 0;
+ int from_index_ouvrant = pos;
+ int from_index_fermant = pos;
+ while (continuer)
+ {
+ of_ouvrant = content.lastIndexOf(ouv, from_index_ouvrant);
+ while (of_ouvrant != -1 && TesteBackslash(content, of_ouvrant))
+ of_ouvrant = content.lastIndexOf(ouv, of_ouvrant - 1);
+ of_fermant = content.lastIndexOf(fer, from_index_fermant);
+ while (of_fermant != -1 && TesteBackslash(content, of_fermant))
+ of_fermant = content.lastIndexOf(fer, of_fermant - 1);
+ if (of_ouvrant == -1)
+ break;
+ if (of_ouvrant < of_fermant)
+ {
+ from_index_ouvrant = of_ouvrant - 1;
+ from_index_fermant = of_fermant - 1;
+ }
+ else
+ continuer = false;
+ ;
+ }
+ if (of_ouvrant != -1)
+ {
+ dlc.Montre_Parenthese(of_ouvrant);
+ position[0] = of_ouvrant;
+ }
+ else
+ position[0] = -1;
+ dlc.Montre_Parenthese(pos);
+ position[1] = pos;
+ }
+
+ // Change Syntax Highlighting for the command line
+ public void initStyles(int c_comment, int sty_comment, int c_primitive, int sty_primitive, int c_parenthese,
+ int sty_parenthese, int c_operande, int sty_operande)
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ dlc.initStyles(wc.getCommentColor(), wc.getCommentStyle(), wc.getPrimitiveColor(), wc.getPrimitiveStyle(),
+ wc.getBraceColor(), wc.getBraceStyle(), wc.getOperatorColor(), wc.getOperatorStyle());
+ }
+
+ // Enable or disable Syntax Highlighting
+ public void setColoration(boolean b)
+ {
+ dlc.setColoration(b);
+ }
+
+ public void setActive(boolean b)
+ {
+ active = b;
+ }
+
+}
diff --git a/logo/src/xlogo/gui/ZoneEdition.java b/logo/src/xlogo/gui/ZoneEdition.java
new file mode 100644
index 0000000..afbda11
--- /dev/null
+++ b/logo/src/xlogo/gui/ZoneEdition.java
@@ -0,0 +1,196 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui;
+
+import javax.swing.event.*;
+import javax.swing.JTextPane;
+import javax.swing.text.BadLocationException;
+import javax.swing.SwingUtilities;
+
+import xlogo.StyledDocument.DocumentLogo;
+import xlogo.storage.WSManager;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+class ZoneEdition extends JTextPane implements CaretListener{
+ private static final long serialVersionUID = 1L;
+ private DocumentLogo dsd=null;
+ // Si la correspondance entre parenthese ou crochets est activée
+ private boolean active=false;
+ // Dernière position allumée
+ private int[] position=new int[2];
+
+
+// public DocumentLogo getDsd(){
+// return dsd;
+// }
+ public void setActive(boolean b){
+ active=b;
+ }
+ ZoneEdition(EditorTextPane etp){
+ dsd=etp.getDsd();
+ addCaretListener(this);
+}
+
+// Teste si le caractère précédent est un backslash
+private boolean TesteBackslash(String content,int pos){
+ String caractere="";
+ if (pos>0) caractere=content.substring(pos-1,pos);
+ //System.out.println(caractere);
+ if (caractere.equals("\\")) return true;
+ return false;
+}
+
+class verif_parenthese implements Runnable{
+ int pos;
+ verif_parenthese(int pos){
+ this.pos=pos;
+ }
+ public void run(){
+ if (active){
+ active=false;
+ int debut=position[0];
+ int fin=position[0];
+ if (debut!=-1){
+ if (debut>0) debut--;
+ if (fin<dsd.getLength()) fin++;
+ try{
+ String content=dsd.getText(0,dsd.getLength());
+ dsd.colore(content,debut,fin);
+ }
+ catch(BadLocationException e){}
+ }
+ debut=position[1];
+ fin=position[1];
+ if (debut!=-1){
+ if (debut>0) debut--;
+ if (fin<dsd.getLength()) debut++;
+ if (fin<dsd.getLength()) fin++;
+ try{
+ String content=dsd.getText(0,dsd.getLength());
+ dsd.colore(content,debut,fin);
+ }
+ catch(BadLocationException e){}
+ }
+ }
+ int length=dsd.getLength();
+ try{
+ String content=dsd.getText(pos,length-pos);
+ int id=-1;
+ if (length>pos) {
+ id="[]()".indexOf(content.substring(0,1));
+ }
+ if (id>-1&&!TesteBackslash(dsd.getText(0,pos),pos)){
+ active=true;
+ switch(id){
+ case 0:
+ chercheApres(content,pos,"[","]");
+ break;
+ case 1:
+ content=getText(0,pos);
+ chercheAvant(content,pos,"[","]");
+ break;
+ case 2:
+ chercheApres(content,pos,"(",")");
+ break;
+ case 3:
+ content=getText(0,pos);
+ chercheAvant(content,pos,"(",")");
+ break;
+ }
+ }
+ }
+ catch(BadLocationException e1){}
+
+ }
+}
+public void caretUpdate(CaretEvent e){
+ int pos=e.getDot();
+ if (WSManager.getWorkspaceConfig().isSyntaxHighlightingEnabled()) SwingUtilities.invokeLater(new verif_parenthese(pos));
+}
+ void chercheApres(String content,int pos,String ouv,String fer){
+ boolean continuer=true;
+ int of_ouvrant;
+ int of_fermant=0;
+ int from_index_ouvrant=1;
+ int from_index_fermant=1;
+ while(continuer){
+ of_ouvrant=content.indexOf(ouv,from_index_ouvrant);
+ while (of_ouvrant!=-1&&TesteBackslash(content,of_ouvrant)) of_ouvrant=content.indexOf(ouv,of_ouvrant+1);
+ of_fermant=content.indexOf(fer,from_index_fermant);
+ while (of_fermant!=-1&&TesteBackslash(content,of_fermant)) of_fermant=content.indexOf(fer,of_fermant+1);
+ if (of_fermant==-1) break;
+ if (of_ouvrant!=-1 && of_ouvrant<of_fermant) {
+ from_index_ouvrant=of_ouvrant+1;
+ from_index_fermant=of_fermant+1;
+ }
+ else continuer=false;;
+ }
+ if (of_fermant!=-1) {
+ dsd.Montre_Parenthese(of_fermant+pos);
+ position[1]=of_fermant+pos;
+ }
+ else position[1]=-1;
+ dsd.Montre_Parenthese(pos);
+ position[0]=pos;
+ }
+ void chercheAvant(String content,int pos,String ouv,String fer){
+ boolean continuer=true;
+ int of_fermant=0;
+ int of_ouvrant=0;
+ int from_index_ouvrant=pos;
+ int from_index_fermant=pos;
+ while(continuer){
+ of_ouvrant=content.lastIndexOf(ouv,from_index_ouvrant);
+ while (of_ouvrant!=-1&&TesteBackslash(content,of_ouvrant)) of_ouvrant=content.lastIndexOf(ouv,of_ouvrant-1);
+ of_fermant=content.lastIndexOf(fer,from_index_fermant);
+ while (of_fermant!=-1&&TesteBackslash(content,of_fermant)) of_fermant=content.lastIndexOf(fer,of_fermant-1);
+ if (of_ouvrant==-1) break;
+ if (of_ouvrant<of_fermant) {
+ from_index_ouvrant=of_ouvrant-1;
+ from_index_fermant=of_fermant-1;
+ }
+ else continuer=false;;
+ }
+ if (of_ouvrant!=-1) {
+ dsd.Montre_Parenthese(of_ouvrant);
+ position[0]=of_ouvrant;
+ }
+ else position[0]=-1;
+ dsd.Montre_Parenthese(pos);
+ position[1]=pos;
+
+
+ }
+
+} \ No newline at end of file
diff --git a/logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java b/logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java
new file mode 100644
index 0000000..0e161a2
--- /dev/null
+++ b/logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java
@@ -0,0 +1,232 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * a lot of modifications, extensions, refactorings have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui.components;
+
+import javax.swing.*;
+
+import java.awt.event.*;
+import java.awt.*;
+import java.util.ArrayList;
+
+import xlogo.kernel.DrawPanel;
+import xlogo.Logo;
+
+/**
+ * Title : XLogo Description : XLogo is an interpreter for the Logo programming
+ * language
+ *
+ * @author Loïc Le Coq, Marko refactored
+ */
+public class ColorStyleSelectionPanel {
+
+ private JPanel component = new JPanel();
+
+ public JPanel getComponent()
+ {
+ return component;
+ }
+
+ private Integer[] intArray = new Integer[17];
+ private JButton bchoisir = new JButton(
+ Logo.messages.getString("pref.highlight.other"));
+ private JComboBox combo_couleur;
+ private String[] msg = {
+ Logo.messages.getString("style.none"),
+ Logo.messages.getString("style.bold"),
+ Logo.messages.getString("style.italic"),
+ Logo.messages.getString("style.underline") };
+ private JComboBox style = new JComboBox(msg);
+ private JLabel titre = new JLabel();
+ private Color couleur_perso = Color.WHITE;
+ private GridBagLayout gb = new GridBagLayout();
+
+ public ColorStyleSelectionPanel(int rgb, int sty, String title) {
+ //WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ //Font font = wc.getFont();
+
+ component.setLayout(gb);
+
+ for (int i = 0; i < 17; i++) {
+ intArray[i] = new Integer(i);
+ }
+ // Create the combo box.
+ //titre.setFont(font);
+ titre.setText(title + ":");
+
+ combo_couleur = new JComboBox(intArray);
+ ComboBoxRenderer renderer = new ComboBoxRenderer();
+ combo_couleur.setRenderer(renderer);
+ setColorAndStyle(rgb, sty);
+ combo_couleur.setActionCommand("combo");
+ combo_couleur.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ notifyActionListeners(e);
+ }
+ });
+ //bchoisir.setFont(font);
+ //style.setFont(font);
+ bchoisir.setActionCommand("bouton");
+ bchoisir.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Color color = selectColor();
+ if (null != color) {
+ couleur_perso = color;
+ combo_couleur.setSelectedIndex(7);
+ combo_couleur.repaint();
+ }
+ notifyActionListeners(e);
+ }
+ });
+
+ style.setActionCommand("style");
+ style.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ notifyActionListeners(e);
+ }
+ });
+ //int hauteur = font.getSize() + 5;
+ // jt.setPreferredSize(new Dimension(240,hauteur));
+
+ // Lay out the demo.
+ component.add(combo_couleur, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(5, 5, 5, 5), 0, 0));
+ component.add(bchoisir, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(5, 5, 5, 5), 0, 0));
+ component.add(style, new GridBagConstraints(2, 1, 1, 1, 1.0, 1.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(5, 5, 5, 5), 0, 0));
+ component.add(titre, new GridBagConstraints(0, 0, 3, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.VERTICAL,
+ new Insets(5, 5, 0, 5), 0, 0));
+
+ component.setBorder(BorderFactory.createLineBorder(Color.BLUE));
+ //component.setPreferredSize(new Dimension(300, hauteur * 2 + 20));
+ }
+
+ public void setTitle(String title)
+ {
+ titre.setText(title);
+ }
+
+ public void setColorAndStyle(int rgb, int sty) {
+ style.setSelectedIndex(sty);
+ int index = -1;
+ for (int i = 0; i < 17; i++) {
+ if (DrawPanel.defaultColors[i].getRGB() == rgb) {
+ index = i;
+ }
+ }
+ if (index == -1) {
+ couleur_perso = new Color(rgb);
+ index = 7;
+ }
+ combo_couleur.setSelectedIndex(index);
+ }
+
+ private Color selectColor(){
+ return JColorChooser.showDialog(component, "",
+ DrawPanel.defaultColors[combo_couleur.getSelectedIndex()]);
+ }
+
+ private class ComboBoxRenderer extends JPanel implements ListCellRenderer {
+ private static final long serialVersionUID = 1L;
+ int id = 0;
+
+ public ComboBoxRenderer() {
+ setOpaque(true);
+ setPreferredSize(new Dimension(50, 20));
+ }
+
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus) {
+ // Get the selected index. (The index param isn't
+ // always valid, so just use the value.)
+ int selectedIndex = ((Integer) value).intValue();
+ this.id = selectedIndex;
+ if (isSelected) {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ } else {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+ // Set the icon and text. If icon was null, say so.
+
+ setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+ return this;
+ }
+
+ public void paint(Graphics g) {
+ super.paint(g);
+ if (id != 7)
+ g.setColor(DrawPanel.defaultColors[id]);
+ else
+ g.setColor(couleur_perso);
+ g.fillRect(5, 2, 40, 15);
+ }
+ }
+
+ public int color() {
+ int id = combo_couleur.getSelectedIndex();
+ if (id != 7)
+ return DrawPanel.defaultColors[id].getRGB();
+ return couleur_perso.getRGB();
+ }
+
+ public int style() {
+ int id = style.getSelectedIndex();
+ return id;
+ }
+
+ public void setEnabled(boolean b) {
+ component.setEnabled(b);
+ combo_couleur.setEnabled(b);
+ style.setEnabled(b);
+ bchoisir.setEnabled(b);
+ }
+
+
+ private ArrayList<ActionListener> actionListeners = new ArrayList<ActionListener>();
+
+ public void addStyleChangeListener(ActionListener listener)
+ {
+ actionListeners.add(listener);
+ }
+
+ private void notifyActionListeners(ActionEvent e)
+ {
+ for (ActionListener listener : actionListeners)
+ listener.actionPerformed(e);
+ }
+}
diff --git a/logo/src/xlogo/gui/components/ProcedureSearch.java b/logo/src/xlogo/gui/components/ProcedureSearch.java
new file mode 100644
index 0000000..4c01acc
--- /dev/null
+++ b/logo/src/xlogo/gui/components/ProcedureSearch.java
@@ -0,0 +1,309 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.components;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import xlogo.interfaces.ProcedureMapper;
+import xlogo.interfaces.ProcedureMapper.ProcedureMapListener;
+import xlogo.utils.Utils;
+
+public class ProcedureSearch extends JPanel
+{
+ private static final long serialVersionUID = 2275479142269992452L;
+
+ private ProcedureMapper procedureMapper = null;
+
+ private JComboBox procedureSelection = new JComboBox();
+ private JButton procedureSearchButton = new JButton();
+
+ public ProcedureSearch(ProcedureMapper procedureMapper)
+ {
+ this.procedureMapper = procedureMapper;
+ setExecutables(procedureMapper.getAllProcedureNames());
+ initComponents();
+ //applyColorTheme();
+ initListeners();
+ }
+
+ private void initListeners()
+ {
+ procedureMapper.addProcedureMapListener(new ProcedureMapListener(){
+
+ @Override
+ public void ownerRenamed(String oldName, String newName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ ownerRenamed(oldName, newName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void defined(String fileName, Collection<String> procedures)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ defined(fileName, procedures);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void defined(String fileName, String procedure)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ defined(fileName, procedure);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void undefined(String fileName, Collection<String> procedures)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ undefined(fileName, procedures);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void undefined(String fileName, String procedure)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ setExecutables(procedureMapper.getAllProcedureNames());
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ undefined(fileName, procedure);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ procedureSearchButton.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent arg0)
+ {
+ String selected = (String) procedureSelection.getSelectedItem();
+ if (selected == null || selected.length() == 0)
+ return;
+ notifySearchListeners(selected);
+ }
+ });
+
+ procedureSelection.getEditor().addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ String selected = (String) procedureSelection.getSelectedItem();
+ if (selected == null || selected.length() == 0)
+ return;
+ notifySearchListeners(selected);
+ }
+ });
+ }
+
+ private void setExecutables(Collection<String> procedures)
+ {
+ String[] sorted = new String[procedures.size()];
+ sorted = procedures.toArray(sorted);
+ Arrays.sort(sorted);
+
+ procedureSelection.setModel(new DefaultComboBoxModel(sorted));
+ }
+
+ private void initComponents()
+ {
+ procedureSelection.setEditable(true);
+
+ Image img = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource("chercher.png"));
+ procedureSearchButton.setIcon(new ImageIcon(img.getScaledInstance(20, 20, Image.SCALE_SMOOTH)));
+
+ setLayout(new GridBagLayout());
+ GridBagConstraints c = new GridBagConstraints();
+
+ c.fill = GridBagConstraints.BOTH;
+ c.weightx = 1;
+ add(procedureSelection, c);
+
+ c.gridx = 1;
+ c.weightx = 0;
+ add(procedureSearchButton, c);
+ }
+
+ public JComponent getComponent()
+ {
+ return this;
+ }
+
+ @Override
+ public void setFont(Font font)
+ {
+ super.setFont(font);
+ if (procedureSelection != null) // Apparently this became called before object construction...
+ procedureSelection.setFont(font);
+ }
+
+ /*
+ * PROCEDURE SEARCH REQUEST LISTENER
+ */
+
+ public interface ProcedureSearchRequestListener
+ {
+ public void requestProcedureSearch(String procedureName);
+ }
+
+ private ArrayList<ProcedureSearchRequestListener> searchListeners = new ArrayList<ProcedureSearchRequestListener>();
+
+ public void addSearchRequestListener(ProcedureSearchRequestListener listener)
+ {
+ searchListeners.add(listener);
+ }
+
+ public void removeSearchRequestListener(ProcedureSearchRequestListener listener)
+ {
+ searchListeners.remove(listener);
+ }
+
+ private void notifySearchListeners(String procedureName)
+ {
+ for (ProcedureSearchRequestListener listener : searchListeners)
+ listener.requestProcedureSearch(procedureName);
+ }
+}
diff --git a/logo/src/xlogo/gui/components/TurtleComboBox.java b/logo/src/xlogo/gui/components/TurtleComboBox.java
new file mode 100644
index 0000000..952ed7e
--- /dev/null
+++ b/logo/src/xlogo/gui/components/TurtleComboBox.java
@@ -0,0 +1,169 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were adapted by Marko Zivkovic
+ *
+ * The original authors:
+ *
+ * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * - Neither the name of Oracle or the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package xlogo.gui.components;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+import xlogo.utils.Utils;
+
+public class TurtleComboBox extends JPanel
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6921667779684536164L;
+ ImageIcon[] images;
+ String[] petStrings = { "preview0", "preview1", "preview2", "preview3", "preview4", "preview5", "preview6" };
+
+ JComboBox petList;
+ /*
+ * Despite its use of EmptyBorder, this panel makes a fine content
+ * pane because the empty border just increases the panel's size
+ * and is "painted" on top of the panel's normal background. In
+ * other words, the JPanel fills its entire background if it's
+ * opaque (which it is by default); adding a border doesn't change
+ * that.
+ */
+ public TurtleComboBox()
+ {
+ super(new BorderLayout());
+
+ //Load the pet images and create an array of indexes.
+ images = new ImageIcon[petStrings.length];
+ Integer[] intArray = new Integer[petStrings.length];
+ for (int i = 0; i < petStrings.length; i++)
+ {
+ intArray[i] = new Integer(i);
+ images[i] = createImageIcon(petStrings[i] + ".png");
+ if (images[i] != null)
+ {
+ images[i].setDescription(petStrings[i]);
+ }
+ }
+
+ //Create the combo box.
+ petList = new JComboBox(intArray);
+ ComboBoxRenderer renderer = new ComboBoxRenderer();
+ renderer.setPreferredSize(new Dimension(50, 50));
+ petList.setRenderer(renderer);
+ petList.setMaximumRowCount(7);
+
+ //Lay out the demo.
+ add(petList, BorderLayout.PAGE_START);
+ setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ }
+
+ public JComboBox getComboBox()
+ {
+ return petList;
+ }
+
+ /** Returns an ImageIcon, or null if the path was invalid. */
+ protected static ImageIcon createImageIcon(String path)
+ {
+ try{
+ return new ImageIcon(Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(path)));
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ class ComboBoxRenderer extends JLabel implements ListCellRenderer
+ {
+
+ private static final long serialVersionUID = -2208613325470559104L;
+
+ public ComboBoxRenderer()
+ {
+ setOpaque(true);
+ setHorizontalAlignment(CENTER);
+ setVerticalAlignment(CENTER);
+ }
+
+ /*
+ * This method finds the image and text corresponding
+ * to the selected value and returns the label, set up
+ * to display the text and image.
+ */
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+ boolean cellHasFocus)
+ {
+ //Get the selected index. (The index param isn't
+ //always valid, so just use the value.)
+ int selectedIndex = ((Integer) value).intValue();
+
+ if (isSelected)
+ {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ }
+ else
+ {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+
+ //Set the icon and text. If icon was null, say so.
+ ImageIcon icon = images[selectedIndex];
+ setIcon(icon);
+
+ return this;
+ }
+ }
+}
diff --git a/logo/src/xlogo/gui/components/X4SComponent.java b/logo/src/xlogo/gui/components/X4SComponent.java
new file mode 100644
index 0000000..f28341d
--- /dev/null
+++ b/logo/src/xlogo/gui/components/X4SComponent.java
@@ -0,0 +1,81 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.components;
+
+import java.io.File;
+
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+/**
+ * Base Class for well structured GUI components.
+ * includes commonly used features for displaying popups and retrieving user input per dialogs.
+ * @author Marko
+ *
+ */
+public abstract class X4SComponent extends X4SGui{
+
+ public X4SComponent() {
+ super();
+ }
+
+ public abstract JComponent getComponent();
+
+ protected String getUserText(String message, String title)
+ {
+ return (String) JOptionPane.showInputDialog(
+ getComponent(),
+ message,
+ title,
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ null,
+ null);
+ }
+
+ protected boolean getUserYesOrNo(String message, String title)
+ {
+ int ans = JOptionPane.showConfirmDialog(getComponent(), message, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+
+ return ans == JOptionPane.YES_OPTION;
+ }
+
+ protected File getUserSelectedDirectory()
+ {
+ final JFileChooser fc = new JFileChooser();
+ fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ int returnVal = fc.showOpenDialog(getComponent());
+
+ if(returnVal == JFileChooser.APPROVE_OPTION)
+ return fc.getSelectedFile();
+ else
+ return null;
+ }
+
+}
diff --git a/logo/src/xlogo/gui/components/X4SFrame.java b/logo/src/xlogo/gui/components/X4SFrame.java
new file mode 100644
index 0000000..92296bb
--- /dev/null
+++ b/logo/src/xlogo/gui/components/X4SFrame.java
@@ -0,0 +1,40 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.components;
+
+import javax.swing.JFrame;
+
+public abstract class X4SFrame extends X4SGui{
+
+ public X4SFrame() {
+ super();
+ }
+
+ public abstract JFrame getFrame();
+
+}
diff --git a/logo/src/xlogo/gui/components/X4SGui.java b/logo/src/xlogo/gui/components/X4SGui.java
new file mode 100644
index 0000000..c0dbc81
--- /dev/null
+++ b/logo/src/xlogo/gui/components/X4SGui.java
@@ -0,0 +1,112 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.components;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import xlogo.AppSettings;
+
+/**
+ * Automatically calls all convenience features in the following order:
+ * <li> initComponent() </li>
+ * <li> layoutComponent() </li>
+ * <li> setText() </li>
+ * <li> initEventListeners() </li>
+ * <li> startListernForLanguageChangeEvents() </li>
+ * <p> Additionally, setText is called when language is changed by the user.
+ * If you do not need to update the language, just {@link X4SGui#stopListenForLanguageChangeEvents()}
+ * @since June 10th 2014
+ * @author Marko
+ */
+public abstract class X4SGui {
+
+ public X4SGui() {
+ initComponent();
+ layoutComponent();
+ setText();
+ initEventListeners();
+ startListenForLanguageChangeEvents();
+ }
+ /**
+ * Subclassses should make sure the component is completely initialized and ready to be used.
+ * Called before setText() and layotComponent()
+ */
+ protected abstract void initComponent();
+
+ protected abstract void layoutComponent();
+
+ protected abstract void initEventListeners();
+
+ public void stopEventListeners()
+ {
+ stopListenForLanguageChangeEvents();
+ }
+
+ private ActionListener languageChangeListener;
+
+ /**
+ * Note: registers only once, even if called more than once.
+ */
+ public void startListenForLanguageChangeEvents()
+ {
+ if (languageChangeListener != null)
+ return;
+ languageChangeListener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ setText();
+ }
+ };
+ AppSettings.getInstance().addLanguageChangeListener(languageChangeListener);
+ }
+
+ public void stopListenForLanguageChangeEvents()
+ {
+ if (languageChangeListener == null)
+ return;
+ AppSettings.getInstance().removeLanguageChangeListener(languageChangeListener);
+ languageChangeListener = null;
+ }
+
+ /**
+ * Called whenever language is changed or the component is initialized.
+ */
+ protected void setText() { }
+
+ /**
+ * Shortcut to LanguageManager.getInstance().translate(key)
+ * @param key
+ * @return
+ */
+ protected String translate(String key)
+ {
+ return AppSettings.getInstance().translate(key);
+ }
+
+}
diff --git a/logo/src/xlogo/gui/components/fileslist/11_alerts_and_states_error.png b/logo/src/xlogo/gui/components/fileslist/11_alerts_and_states_error.png
new file mode 100644
index 0000000..097c3bc
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/11_alerts_and_states_error.png
Binary files differ
diff --git a/logo/src/xlogo/gui/components/fileslist/1_navigation_accept.png b/logo/src/xlogo/gui/components/fileslist/1_navigation_accept.png
new file mode 100644
index 0000000..cf5fab3
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/1_navigation_accept.png
Binary files differ
diff --git a/logo/src/xlogo/gui/components/fileslist/5_content_discard.png b/logo/src/xlogo/gui/components/fileslist/5_content_discard.png
new file mode 100644
index 0000000..cedb108
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/5_content_discard.png
Binary files differ
diff --git a/logo/src/xlogo/gui/components/fileslist/5_content_edit.png b/logo/src/xlogo/gui/components/fileslist/5_content_edit.png
new file mode 100644
index 0000000..f09b2e4
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/5_content_edit.png
Binary files differ
diff --git a/logo/src/xlogo/gui/components/fileslist/5_content_remove.png b/logo/src/xlogo/gui/components/fileslist/5_content_remove.png
new file mode 100644
index 0000000..9f4c3d6
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/5_content_remove.png
Binary files differ
diff --git a/logo/src/xlogo/gui/components/fileslist/FilesList.java b/logo/src/xlogo/gui/components/fileslist/FilesList.java
new file mode 100644
index 0000000..eaef2b9
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/FilesList.java
@@ -0,0 +1,665 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.components.fileslist;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
+import xlogo.Logo;
+import xlogo.interfaces.BasicFileContainer;
+import xlogo.interfaces.BasicFileContainer.FileContainerChangeListener;
+import xlogo.interfaces.BroadcasterErrorFileContainer;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector.ErrorListener;
+import xlogo.interfaces.MessageBroadcaster.MessageListener;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.gui.components.fileslist.IFilesListItem.ItemRequestHandler;
+
+public class FilesList extends JPanel
+{
+ private static final long serialVersionUID = -3330227288228959914L;
+
+ BroadcasterErrorFileContainer model;
+ FileContainerChangeListener fileContainerModelListener;
+ ErrorListener errorListener;
+ MessageListener messageListener;
+
+ // Handlers
+ private ActionListener addFileRequestHandler;
+ private ItemRequestHandler fileItemRequestHandler;
+
+ // GUI Components
+ /**
+ * The class of file items to use
+ */
+ private Class<? extends IFilesListItem> listItemClass = FilesListItem.class;
+
+ //private JScrollPane scroller;
+ private JButton addFileButton;
+ private Map<String, IFilesListItem> listItems = new HashMap<String, IFilesListItem>();
+
+ private boolean editEnabled = true;
+
+ /*
+ * Init & Model
+ */
+
+ public FilesList(BroadcasterErrorFileContainer fileContainerModel)
+ {
+
+ initComponents();
+ initFileItemListeners();
+ initErrorListener();
+ initFileContainerListener();
+ intitMessageListener();
+ setModel(fileContainerModel);
+ addFileButton.addActionListener(addFileRequestHandler);
+ }
+
+ private void intitMessageListener()
+ {
+ messageListener = new MessageListener(){
+
+ @Override
+ public void messageEvent(final String fileName, final String message)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onMessageEvent(fileName, message);
+ return;
+ }
+
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onMessageEvent(fileName, message);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ messageEvent(fileName, message);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onMessageEvent(String fileName, String message)
+ {
+ IFilesListItem item = listItems.get(fileName);
+ if (item == null)
+ return;
+
+ item.setMessage(message);
+ }
+ };
+ }
+
+ private void initComponents()
+ {
+ this.setOpaque(false);
+
+ Image img = Toolkit.getDefaultToolkit().getImage(getClass().getResource("new_content.png"));
+ addFileButton = new JButton(new ImageIcon(img.getScaledInstance(20, 20, Image.SCALE_SMOOTH)));
+ addFileButton.setOpaque(true);
+
+ setLayout(new GridBagLayout());
+ }
+
+ GridBagConstraints getLayoutConstraints()
+ {
+ GridBagConstraints c = new GridBagConstraints();
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.NORTHWEST;
+ c.gridx = 0;
+ c.gridy = GridBagConstraints.RELATIVE;
+ c.weightx = 1;
+ return c;
+ }
+
+ private void initFileContainerListener()
+ {
+ fileContainerModelListener = new FileContainerChangeListener(){
+
+ @Override
+ public void fileAdded(final String fileName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onFileAdded(fileName);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onFileAdded(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileAdded(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onFileAdded(String fileName)
+ {
+ // On initialization, the files list is fetched using set model
+ // and the events are received, causing a duplication of items in the list.
+ if (listItems.containsKey(fileName))
+ return;
+
+ IFilesListItem item = null;
+ try
+ {
+ item = listItemClass.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ e.printStackTrace();
+ return;
+ }
+ catch (IllegalAccessException e)
+ {
+ e.printStackTrace();
+ return;
+ }
+ item.setRequestHandler(fileItemRequestHandler);
+ item.setFileName(fileName);
+ item.setEditEnabled(model.isFilesListEditable());
+ item.setError(model.hasErrors(fileName));
+ GridBagConstraints c = getLayoutConstraints();
+ add(item.getComponent(), c);
+ listItems.put(fileName, item);
+ revalidate();
+ }
+
+ @Override
+ public void fileRemoved(final String fileName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onFileRemoved(fileName);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onFileRemoved(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileRemoved(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onFileRemoved(String fileName)
+ {
+ IFilesListItem item = listItems.get(fileName);
+ remove(item.getComponent());
+ listItems.remove(fileName);
+ revalidate();
+ validate();
+ }
+
+ @Override
+ public void fileRenamed(final String oldName, final String newName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onFileRenamed(oldName, newName);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onFileRenamed(oldName, newName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileRenamed(oldName, newName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onFileRenamed(String oldName, String newName)
+ {
+ IFilesListItem item = listItems.get(oldName);
+ item.setFileName(newName);
+ item.setSelected(true);
+ listItems.remove(oldName);
+ listItems.put(newName, item);
+ item.getComponent().revalidate();
+ }
+
+ /**
+ * This implementation allows multiple files open, if the model decides so.
+ * The Model is responsible to close a specific file before opening another.
+ */
+ @Override
+ public void fileOpened(final String fileName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onFileOpened(fileName);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onFileOpened(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileOpened(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onFileOpened(String fileName)
+ {
+ IFilesListItem item = listItems.get(fileName);
+ item.setSelected(true);
+ revalidate();
+ }
+
+ @Override
+ public void fileClosed(final String fileName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onFileClosed(fileName);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onFileClosed(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ fileClosed(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onFileClosed(String fileName)
+ {
+ IFilesListItem item = listItems.get(fileName);
+ item.setSelected(false);
+ item.getComponent().revalidate();
+ }
+
+ @Override
+ public void editRightsChanged(final boolean enabled)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onEditRightsChanged(enabled);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onEditRightsChanged(enabled);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ editRightsChanged(enabled);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onEditRightsChanged(boolean enabled)
+ {
+ editEnabled = enabled;
+ if (addFileButton != null)
+ addFileButton.setVisible(editEnabled);
+ for (IFilesListItem item : listItems.values())
+ {
+ item.setEditEnabled(editEnabled);
+ }
+ revalidate();
+ }
+
+ };
+ }
+
+ private void initErrorListener()
+ {
+ errorListener = new ErrorListener(){
+
+ @Override
+ public void errorsDetected(final String fileName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onErrorsDetected(fileName);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onErrorsDetected(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ errorsDetected(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onErrorsDetected(String fileName)
+ {
+ IFilesListItem item = listItems.get(fileName);
+ if (item == null)
+ return;
+ item.setError(true);
+ item.getComponent().revalidate();
+ }
+
+ @Override
+ public void allErrorsCorrected(final String fileName)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ onAllErrorsCorrected(fileName);
+ return;
+ }
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ onAllErrorsCorrected(fileName);
+ }
+ });
+ }
+ catch (InterruptedException e)
+ {
+ allErrorsCorrected(fileName);
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void onAllErrorsCorrected(String fileName)
+ {
+ IFilesListItem item = listItems.get(fileName);
+ item.setError(false);
+ item.getComponent().revalidate();
+ }
+ };
+ }
+
+ public void setModel(BroadcasterErrorFileContainer fileContainerModel)
+ {
+ if (this.model != null)
+ {
+ this.model.removeFileListener(fileContainerModelListener);
+ this.model.removeErrorListener(errorListener);
+ this.model.removeBroadcastListener(messageListener);
+ }
+
+ this.model = fileContainerModel;
+
+ if (this.model != null)
+ {
+ this.model.addFileListener(fileContainerModelListener);
+ this.model.addErrorListener(errorListener);
+ this.model.addBroadcastListener(messageListener);
+ }
+
+ initItemsList();
+ }
+
+ /**
+ * Empties the filesList and listItems and then refills them according to the model
+ */
+ private void initItemsList()
+ {
+ listItems.clear();
+ //filesList.removeAll();
+ removeAll();
+ if (model == null)
+ return;
+
+ GridBagConstraints c = getLayoutConstraints();
+
+ c.anchor = GridBagConstraints.NORTHWEST;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridy = 0;
+ c.weighty = 0;
+ c.weightx = 1;
+ add(addFileButton, c);
+ addFileButton.setVisible(editEnabled);
+
+ for (String fileName : model.getFileNames())
+ fileContainerModelListener.fileAdded(fileName);
+ revalidate();
+ }
+
+ public JComponent getComponent()
+ {
+ return this;
+ }
+
+ public BasicFileContainer getModel()
+ {
+ return model;
+ }
+
+ /*
+ * GETTERS & SETTERS
+ */
+
+ public JScrollPane getScrollPane()
+ {
+ return null;
+ }
+
+ public JButton getAddFileButton()
+ {
+ return addFileButton;
+ }
+
+ protected Map<String, IFilesListItem> getListItems()
+ {
+ return listItems;
+ }
+
+ /**
+ * @param newButton if null, no add button is shown. If this effect is only temporarily wished, use {@link #setEditFilesListEnabled(boolean)}
+ */
+ public void setAddFileButton(JButton newButton)
+ {
+ if (addFileButton != null)
+ {
+ addFileButton.removeActionListener(addFileRequestHandler);
+ //filesList.remove(addFileButton);
+ remove(addFileButton);
+ }
+ if (newButton != null)
+ {
+ newButton.addActionListener(addFileRequestHandler);
+ newButton.setVisible(model.isFilesListEditable());
+ add(newButton);
+ }
+ addFileButton = newButton;
+ revalidate();
+ }
+
+ public void setListItemClass(Class<? extends IFilesListItem> itemClass) throws InstantiationException,
+ IllegalAccessException
+ {
+ if (itemClass == null)
+ throw new IllegalArgumentException("List item class must not be null.");
+
+ this.listItemClass = itemClass;
+
+ initItemsList();
+ }
+
+ // ITEM LISTENERS
+
+ private void initFileItemListeners()
+ {
+ fileItemRequestHandler = new ItemRequestHandler(){
+ @Override
+ public void renameRequest(String oldName, String newName)
+ {
+ model.renameFile(oldName, newName);
+ }
+
+ @Override
+ public void deleteRequest(String fileName)
+ {
+ model.closeFile(fileName);
+ model.removeFile(fileName);
+ }
+
+ @Override
+ public void openRequest(String fileName)
+ {
+ model.openFile(fileName);
+ }
+
+ @Override
+ public void closeRequest(String fileName)
+ {
+ model.closeFile(fileName);
+ }
+ };
+
+ addFileRequestHandler = new ActionListener(){
+
+ @Override
+ public void actionPerformed(ActionEvent event)
+ {
+ String name = model.makeUniqueFileName(Logo.messages.getString("new.file")); // TODO remove dependency
+ try
+ {
+ model.createFile(name);
+ }
+ catch (Exception e)
+ {
+ DialogMessenger.getInstance().dispatchError(Logo.messages.getString("ws.error.title"), // TODO remove dependency
+ Logo.messages.getString("ws.error.could.not.create.logo.file") + "\n" + e.toString());
+ }
+ }
+ };
+ }
+
+}
diff --git a/logo/src/xlogo/gui/components/fileslist/FilesListItem.java b/logo/src/xlogo/gui/components/fileslist/FilesListItem.java
new file mode 100644
index 0000000..33c3934
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/FilesListItem.java
@@ -0,0 +1,542 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.components.fileslist;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.UIManager;
+
+/**
+ * This list item was programmed to provide general methods for a files list.
+ *
+ * However, the component styles could try to follow the current look and feel.
+ * At the moment, style and colors are hard-coded.
+ *
+ * @author Marko Zivkovic
+ */
+public class FilesListItem extends JPanel implements IFilesListItem
+{
+ private static final long serialVersionUID = 3061129268414679076L;
+
+ /*
+ * These are just estimations that are used for getMinimumSize
+ */
+ //private static int MINIMUM_BUTTON_SIZE = 15; // This scales the button icons
+
+ /*
+ * Item State and properties
+ */
+
+ private boolean isEditEnabled = true;
+
+ /**
+ * This is intentionally not included in the State enum (it would double the amount of states),
+ * because it affects only one property
+ */
+ private State state = State.UNSELECTED;
+
+ /**
+ * This enum denotes the different states that FilesListItem can have.
+ * It further provides all necessary state transitions and the state-dependent sizes.
+ * <p>
+ * Note that only properties are modeled here that affect more than one component's properties,
+ * or properties that depend on more than one variable.
+ * @author Marko
+ *
+ */
+ private enum State
+ {
+ UNSELECTED, SELECTED, EDITING,
+ // E prefix <=> File has error
+ E_UNSELECTED, E_SELECTED, E_EDITING;
+
+ /*
+ * State transitions
+ */
+
+ public State selected()
+ {
+ switch(this)
+ {
+ case E_EDITING:
+ case E_SELECTED:
+ case E_UNSELECTED:
+ return State.E_SELECTED;
+ default:
+ return State.SELECTED;
+ }
+ }
+
+ public State unselected()
+ {
+ switch(this)
+ {
+ case E_EDITING:
+ case E_SELECTED:
+ case E_UNSELECTED:
+ return State.E_UNSELECTED;
+ default:
+ return State.UNSELECTED;
+ }
+ }
+
+ public State editing()
+ {
+ switch(this)
+ {
+ case E_EDITING:
+ case E_SELECTED:
+ case E_UNSELECTED:
+ return State.E_EDITING;
+ default:
+ return State.EDITING;
+ }
+ }
+
+ public State withError()
+ {
+ switch(this)
+ {
+ case EDITING:
+ case E_EDITING:
+ return State.E_EDITING;
+ case SELECTED:
+ case E_SELECTED:
+ return State.E_SELECTED;
+ default:
+ return State.E_UNSELECTED;
+ }
+ }
+
+ public State withoutError()
+ {
+ switch(this)
+ {
+ case EDITING:
+ case E_EDITING:
+ return State.EDITING;
+ case SELECTED:
+ case E_SELECTED:
+ return State.SELECTED;
+ default:
+ return State.UNSELECTED;
+ }
+ }
+
+ /*
+ * State dependent component properties
+ */
+
+ public Color getBackgroundColor()
+ {
+ switch(this)
+ {
+ case E_SELECTED:
+ case E_EDITING:
+ return new Color(0xFF6666);
+ case E_UNSELECTED:
+ return new Color(0xFFCCCC);
+ case SELECTED:
+ case EDITING:
+ return new Color(0xDBEBFF);
+ case UNSELECTED:
+ default:
+ return new Color(0xA8CFFF);
+
+ }
+ }
+
+ public Color getBorderColor()
+ {
+ switch(this)
+ {
+ case E_SELECTED:
+ case E_EDITING:
+ return new Color(0xFF4242);
+ case E_UNSELECTED:
+ return new Color(0xFFE0E0);
+ case UNSELECTED:
+ return new Color(0x5BA4FE);
+ default:
+ return new Color(0xEAF4FF);
+
+ }
+ }
+
+ public int getBorderThickness()
+ {
+ switch(this)
+ {
+ case UNSELECTED:
+ case E_UNSELECTED:
+ return 1;
+ default:
+ return 2;
+ }
+ }
+
+ public boolean isSelected()
+ {
+ switch(this)
+ {
+ case E_SELECTED:
+ case SELECTED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public boolean isEditing()
+ {
+ switch(this)
+ {
+ case E_EDITING:
+ case EDITING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public boolean hasError()
+ {
+ switch(this)
+ {
+ case E_UNSELECTED:
+ case E_SELECTED:
+ case E_EDITING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ }
+
+ /*
+ * Request handler
+ *
+ * Note : this component does not change its state alone.
+ * It only asks the handler to handle some user requests.
+ * It is up to the handler to change the state of this component, based on the result of the request.
+ */
+
+ ItemRequestHandler handler;
+
+ /*
+ * Components
+ */
+
+ private JButton openButton = new JButton();
+ private JButton closeButton = new JButton();
+ private JButton editButton = new JButton();
+ private JButton removeButton = new JButton();
+ private JButton confirmButton = new JButton();
+ private JLabel errorIcon = new JLabel();
+ private JTextField textField = new JTextField();
+ private JLabel messageLabel = new JLabel();
+
+ /*
+ * Constructor & Layout
+ */
+
+ public FilesListItem()
+ {
+
+ closeButton.setIcon(createImageIcon("close_icon2.png", 20, 20));
+ editButton.setIcon(createImageIcon("5_content_edit.png", 20, 20));
+ removeButton.setIcon(createImageIcon("5_content_discard.png", 20, 20));
+ confirmButton.setIcon(createImageIcon("1_navigation_accept.png", 20, 20));
+ errorIcon.setIcon(createImageIcon("11_alerts_and_states_error.png", 20, 20));
+
+ initComponents();
+ initLayout();
+ validate();
+ initComponentListener();
+ }
+
+ public FilesListItem(String fileName, ItemRequestHandler handler)
+ {
+ setFileName(fileName);
+ setRequestHandler(handler);
+ initComponents();
+ initLayout();
+ validate();
+ initComponentListener();
+ }
+
+ private void initLayout()
+ {
+ try
+ {
+ // Apparently, these properties were not set automatically
+ Font font = (Font) UIManager.get("defaultFont");
+ openButton.setFont(font);
+ closeButton.setFont(font);
+ messageLabel.setFont(font);
+ textField.setFont(font);
+ }catch(ClassCastException ignore) {}
+
+ this.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
+ this.setLayout(new GridBagLayout());
+ GridBagConstraints c = new GridBagConstraints();
+
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ add(errorIcon, c);
+ add(removeButton, c);
+
+ c.weightx=1; // This is the only one which should extend
+ c.gridx = 1;
+ add(openButton, c);
+ add(closeButton, c);
+ add(textField, c);
+
+ c.weightx=0;
+ c.gridx = 2;
+ add(messageLabel, c);
+
+ c.gridx = 3;
+ add(editButton, c);
+ add(confirmButton, c);
+ }
+
+ private void initComponents()
+ {
+ // The background of this JPanel will be visible below.
+ // => state dependent background color must only be set once
+ openButton.setOpaque(false);
+ closeButton.setOpaque(false);
+ editButton.setOpaque(false);
+ removeButton.setOpaque(false);
+ confirmButton.setOpaque(false);
+ errorIcon.setOpaque(false);
+ messageLabel.setOpaque(false);
+ }
+
+ private void initComponentListener()
+ {
+ openButton.addActionListener(new ActionListener(){
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ if (handler != null)
+ handler.openRequest(openButton.getText());
+ }
+ });
+
+ closeButton.addActionListener(new ActionListener(){
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ if (handler != null)
+ handler.closeRequest(openButton.getText());
+ }
+ });
+
+ editButton.addActionListener(new ActionListener(){
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ state = state.editing();
+ validate();
+ }
+ });
+
+ removeButton.addActionListener(new ActionListener(){
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ if (handler != null)
+ handler.deleteRequest(openButton.getText());
+ }
+ });
+
+ confirmButton.addActionListener(new ActionListener(){
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ String oldName = openButton.getText();
+ String newName = textField.getText();
+
+ if (handler != null && !oldName.equals(newName))
+ handler.renameRequest(oldName, newName);
+ else
+ {
+ state = state.selected();
+ validate();
+ }
+ }
+ });
+
+ textField.addActionListener(new ActionListener(){
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ String oldName = openButton.getText();
+ String newName = textField.getText();
+
+ if (handler != null && !oldName.equals(newName))
+ handler.renameRequest(oldName, newName);
+ else
+ {
+ state = state.selected();
+ validate();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void validate()
+ {
+ super.validate();
+ // First the properties of components are set before super.validate()
+ // Otherwise the changes are not validated, are they?
+ boolean isSelected = state.isSelected();
+ boolean isEditing = state.isEditing();
+ boolean hasError = state.hasError();
+
+ openButton.setVisible(!isSelected && !isEditing);
+ closeButton.setVisible(isSelected);
+ editButton.setVisible(isSelected && isEditEnabled);
+
+ confirmButton.setVisible(isEditing);
+ textField.setVisible(isEditing);
+ removeButton.setVisible(isEditing);
+
+ errorIcon.setVisible(hasError && !isEditing);
+
+ Color backgroundColor = state.getBackgroundColor();
+ setBackground(backgroundColor);
+
+ this.setBorder(BorderFactory.createLineBorder(state.getBorderColor(), state.getBorderThickness()) );
+
+ }
+
+ /*
+ * IFilesListItem
+ */
+
+ @Override
+ public JComponent getComponent()
+ {
+ return this;
+ }
+
+ @Override
+ public void setRequestHandler(ItemRequestHandler handler)
+ {
+ this.handler = handler;
+ validate();
+ }
+
+ @Override
+ public void setFileName(String fileName)
+ {
+ this.openButton.setText(fileName);
+ this.closeButton.setText(fileName);
+ this.textField.setText(fileName);
+ validate();
+ }
+
+ @Override
+ public void setSelected(boolean isSelected)
+ {
+ state = isSelected ? state.selected() : state.unselected();
+ invalidate();
+ validate();
+ }
+
+
+ @Override
+ public void setEditing(boolean isEditing)
+ {
+ state = isEditing ? state.editing() : state.selected();
+ validate();
+ }
+
+ /**
+ * This is used to display the clock per file item in XLogo4Schools
+ * Generally, this can be any message.
+ * @param msg : if null, the message will be hidden, otherwise it is displayed
+ */
+ @Override
+ public void setMessage(String msg)
+ {
+ messageLabel.setVisible(msg != null);
+ messageLabel.setText(msg);
+ validate();
+ }
+
+
+ public String getMessage()
+ {
+ return messageLabel.getText();
+ }
+
+
+ @Override
+ public void setError(boolean hasError)
+ {
+ state = hasError ? state.withError() : state.withoutError();
+ validate();
+ }
+
+
+ public void setEditEnabled(boolean enabled)
+ {
+ isEditEnabled = enabled;
+ validate();
+ }
+
+ /*
+ * Helper
+ */
+ private ImageIcon createImageIcon(String path, int width, int heigth) {
+ Image img = Toolkit.getDefaultToolkit().getImage(getClass().getResource(path));
+ return new ImageIcon(img.getScaledInstance(width, heigth, Image.SCALE_SMOOTH));
+ }
+}
diff --git a/logo/src/xlogo/gui/components/fileslist/IFilesListItem.java b/logo/src/xlogo/gui/components/fileslist/IFilesListItem.java
new file mode 100644
index 0000000..684ec96
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/IFilesListItem.java
@@ -0,0 +1,60 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.components.fileslist;
+
+import javax.swing.JComponent;
+
+public interface IFilesListItem
+{
+ public JComponent getComponent();
+
+ public void setSelected(boolean isSelected);
+
+ public void setEditing(boolean isSelected);
+
+ public void setMessage(String msg);
+
+ public void setError(boolean hasError);
+
+ public void setFileName(String string);
+
+ public void setRequestHandler(ItemRequestHandler handler);
+
+ public void setEditEnabled(boolean enabled);
+
+ public interface ItemRequestHandler
+ {
+ public void openRequest(String fileName);
+
+ public void closeRequest(String fileName);
+
+ public void renameRequest(String oldName, String newName);
+
+ public void deleteRequest(String fileName);
+ }
+}
diff --git a/logo/src/xlogo/gui/components/fileslist/close_icon2.png b/logo/src/xlogo/gui/components/fileslist/close_icon2.png
new file mode 100644
index 0000000..8892697
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/close_icon2.png
Binary files differ
diff --git a/logo/src/xlogo/gui/components/fileslist/new_content.png b/logo/src/xlogo/gui/components/fileslist/new_content.png
new file mode 100644
index 0000000..884c9d2
--- /dev/null
+++ b/logo/src/xlogo/gui/components/fileslist/new_content.png
Binary files differ
diff --git a/logo/src/xlogo/gui/preferences/AbstractPanelColor.java b/logo/src/xlogo/gui/preferences/AbstractPanelColor.java
new file mode 100644
index 0000000..e580526
--- /dev/null
+++ b/logo/src/xlogo/gui/preferences/AbstractPanelColor.java
@@ -0,0 +1,143 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui.preferences;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JColorChooser;
+import javax.swing.JComboBox;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.ListCellRenderer;
+
+import xlogo.Logo;
+import xlogo.kernel.DrawPanel;
+import xlogo.storage.WSManager;
+
+public abstract class AbstractPanelColor extends JPanel implements ActionListener{
+ private static final long serialVersionUID = 1L;
+// private ImageIcon[] images=new ImageIcon[17];
+ private Integer[] intArray=new Integer[17];
+ private JButton bchoisir=new JButton(Logo.messages.getString("pref.highlight.other"));
+ protected JComboBox combo_couleur;
+ private Color couleur_perso=Color.WHITE;
+ public AbstractPanelColor(Color c) {
+ setBackground(Color.blue);
+ for(int i=0;i<17;i++){
+ intArray[i]=new Integer(i);
+ }
+ combo_couleur = new JComboBox(intArray);
+ ComboBoxRenderer renderer= new ComboBoxRenderer();
+ combo_couleur.setRenderer(renderer);
+ setColorAndStyle(c);
+ combo_couleur.setActionCommand("combo");
+ combo_couleur.addActionListener(this);
+ bchoisir.setFont(WSManager.getWorkspaceConfig().getFont());
+ bchoisir.setActionCommand("bouton");
+ bchoisir.addActionListener(this);
+ add(combo_couleur);
+ add(bchoisir);
+ }
+ public void setEnabled(boolean b){
+ super.setEnabled(b);
+ combo_couleur.setEnabled(b);
+ bchoisir.setEnabled(b);
+
+ }
+ void setColorAndStyle(Color rgb){
+ int index=-1;
+ for(int i=0;i<17;i++){
+ if (DrawPanel.defaultColors[i].equals(rgb)){
+ index=i;
+ }
+ }
+ if (index==-1){couleur_perso=rgb;index=7;}
+ combo_couleur.setSelectedIndex(index);
+ }
+ abstract public void actionPerformed(ActionEvent e);
+
+ private class ComboBoxRenderer extends JPanel
+ implements ListCellRenderer {
+ private static final long serialVersionUID = 1L;
+ int id=0;
+ public ComboBoxRenderer() {
+ setOpaque(true);
+ setPreferredSize(new Dimension(50,20));
+ }
+
+ public Component getListCellRendererComponent(
+ JList list,
+ Object value,
+ int index,
+ boolean isSelected,
+ boolean cellHasFocus) {
+//Get the selected index. (The index param isn't
+//always valid, so just use the value.)
+ int selectedIndex = ((Integer)value).intValue();
+ this.id=selectedIndex;
+ if (isSelected) {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ } else {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+ //Set the icon and text. If icon was null, say so.
+
+ setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
+ return this;
+ }
+ public void paint(Graphics g){
+ super.paint(g);
+ if (id!=7) g.setColor(DrawPanel.defaultColors[id]);
+ else g.setColor(couleur_perso);
+ g.fillRect(5,2,40,15);
+ }
+ }
+ public Color getValue(){
+ int id=combo_couleur.getSelectedIndex();
+ if (id!=7) return DrawPanel.defaultColors[id];
+ return couleur_perso;
+ }
+ protected void actionButton(){
+ Color color=JColorChooser.showDialog(this,"",DrawPanel.defaultColors[combo_couleur.getSelectedIndex()]);
+ if (null!=color){
+ couleur_perso=color;
+ combo_couleur.setSelectedIndex(7);
+ combo_couleur.repaint();
+ }
+ }
+}
diff --git a/logo/src/xlogo/gui/preferences/PanelColor.java b/logo/src/xlogo/gui/preferences/PanelColor.java
new file mode 100644
index 0000000..2fbfe18
--- /dev/null
+++ b/logo/src/xlogo/gui/preferences/PanelColor.java
@@ -0,0 +1,45 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq
+ */
+
+package xlogo.gui.preferences;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+
+public class PanelColor extends AbstractPanelColor{
+
+ private static final long serialVersionUID = 1L;
+ public PanelColor(Color c){
+ super(c);
+ }
+ public void actionPerformed(ActionEvent e){
+ String cmd=e.getActionCommand();
+ if (cmd.equals("bouton")){
+ actionButton();
+ }
+ }
+}
diff --git a/logo/src/xlogo/gui/translation/BottomPanel.java b/logo/src/xlogo/gui/translation/BottomPanel.java
new file mode 100644
index 0000000..bcd98e3
--- /dev/null
+++ b/logo/src/xlogo/gui/translation/BottomPanel.java
@@ -0,0 +1,89 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings migh have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui.translation;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import java.awt.BorderLayout;
+import xlogo.Logo;
+import xlogo.utils.Utils;
+public class BottomPanel extends JPanel {
+ private static final long serialVersionUID = 1L;
+ private TranslateXLogo tx;
+ private JTabbedPane jt;
+ private MyTable messageTable;
+ private MyTable primTable;
+ private String id;
+ private String action;
+ private JButton searchButton;
+ private ImageIcon ichercher=Utils.dimensionne_image("chercher.png",this);
+
+ protected BottomPanel(TranslateXLogo tx,String action, String id){
+ this.tx=tx;
+ this.action=action;
+ this.id=id;
+ initGui();
+ }
+ private void initGui(){
+ setLayout(new BorderLayout());
+ jt= new JTabbedPane();
+ messageTable=new MyTable(tx,action,id,"langage");
+ primTable=new MyTable(tx,action,id,"primitives");
+ jt.add(primTable,Logo.messages.getString("primitives"));
+ jt.add(messageTable,Logo.messages.getString("messages"));
+ javax.swing.JScrollPane scroll=new javax.swing.JScrollPane(jt);
+
+ add(scroll,BorderLayout.CENTER);
+ searchButton=new JButton(ichercher);
+ searchButton.setToolTipText(Logo.messages.getString("find"));
+ searchButton.addActionListener(tx);
+ searchButton.setActionCommand(TranslateXLogo.SEARCH);
+ searchButton.setSize(new java.awt.Dimension(100,50));
+ add(searchButton,BorderLayout.EAST);
+ }
+ protected String getPrimValue(int a, int b){
+ return primTable.getValue(a, b);
+ }
+ protected String getMessageValue(int a, int b){
+ String st=messageTable.getValue(a, b);
+ return st;
+ }
+ protected MyTable getMessageTable(){
+ return this.messageTable;
+ }
+ protected MyTable getPrimTable(){
+ return this.primTable;
+ }
+ protected MyTable getVisibleTable(){
+ if (jt.getSelectedIndex()==0) return primTable;
+ return messageTable;
+ }
+}
diff --git a/logo/src/xlogo/gui/translation/FirstPanel.java b/logo/src/xlogo/gui/translation/FirstPanel.java
new file mode 100644
index 0000000..1fc0309
--- /dev/null
+++ b/logo/src/xlogo/gui/translation/FirstPanel.java
@@ -0,0 +1,237 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings migh have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui.translation;
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.ButtonGroup;
+import javax.swing.JRadioButton;
+import javax.swing.JButton;
+import javax.swing.ListCellRenderer;
+import javax.swing.JComboBox;
+
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.MediaTracker;
+import java.awt.Toolkit;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+
+import java.awt.event.*;
+public class FirstPanel extends JPanel implements ActionListener{
+ private static final long serialVersionUID = 1L;
+ private Integer[] intArray;
+
+
+ private JRadioButton consultButton;
+ private JRadioButton modifyButton;
+ private JRadioButton completeButton;
+ private JRadioButton createButton;
+ private JButton validButton;
+ private ButtonGroup group=new ButtonGroup();
+ private JLabel label;
+ private JComboBox comboLangModify;
+ private JComboBox comboLangComplete;
+// private JTextField textLang;
+ private TranslateXLogo tx;
+ protected FirstPanel(TranslateXLogo tx){
+ this.tx=tx;
+ int n=Logo.translationLanguage.length;
+ intArray=new Integer[n];
+ for(int i=0;i<n;i++){
+ intArray[i]=new Integer(i);
+ }
+ initGui();
+ }
+ protected String getAction(){
+ if (modifyButton.isSelected()) return TranslateXLogo.MODIFY;
+ else if (completeButton.isSelected()) return TranslateXLogo.COMPLETE;
+ else if (consultButton.isSelected()) return TranslateXLogo.CONSULT;
+ else if (createButton.isSelected()) return TranslateXLogo.CREATE;
+ return null;
+ }
+ protected String getLang(){
+ if (modifyButton.isSelected()) return String.valueOf(comboLangModify.getSelectedIndex());
+ return String.valueOf(comboLangComplete.getSelectedIndex());
+ }
+/* protected String getNewLang(){
+ return textLang.getText();
+ }*/
+ private void initGui(){
+
+ setLayout(new GridBagLayout());
+
+ // textLang=new JTextField();
+ label=new JLabel(Logo.messages.getString("translatewant"));
+ createButton=new JRadioButton(Logo.messages.getString("translatecreate"));
+ modifyButton=new JRadioButton(Logo.messages.getString("translatemodify"));
+ completeButton=new JRadioButton(Logo.messages.getString("translatecomplete"));
+ consultButton=new JRadioButton(Logo.messages.getString("translateconsult"));
+ createButton.setActionCommand(TranslateXLogo.CREATE);
+ modifyButton.setActionCommand(TranslateXLogo.MODIFY);
+ consultButton.setActionCommand(TranslateXLogo.CONSULT);
+ completeButton.setActionCommand(TranslateXLogo.COMPLETE);
+ createButton.addActionListener(this);
+ completeButton.addActionListener(this);
+ modifyButton.addActionListener(this);
+ consultButton.addActionListener(this);
+ comboLangModify=new JComboBox(intArray);
+ comboLangModify.setRenderer(new Contenu());
+ comboLangComplete=new JComboBox(intArray);
+ comboLangComplete.setRenderer(new Contenu());
+ validButton=new JButton(Logo.messages.getString("pref.ok"));
+ validButton.setActionCommand(TranslateXLogo.OK);
+ validButton.addActionListener(tx);
+ setSize(new java.awt.Dimension(600,120));
+ validButton.setSize(new java.awt.Dimension(100,50));
+ // textLang.setSize(new java.awt.Dimension(100,20));
+
+ group.add(createButton);
+ group.add(modifyButton);
+ group.add(completeButton);
+ group.add(consultButton);
+
+ add(label, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(
+ 0,0,0,0), 0, 0));
+ add(createButton, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(
+ 0,0,0,0), 0, 0));
+ //add(textLang, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0,
+ // GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(
+ // 0,0,0,0), 0, 0));
+ add(modifyButton, new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(
+ 0,0,0,0), 0, 0));
+ add(comboLangModify, new GridBagConstraints(1, 2, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(
+ 0,0,0,0), 0, 0));
+ add(completeButton, new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(
+ 0,0,0,0), 0, 0));
+ add(comboLangComplete, new GridBagConstraints(1, 3, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(
+ 0,0,0,0), 0, 0));
+ add(consultButton, new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(
+ 0,0,0,0), 0, 0));
+ add(validButton, new GridBagConstraints(2, 5, 1, 1, 1.0, 1.0,
+ GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(
+ 0,0,0,0), 0, 0));
+ comboLangComplete.setVisible(false);
+ comboLangModify.setVisible(false);
+ // textLang.setVisible(false);
+
+
+
+ }
+ public void actionPerformed(ActionEvent e){
+ String cmd=e.getActionCommand();
+ if (cmd.equals(TranslateXLogo.MODIFY)){
+ comboLangComplete.setVisible(false);
+ comboLangModify.setVisible(true);
+ // textLang.setVisible(false);
+ }
+ else if (cmd.equals(TranslateXLogo.CREATE)){
+ comboLangComplete.setVisible(false);
+ comboLangModify.setVisible(false);
+ //textLang.setVisible(true);
+ //textLang.validate();
+ }
+ else if (cmd.equals(TranslateXLogo.COMPLETE)){
+ comboLangComplete.setVisible(true);
+ comboLangModify.setVisible(false);
+ //textLang.setVisible(false);
+ }
+ else if (cmd.equals(TranslateXLogo.CONSULT)){
+ comboLangComplete.setVisible(false);
+ comboLangModify.setVisible(false);
+ //textLang.setVisible(false);
+ }
+ }
+
+
+
+ class Contenu extends JLabel implements ListCellRenderer{
+ private static final long serialVersionUID = 1L;
+ private ImageIcon[] drapeau;
+
+ Contenu(){
+ drapeau=new ImageIcon[Logo.translationLanguage.length];
+ cree_icone();
+ }
+ void cree_icone(){
+ for (int i=0;i<drapeau.length;i++){
+ Image image=null;
+ image= Toolkit.getDefaultToolkit().getImage(Utils.class.getResource("drapeau"+i+".png"));
+ MediaTracker tracker=new MediaTracker(this);
+ tracker.addImage(image,0);
+ try{tracker.waitForID(0);}
+ catch(InterruptedException e1){}
+ int largeur=image.getWidth(this);
+ int hauteur=image.getHeight(this);
+ double facteur = (double) WSManager.getWorkspaceConfig().getFont().getSize()/(double)hauteur;
+ image=image.getScaledInstance((int)(facteur*largeur),(int)(facteur*hauteur),Image.SCALE_SMOOTH);
+ tracker=new MediaTracker(this);
+ tracker.addImage(image,0);
+ try{tracker.waitForID(0);}
+ catch(InterruptedException e1){}
+ drapeau[i]=new ImageIcon();
+ drapeau[i].setImage(image);
+// drapeau[i]=new ImageIcon(image);
+ }
+
+ }
+ public Component getListCellRendererComponent(JList list, Object value,int
+ index, boolean isSelected,boolean cellHasFocus){
+ setOpaque(true);
+ int selectedIndex = ((Integer)value).intValue();
+ setText(Logo.translationLanguage[selectedIndex]);
+ setIcon(drapeau[selectedIndex]);
+ if (isSelected) {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground()); }
+ else{
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+ setBorder(BorderFactory.createEmptyBorder(5,0,5,5));
+// setEnabled(list.isEnabled());
+ return(this);
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/gui/translation/MyTable.java b/logo/src/xlogo/gui/translation/MyTable.java
new file mode 100644
index 0000000..9976a09
--- /dev/null
+++ b/logo/src/xlogo/gui/translation/MyTable.java
@@ -0,0 +1,375 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings migh have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui.translation;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.awt.Component;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Collections;
+
+import javax.swing.ListSelectionModel;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.AbstractCellEditor;
+import javax.swing.table.TableCellEditor;
+import javax.swing.JTextArea;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.event.*;
+import javax.swing.JScrollPane;
+
+import java.awt.event.*;
+
+import xlogo.gui.Searchable;
+import xlogo.storage.workspace.Language;
+import xlogo.Logo;
+public class MyTable extends JPanel implements Searchable{
+ private static final long serialVersionUID = 1L;
+ private JTable table;
+ private JScrollPane scrollPane;
+ private TranslateXLogo tx;
+ private String id;
+ private String action;
+ private String bundle;
+ private Vector<String> keys;
+ protected MyTable(TranslateXLogo tx, String action, String id,String bundle){
+ this.tx=tx;
+ this.action=action;
+ this.id=id;
+ this.bundle=bundle;
+ initGui();
+ }
+
+ public void setColumnSize(){
+ for (int i = 0 ; i < table.getColumnCount() ; i++)
+ {
+ table.getColumnModel().getColumn(i).setPreferredWidth(200);
+ }
+ }
+ protected String getValue(int a, int b){
+ return table.getValueAt(a, b).toString();
+ }
+ private void initGui(){
+ setLayout(new java.awt.BorderLayout());
+ table=new JTable(new MyModel(bundle,action,id));
+
+ MultiLineCellEditor editor = new MultiLineCellEditor(table);
+ table.setDefaultEditor(String.class,editor);
+
+ table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ scrollPane = new JScrollPane(table);
+ //table.setFillsViewportHeight(true);
+ // Only one selection is possible
+ table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ // You can only select Cell
+ table.setCellSelectionEnabled (true);
+ setColumnSize();
+ add(scrollPane,java.awt.BorderLayout.CENTER);
+
+ }
+ protected Vector<String> getKeys(){
+ return keys;
+ }
+ class MultiLineCellEditor extends AbstractCellEditor implements TableCellEditor {
+ private static final long serialVersionUID = 1L;
+ MyTextArea textArea;
+ JTable table;
+
+ public MultiLineCellEditor(JTable ta) {
+ super();
+ table = ta;
+ // this component relies on having this renderer for the String class
+ MultiLineCellRenderer renderer = new MultiLineCellRenderer();
+ table.setDefaultRenderer(String.class,renderer);
+// JOptionPane.showMessageDialog(null,"jui ds l'editor","h",JOptionPane.INFORMATION_MESSAGE);
+ textArea = new MyTextArea();
+ textArea.setLineWrap(true);
+ textArea.setWrapStyleWord(true);
+ for(int i=0;i<table.getRowCount();i++) updateRow(i);
+ }
+
+ /** This method determines the height in pixel of a cell given the text it contains */
+ private int cellHeight(int row,int col) {
+ if (row == table.getEditingRow() && col == table.getEditingColumn())
+ return textArea.getPreferredSize().height;
+ else
+ return table.getDefaultRenderer(String.class).getTableCellRendererComponent(table,
+ table.getModel().getValueAt(row,col),false,false,row,col).getPreferredSize().height;
+ }
+
+ void cellGrewEvent(int row,int column) {
+ updateRow(row);
+ }
+
+ void cellShrankEvent(int row,int column) {
+ updateRow(row);
+ }
+
+ void updateRow(int row) {
+ int maxHeight = 0;
+ for(int j=0;j<table.getColumnCount();j++) {
+ int ch;
+ if ((ch = cellHeight(row,j)) > maxHeight) {
+ maxHeight = ch;
+ }
+ }
+ table.setRowHeight(row,maxHeight);
+ }
+
+ public Object getCellEditorValue() {
+ return textArea.getText();
+ }
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
+ int row, int column) {
+ textArea.setText(table.getValueAt(row,column).toString());
+ textArea.rowEditing = row;
+ textArea.columnEditing = column;
+ textArea.lastPreferredHeight = textArea.getPreferredSize().height;
+
+ return textArea;
+
+ }
+
+ class MyTextArea extends JTextArea implements KeyListener {
+ private static final long serialVersionUID = 1L;
+ int lastPreferredHeight = 0;
+ int rowEditing;
+ int columnEditing;
+
+ MyTextArea() {
+ addKeyListener(this);
+ // This is a fix to Bug Id 4256006
+ addAncestorListener( new AncestorListener(){
+ public void ancestorAdded(AncestorEvent e){
+ requestFocus();
+ }
+ public void ancestorMoved(AncestorEvent e){}
+ public void ancestorRemoved(AncestorEvent e){}
+ });
+ }
+
+ public void keyPressed(KeyEvent e) {}
+ public void keyReleased(KeyEvent e) {}
+ public void keyTyped(KeyEvent e) {
+ if (getPreferredSize().getHeight() > lastPreferredHeight) {
+ lastPreferredHeight = getPreferredSize().height;
+ cellGrewEvent(rowEditing,columnEditing);
+ // this will trigger the addition of extra lines upon the cell growing and
+ // prevent all the text being lost when the cell grows to the point of requiring
+ // scrollbars
+ table.setValueAt(getText(),rowEditing,columnEditing);
+ }
+ else if (getPreferredSize().getHeight() < lastPreferredHeight) {
+ lastPreferredHeight = getPreferredSize().height;
+ cellShrankEvent(rowEditing,columnEditing);
+ }
+ else if (table.getValueAt(rowEditing,columnEditing).equals(""))
+ table.setValueAt(getText(),rowEditing,columnEditing);
+
+ }
+ }
+ }
+
+ class MyModel extends AbstractTableModel {
+ private static final long serialVersionUID = 1L;
+ private String[] columnNames;
+ private String[][] rowData;
+ String action;
+ String id;
+
+ MyModel(String bundle,String action, String id){
+ this.action=action;
+ this.id=id;
+
+ if (action.equals(TranslateXLogo.CREATE)){
+ // Initilaize all Column Names
+ String[] tmp=Logo.translationLanguage;
+ columnNames=new String[tmp.length+1];
+ columnNames[0]=id;
+ for (int i=1;i<columnNames.length;i++){
+ columnNames[i]=tmp[i-1];
+ }
+ }
+ else columnNames=Logo.translationLanguage;
+ buildRowData(bundle, action,id);
+ }
+ public String getColumnName(int col) {
+ return columnNames[col].toString();
+ }
+ public int getRowCount() { return rowData.length; }
+ public int getColumnCount() { return columnNames.length; }
+ public Object getValueAt(int row, int col) {
+ return rowData[row][col];
+ }
+ public boolean isCellEditable(int row, int col)
+ {
+ if (action.equals(TranslateXLogo.CONSULT)) return false;
+ else if (action.equals(TranslateXLogo.CREATE)) {
+ if (col==0) return true;
+ else return false;
+ }
+ else if (action.equals(TranslateXLogo.MODIFY)) {
+ if (col==Integer.parseInt(id)) return true;
+ else return false;
+ }
+ else if (action.equals(TranslateXLogo.COMPLETE)) {
+ if (col==Integer.parseInt(id)) {
+
+ if (rowData[row][col].equals("")) return true;
+ else return false;
+ }
+ else return false;
+ }
+ return true; }
+ public void setValueAt(Object value, int row, int col) {
+ rowData[row][col] = value.toString();
+ fireTableCellUpdated(row, col);
+ }
+ public Class<? extends Object> getColumnClass(int c) {
+ return getValueAt(0, c).getClass();
+
+ }
+
+ private void buildRowData(String bundle,String action, String id){
+ ResourceBundle[] rb=new ResourceBundle[getColumnCount()];
+ // initialize all ResourceBundle
+ for(int i=0;i<getColumnCount();i++){
+ Locale locale = Language.getLanguage(i).getLocale();
+ // In CREATE Mode, when i=getColumnCount(), the last locale is null
+ if (null==locale) break;
+ rb[i] = ResourceBundle.getBundle(bundle, locale);
+ }
+ keys=new Vector<String>();
+ Enumeration<String> en=rb[0].getKeys();
+ while(en.hasMoreElements()){
+ String value=en.nextElement();
+ keys.add(value);
+ }
+ Collections.sort(keys);
+ rowData=new String[keys.size()][getColumnCount()];
+ int row=0;
+ for (int j=0;j<keys.size();j++) {
+ String key=keys.get(j).toString();
+
+ for(int i=0;i<getColumnCount();i++){
+ if (action.equals(TranslateXLogo.CREATE)){
+ if (i==0) rowData[row][0]="";
+ else rowData[row][i]=rb[i-1].getString(key);
+ }
+ else {
+ String element=rb[i].getString(key);
+ if (element.equals("")) {
+ rowData[row][i]="";
+ }
+ else rowData[row][i]=element;
+ }
+ }
+ row++;
+ }
+ }
+ }
+ class MultiLineCellRenderer extends JTextArea implements TableCellRenderer {
+ private static final long serialVersionUID = 1L;
+ public MultiLineCellRenderer() {
+ setEditable(false);
+ setLineWrap(true);
+ setWrapStyleWord(true);
+ }
+
+ public Component getTableCellRendererComponent(JTable table,Object value,boolean isSelected, boolean hasFocus, int row, int column) {
+
+ if (value instanceof String) {
+ if (value.toString().equals("")) setBackground(new java.awt.Color(255,200,200));
+ else setBackground(java.awt.Color.WHITE);
+ setText((String)value);
+ if (isSelected) setBackground(java.awt.Color.LIGHT_GRAY);
+ // set the table's row height, if necessary
+ //updateRowHeight(row,getPreferredSize().height);
+ }
+ else
+ setText("");
+ return this;
+ }
+ }
+ int selectedRow=0;
+ int selectedColumn=0;
+ public boolean find(String element,boolean forward){
+ int row=table.getSelectedRow();
+ int col=table.getSelectedColumn();
+ if (row!=-1) {
+ if (row==table.getRowCount()-1) {
+ selectedRow=0;
+ if (col==table.getColumnCount()-1) selectedColumn=0;
+ else selectedColumn=col+1;
+ }
+ else selectedRow=row+1;
+ }
+ int start=selectedRow;
+ for (int c=selectedColumn;c<table.getColumnCount();c++){
+ for(int r=start;r<table.getRowCount();r++){
+ String value=table.getValueAt(r, c).toString();
+ int index=value.indexOf(element);
+ if (index!=-1) {
+ table.clearSelection();
+ if (r==table.getRowCount()-1){
+ selectedRow=0;
+ if (c==table.getColumnCount()-1) selectedColumn=0;
+ else selectedColumn=c+1;
+ }
+ else {
+ selectedRow=r+1;
+ selectedColumn=c;
+ }
+ boolean b=table.editCellAt(r, c);
+ if (!b) {
+ table.changeSelection(r, c,false,false);
+ }
+// System.out.println(selectedRow+" "+selectedColumn);
+ return true;
+ }
+ }
+ start=0;
+ }
+ selectedColumn=0;
+ selectedRow=0;
+ table.changeSelection(0, 0,false,false);
+ return false;
+ }
+ public void replace(String element, boolean forward){
+
+ }
+ public void replaceAll(String element, String substitute){
+
+ }
+ public void removeHighlight(){
+ tx.resetSearchFrame();
+ }
+
+}
diff --git a/logo/src/xlogo/gui/translation/TopPanel.java b/logo/src/xlogo/gui/translation/TopPanel.java
new file mode 100644
index 0000000..239398b
--- /dev/null
+++ b/logo/src/xlogo/gui/translation/TopPanel.java
@@ -0,0 +1,63 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings migh have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui.translation;
+import java.awt.FlowLayout;
+import java.awt.Dimension;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.JButton;
+import xlogo.Logo;
+public class TopPanel extends JPanel {
+ private static final long serialVersionUID = 1L;
+ private TranslateXLogo tx;
+ private JTextArea area;
+ private JButton sendButton;
+ protected TopPanel(TranslateXLogo tx){
+ this.tx=tx;
+ initGui();
+ }
+ private void initGui(){
+ setLayout(new FlowLayout());
+ area=new JTextArea(Logo.messages.getString("translatemessage"));
+ area.setWrapStyleWord(true);
+ area.setLineWrap(true);
+ sendButton=new JButton(Logo.messages.getString("pref.ok"));
+
+
+ area.setEditable(false);
+ sendButton.addActionListener(tx);
+ sendButton.setActionCommand(TranslateXLogo.SEND);
+
+
+ area.setSize(new Dimension(400,100));
+ sendButton.setSize(new Dimension(50,30));
+ add(area);
+ add(sendButton);
+ }
+}
diff --git a/logo/src/xlogo/gui/translation/TranslateXLogo.java b/logo/src/xlogo/gui/translation/TranslateXLogo.java
new file mode 100644
index 0000000..9be2d37
--- /dev/null
+++ b/logo/src/xlogo/gui/translation/TranslateXLogo.java
@@ -0,0 +1,178 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings migh have been applied by Marko Zivkovic
+ */
+
+package xlogo.gui.translation;
+
+import javax.swing.JFrame;
+import javax.swing.JFileChooser;
+
+import java.awt.BorderLayout;
+import java.awt.Toolkit;
+import java.awt.event.*;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import xlogo.Logo;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.Language;
+import xlogo.utils.Utils;
+import xlogo.gui.SearchFrame;
+/**
+ * Modifications made by Marko: <br>
+ * IO error is displayed through DialogMessenger (singleton) instead of {@link Application#ecris()}. Result: This class is completely decoupled from the Application class.
+ * @author lo�c Le Coq, slightly modified by Marko Zivkovic
+ */
+public class TranslateXLogo extends JFrame implements ActionListener {
+ private static final long serialVersionUID = 1L;
+ private String id="";
+ private String action;
+
+ protected static final String OK="ok";
+ protected static final String SEND="send";
+ protected static final String SEARCH="search";
+
+ protected static final String CONSULT="0";
+ protected static final String MODIFY="1";
+ protected static final String CREATE="2";
+ protected static final String COMPLETE="3";
+
+ private FirstPanel first;
+ private TopPanel top;
+ private BottomPanel bottom;
+
+ private SearchFrame sf=null;
+ public TranslateXLogo(){
+ initGui();
+ }
+ private void initGui(){
+ setIconImage(Toolkit.getDefaultToolkit().createImage(
+ Utils.class.getResource("icone.png")));
+ setTitle(Logo.messages.getString("menu.help.translatexlogo"));
+ first=new FirstPanel(this);
+ getContentPane().add(first);
+ setVisible(true);
+ }
+ public void actionPerformed(ActionEvent e){
+ String cmd=e.getActionCommand();
+ if (cmd.equals(TranslateXLogo.OK)){
+ action=first.getAction();
+
+ if (null==action) return;
+ else if (action.equals(TranslateXLogo.MODIFY)) id=first.getLang();
+ else if (action.equals(TranslateXLogo.COMPLETE)) id=first.getLang();
+ //else if (action.equals(TranslateXLogo.CREATE)) id=first.getNewLang();
+ remove(first);
+ bottom=new BottomPanel(this,action,id);
+ getContentPane().setLayout(new BorderLayout());
+ if (!action.equals(TranslateXLogo.CONSULT)){
+ top=new TopPanel(this);
+ getContentPane().add(top,BorderLayout.NORTH);
+
+ }
+
+ getContentPane().add(bottom,BorderLayout.CENTER);
+ this.getContentPane().validate();
+ }
+ else if (cmd.equals(TranslateXLogo.SEND)){
+ String path="";
+ JFileChooser jf=new JFileChooser(Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()));
+ int retval=jf.showDialog(this,Logo.messages
+ .getString("menu.file.save"));
+ if (retval==JFileChooser.APPROVE_OPTION){
+ path=jf.getSelectedFile().getPath();
+ StringBuffer sb=new StringBuffer();
+ try {
+ Locale locale=null;
+ if (action.equals(TranslateXLogo.CREATE)){
+ locale = Language.getLanguage(0).getLocale();
+ }
+ else if (!action.equals(TranslateXLogo.CONSULT)){
+ locale = Language.getLanguage(Integer.parseInt(id)).getLocale();
+ }
+ java.util.Vector<String> v=bottom.getPrimTable().getKeys();
+ ResourceBundle rb = ResourceBundle.getBundle("primitives", locale);
+ for (int i=0;i<v.size();i++) {
+ String key=v.get(i);
+ if (action.equals(TranslateXLogo.CREATE)){
+ writeLine(sb,key,bottom.getPrimValue(i,0));
+ }
+ else if (!action.equals(TranslateXLogo.CONSULT)){
+ String element=bottom.getPrimValue(i, Integer.parseInt(id));
+ // System.out.println(element+" clé "+key);
+ if (!rb.getString(key).equals(element)) writeLine(sb,key,element);
+ }
+ }
+ sb.append("\n---------------------------------------\n");
+ v=bottom.getMessageTable().getKeys();
+ rb = ResourceBundle.getBundle("langage", locale);
+ for(int i=0;i<v.size();i++){
+ String key=v.get(i);
+ if (action.equals(TranslateXLogo.CREATE)){
+ writeLine(sb,key,bottom.getMessageValue(i,0).replaceAll("\\n","\\\\n"));
+ }
+ else if (!action.equals(TranslateXLogo.CONSULT)){
+ String element=bottom.getMessageValue(i, Integer.parseInt(id));
+ if (!rb.getString(key).equals(element)) writeLine(sb,key,element.replaceAll("\\n","\\\\n"));
+ }
+ }
+ Utils.writeLogoFile(path,sb.toString());
+ }
+ catch(NullPointerException e3){System.out.println("annulation");} //Si l'utilisateur annule
+ catch(IOException e2){
+ DialogMessenger.getInstance().dispatchError("general.error.title", Logo.messages.getString("error.ioecriture"));
+ }
+ }
+ }
+ else if (cmd.equals(TranslateXLogo.SEARCH)){
+ if (null==sf) {
+ sf=new SearchFrame(this,bottom.getVisibleTable());
+ sf.setSize(350, 350);
+ sf.setVisible(true);
+ }
+ }
+ }
+ protected void resetSearchFrame(){
+ sf=null;
+ }
+ private void writeLine(StringBuffer sb,String key, String value){
+ sb.append(key);
+ sb.append("=");
+ sb.append(value);
+ sb.append("\n");
+
+ }
+ protected void processWindowEvent(WindowEvent e){
+ super.processWindowEvent(e);
+ if (e.getID()==WindowEvent.WINDOW_CLOSING){
+ //app.close_TranslateXLogo(); TODO Application does not need this. Maybe in settings @ welcome
+ }
+
+ }
+}
diff --git a/logo/src/xlogo/gui/welcome/WelcomeScreen.java b/logo/src/xlogo/gui/welcome/WelcomeScreen.java
new file mode 100644
index 0000000..f6e068c
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/WelcomeScreen.java
@@ -0,0 +1,518 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.JTextComponent;
+
+import java.awt.*;
+import java.io.IOException;
+
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.AsyncMediumAdapter;
+import xlogo.messages.async.AsyncMessage;
+import xlogo.messages.async.AsyncMessenger;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+import xlogo.utils.WebPage;
+import xlogo.Application;
+import xlogo.Logo;
+
+/**
+ * This was initially called {@code Selection_Langue} and it was only displayed when the Application was opened for the very first time.
+ * Now this has become {@code WelcomeScreen}, as it was enhanced with more options than just language selection:
+ * <li> User Account Selection / Creation </li>
+ * <li> Storage Location (master password required) </li>
+ * @author Marko
+ */
+public class WelcomeScreen extends JFrame {
+ private static final long serialVersionUID = 1L;
+
+ private JLabel label;
+
+ private JLabel workspace = new JLabel("Workspace");
+ private JLabel username = new JLabel("User");
+
+ private JComboBox workspaceSelection = new JComboBox();
+ private JComboBox userSelection = new JComboBox();
+
+ private JButton openWorkspaceSettingsBtn = new JButton("Settings");
+ private JButton enterButton = new JButton("Enter");
+
+ private JButton infoButton = new JButton();
+ private JButton gplButton = new JButton();
+
+ private JPanel panel = new JPanel();
+ private GroupLayout groupLayout;
+
+ private ActionListener listener;
+
+ /**
+ *
+ * @param listener to be informed when the user is ready to enter the application
+ */
+ public WelcomeScreen(ActionListener listener){
+ this.listener = listener;
+ // Window
+ super.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("Icon_x4s.png")));
+ setTitle("XLogo4Schools");
+
+ // The XLogo4Schools logo
+ //ImageIcon logo = Utils.dimensionne_image("Logo_xlogo4schools.png", this);
+
+ infoButton.setIcon(createImageIcon("info_icon.png", "Info", 40, 40));
+ gplButton.setIcon(createImageIcon("gnu_gpl.png", "GPL", 40, 40));
+ label = new JLabel(createImageIcon("Logo_xlogo4schools.png", "XLogo4Schools", 250, 40));
+
+ // Select workspace combo box
+ initWorkspaceListModel();
+ workspaceSelection.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ String workspace = (String) workspaceSelection.getSelectedItem();
+ enterWorkspace(workspace);
+ }
+ });
+ // Open workspace settings button
+ openWorkspaceSettingsBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showWorkspaceSettings();
+ }
+ });
+
+ // Select user combo box
+ populateUserList();
+ final JTextComponent tc = (JTextComponent) userSelection.getEditor().getEditorComponent();
+ tc.getDocument().addDocumentListener(new DocumentListener() {
+ public void removeUpdate(DocumentEvent arg0) { enableOrDisableEnter(); }
+ public void insertUpdate(DocumentEvent arg0) { enableOrDisableEnter(); }
+ public void changedUpdate(DocumentEvent arg0) { enableOrDisableEnter(); }
+ private void enableOrDisableEnter()
+ {
+ String username = tc.getText();
+ enterButton.setEnabled(username != null && username.length() != 0);
+ }
+ });
+
+ userSelection.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ String username = (String) userSelection.getSelectedItem();
+ enterButton.setEnabled(username != null && username.length() != 0);
+ }
+ });
+
+ // Enter user space button
+ enterButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ enterApplication();
+ }
+ });
+
+ gplButton.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e)
+ {
+ showGPL();
+ }
+ });
+
+ infoButton.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e)
+ {
+ showInfo();
+ }
+ });
+
+ // Add all
+ initLayout();
+ getContentPane().add(panel);
+ setText();
+ pack();
+ setVisible(true);
+ //MessageManager.getInstance().setParent(this);
+ setMessageManagerParent();
+ }
+
+ private void initWorkspaceListModel()
+ {
+ WSManager wsManager = WSManager.getInstance();
+ try
+ {
+ String lastUsedWorkspace = wsManager.getGlobalConfigInstance().getLastUsedWorkspace();
+ wsManager.enterWorkspace(lastUsedWorkspace);
+ populateWorkspaceList();
+ }
+ catch (IOException e)
+ {
+ DialogMessenger
+ .getInstance()
+ .dispatchMessage(
+ "I'm sorry, something very bad happened",
+ "Please report this error message. You could try to delete the file X4S_GlobalConfig from your home directory, "
+ + "and restart XLogo4Schools. You will have to import your Workspaces again.\n\n"
+ + e.toString());
+ }
+ }
+
+ private void populateWorkspaceList()
+ {
+ GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance();
+ String[] workspaces = gc.getAllWorkspaces();
+ workspaceSelection.setModel(new DefaultComboBoxModel(workspaces));
+ workspaceSelection.setSelectedItem(gc.getLastUsedWorkspace());
+ }
+
+ private void populateUserList()
+ {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ String[] users = wc.getUserList();
+ userSelection.setModel(new DefaultComboBoxModel(users));
+ String lastUser = wc.getLastActiveUser();
+ userSelection.setSelectedItem(lastUser);
+ enterButton.setEnabled(lastUser != null && lastUser.length() > 0);
+ userSelection.setEditable(wc.isUserCreationAllowed());
+ }
+
+ protected void enterWorkspace(String workspaceName) {
+ try {
+ WSManager.getInstance().enterWorkspace(workspaceName);
+ populateUserList();
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.settings.could.not.enter.wp") + "\n\n" + e.toString());
+ }
+ }
+
+ private void initLayout()
+ {
+ setResizable(false);
+ infoButton.setBorder(null);
+ gplButton.setBorder(null);
+
+ infoButton.setOpaque(false);
+ gplButton.setOpaque(false);
+
+ panel.add(workspace);
+ panel.add(username);
+ panel.add(workspaceSelection);
+ panel.add(userSelection);
+ panel.add(openWorkspaceSettingsBtn);
+ panel.add(enterButton);
+ panel.add(infoButton);
+ panel.add(gplButton);
+
+ workspaceSelection.setMinimumSize(new Dimension(200, 25));
+ userSelection.setMinimumSize(new Dimension(200, 25));
+ workspaceSelection.setMaximumSize(new Dimension(200, 25));
+ userSelection.setMaximumSize(new Dimension(200, 25));
+
+ groupLayout = new GroupLayout(panel);
+ panel.setLayout(groupLayout);
+
+ groupLayout.setAutoCreateGaps(true);
+ groupLayout.setAutoCreateContainerGaps(true);
+
+ groupLayout.setVerticalGroup(
+ groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(gplButton)
+ .addComponent(infoButton)
+ .addComponent(label))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspace)
+ .addComponent(workspaceSelection)
+ .addComponent(openWorkspaceSettingsBtn))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(username)
+ .addComponent(userSelection)
+ .addComponent(enterButton))
+ );
+
+ groupLayout.setHorizontalGroup(
+ groupLayout.createParallelGroup()
+ .addGroup(
+ groupLayout.createSequentialGroup()
+ .addComponent(label)
+ .addComponent(gplButton)
+ .addComponent(infoButton))
+ .addGroup(
+ groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspace)
+ .addComponent(username))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceSelection)
+ .addComponent(userSelection))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(openWorkspaceSettingsBtn)
+ .addComponent(enterButton))
+ )
+ );
+ }
+
+ /**
+ * Display {@link xlogo.gui.welcome.WelcomeScreen} when starting the application.
+ */
+ private void showWorkspaceSettings()
+ {
+
+ Runnable runnable = new Runnable() {
+ public void run() {
+ String authentification = null;
+ GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance();
+ if (gc.isPasswordRequired())
+ {
+ authentification = showPasswordPopup();
+ if (authentification == null)
+ return; // user cancelled the process
+
+ if(!gc.authenticate(new String(authentification)))
+ {
+ // Could not authenticate => cancel
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("i.am.sorry"),
+ Logo.messages.getString("welcome.wrong.pw"));
+ return;
+ }
+ }
+
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ setMessageManagerParent();
+ setText();
+ populateWorkspaceList();
+ populateUserList();
+ setEnabled(true);
+ }
+ };
+
+ setEnabled(false);
+ new WorkspaceSettings(listener, authentification);
+ }
+ };
+
+ new Thread(runnable).start();
+ }
+
+ private void setMessageManagerParent()
+ {
+ DialogMessenger.getInstance().setMedium(new AsyncMediumAdapter<AsyncMessage<JFrame>, JFrame>(){
+ public boolean isReady()
+ {
+ return getThis().isDisplayable();
+ }
+ public JFrame getMedium()
+ {
+ return getThis();
+ }
+ public void addMediumReadyListener(final AsyncMessenger messenger)
+ {
+ getThis().addWindowStateListener(new WindowStateListener(){
+
+ @Override
+ public void windowStateChanged(WindowEvent e)
+ {
+ if (getThis().isDisplayable())
+ messenger.onMediumReady();
+ }
+ });
+ }
+ });
+ }
+
+ private JFrame getThis()
+ {
+ return this;
+ }
+
+
+ protected String showPasswordPopup() {
+ JPasswordField passwordField = new JPasswordField();
+ int option = JOptionPane.showConfirmDialog(this, passwordField, Logo.messages.getString("welcome.enter.pw"),
+ JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
+
+ if (option == JOptionPane.OK_OPTION) {
+ return new String(passwordField.getPassword());
+ }
+ return null;
+ }
+
+ public void enterApplication()
+ {
+ System.gc();
+ String username = (String) userSelection.getSelectedItem();
+
+ if ((username == null) || (username.length() == 0))
+ return; // this should not happen since the enter button is disabled
+
+ if(!Storable.checkLegalName(username))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME));
+ return;
+ }
+
+
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ if (!wc.existsUserLogically(username))
+ wc.createUser(username);
+
+ try {
+ WSManager.getInstance().enterUserSpace(username);
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("welcome.could.not.enter.user") + e.toString());
+ return;
+ }
+ System.gc();
+ listener.actionPerformed(new ActionEvent(this, 0, null));
+ }
+
+ @Override
+ public void dispose()
+ {
+ try {
+ WSManager.getInstance().getGlobalConfigInstance().store();
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("storage.could.not.store.gc"));
+ }
+
+ System.gc();
+ super.dispose();
+ }
+
+ public void setText()
+ {
+ workspace.setText(Logo.messages.getString("welcome.workspace"));
+ username.setText(Logo.messages.getString("welcome.username"));
+ openWorkspaceSettingsBtn.setText(Logo.messages.getString("welcome.settings"));
+ enterButton.setText(Logo.messages.getString("welcome.enter"));
+ setTitle(Logo.messages.getString("welcome.title"));
+ pack();
+ }
+
+ /**
+ * Like in XLogo, almost unmodified.
+ * It is displayed in the language of the currently selected workspace.
+ */
+ private void showGPL()
+ {
+ JFrame frame = new JFrame(Logo.messages.getString("menu.help.licence"));
+ frame.setIconImage(Toolkit.getDefaultToolkit().createImage(WebPage.class.getResource("Logo_xlogo4schools.png")));
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.setSize(500, 500);
+ WebPage editorPane = new WebPage();
+ editorPane.setEditable(false);
+
+ String langCode = WSManager.getWorkspaceConfig().getLanguage().getLanguageCode();
+
+ String path = "gpl/gpl-" + langCode + ".html";
+
+ java.net.URL helpURL = Application.class.getResource(path);
+ if (helpURL != null)
+ {
+ try
+ {
+ editorPane.setPage(helpURL);
+ }
+ catch (IOException e1)
+ {
+ System.err.println("Attempted to read a bad URL: " + helpURL);
+ }
+ }
+ else
+ {
+ System.err.println("Couldn't find file: " + path);
+ }
+
+ // Put the editor pane in a scroll pane.
+ JScrollPane editorScrollPane = new JScrollPane(editorPane);
+ editorScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+ editorScrollPane.setPreferredSize(new Dimension(250, 145));
+ editorScrollPane.setMinimumSize(new Dimension(10, 10));
+ frame.getContentPane().add(editorScrollPane);
+ frame.setVisible(true);
+ }
+
+ private void showInfo()
+ {
+ JFrame frame = new JFrame(Logo.messages.getString("menu.help.licence"));
+ frame.setIconImage(Toolkit.getDefaultToolkit().createImage(WebPage.class.getResource("Icon_x4s.png")));
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.setSize(500, 500);
+ WebPage editorPane = new WebPage();
+ editorPane.setEditable(false);
+
+ String path = "gpl/x4s_info.html";
+
+ java.net.URL helpURL = Application.class.getResource(path);
+ if (helpURL != null)
+ {
+ try
+ {
+ editorPane.setPage(helpURL);
+ }
+ catch (IOException e1)
+ {
+ System.err.println("Attempted to read a bad URL: " + helpURL);
+ }
+ }
+ else
+ {
+ System.err.println("Couldn't find file: " + path);
+ }
+
+ // Put the editor pane in a scroll pane.
+ JScrollPane editorScrollPane = new JScrollPane(editorPane);
+ editorScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+ editorScrollPane.setPreferredSize(new Dimension(250, 145));
+ editorScrollPane.setMinimumSize(new Dimension(10, 10));
+ frame.getContentPane().add(editorScrollPane);
+ frame.setVisible(true);
+ }
+
+ /*
+ * Helper
+ */
+
+ private ImageIcon createImageIcon(String path, String description, int width, int heigth) {
+ Image img = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(path));
+ return new ImageIcon(img.getScaledInstance(width, heigth, Image.SCALE_SMOOTH));
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/gui/welcome/WorkspaceSettings.java b/logo/src/xlogo/gui/welcome/WorkspaceSettings.java
new file mode 100644
index 0000000..9634325
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/WorkspaceSettings.java
@@ -0,0 +1,183 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome;
+
+import java.awt.Toolkit;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowStateListener;
+import java.io.IOException;
+
+import javax.swing.JFrame;
+import javax.swing.JTabbedPane;
+
+import xlogo.gui.components.X4SFrame;
+import xlogo.gui.welcome.settings.tabs.SyntaxHighlightingTab;
+import xlogo.gui.welcome.settings.tabs.ContestTab;
+import xlogo.gui.welcome.settings.tabs.GlobalTab;
+import xlogo.gui.welcome.settings.tabs.WorkspaceTab;
+import xlogo.messages.async.AsyncMediumAdapter;
+import xlogo.messages.async.AsyncMessage;
+import xlogo.messages.async.AsyncMessenger;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+
+public class WorkspaceSettings extends X4SFrame {
+
+ private JFrame frame;
+
+ // TABS
+ JTabbedPane tabs;
+ GlobalTab globalTab;
+ WorkspaceTab workspaceTab;
+ SyntaxHighlightingTab appearanceTab;
+ ContestTab contestTab;
+
+ /**
+ * Used to communicate with the frame which opened this one.
+ */
+ private ActionListener listener;
+
+ public WorkspaceSettings(ActionListener listener, String authentification)
+ {
+ super();
+ this.listener = listener;
+ globalTab.authenticate(authentification);
+ frame.setVisible(true);
+ }
+
+ @Override
+ public JFrame getFrame() {
+ return frame;
+ }
+
+ @Override
+ protected void initComponent() {
+ frame = new JFrame(){
+ private static final long serialVersionUID = 7057009528231153055L;
+
+ @Override
+ public void dispose()
+ {
+ try {
+ WSManager.getInstance().getGlobalConfigInstance().store();
+ WSManager.getInstance().getWorkspaceConfigInstance().store();
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("ws.error.title"),
+ translate("storage.could.not.store.gc"));
+ }
+
+ listener.actionPerformed(null);
+ super.dispose();
+ System.gc();
+ }
+ };
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("Icon_x4s.png"))); // TODO need new icon?
+
+ tabs = new JTabbedPane();
+ globalTab = new GlobalTab();
+ workspaceTab = new WorkspaceTab();
+ appearanceTab = new SyntaxHighlightingTab();
+ contestTab = new ContestTab();
+
+ setMessageManagerParent();
+ }
+
+ @Override
+ protected void layoutComponent() {
+ frame.setResizable(false);
+
+ tabs.addTab("Global", null, globalTab.getComponent(), "Global Settings");
+ tabs.addTab("Workspace", null, workspaceTab.getComponent(), "Workspace Settings");
+ tabs.addTab("Appearance", null, appearanceTab.getComponent(), "Workspace Appearance");
+ tabs.addTab("Contest", null, contestTab.getComponent(), "Contest Settings");
+
+ frame.getContentPane().add(tabs);
+ }
+
+ @Override
+ protected void setText()
+ {
+ frame.setTitle(translate("ws.settings.title"));
+ tabs.setTitleAt(0, translate("ws.settings.global"));
+ tabs.setToolTipTextAt(0, translate("ws.settings.global.settings"));
+ tabs.setTitleAt(1, translate("ws.settings.workspace"));
+ tabs.setToolTipTextAt(1, translate("ws.settings.global.settings"));
+ tabs.setTitleAt(2, translate("ws.settings.syntax")); // TODO make translation
+ tabs.setToolTipTextAt(2, translate("ws.settings.global.settings"));
+ tabs.setTitleAt(3, translate("ws.settings.contest"));
+ tabs.setToolTipTextAt(3, translate("ws.settings.global.settings"));
+ frame.pack();
+ }
+
+ @Override
+ protected void initEventListeners()
+ {
+
+ }
+
+ @Override
+ public void stopEventListeners()
+ {
+ stopListenForLanguageChangeEvents();
+ globalTab.stopEventListeners();
+ workspaceTab.stopEventListeners();
+ appearanceTab.stopEventListeners();
+ contestTab.stopEventListeners();
+ }
+
+ private void setMessageManagerParent()
+ {
+ DialogMessenger.getInstance().setMedium(new AsyncMediumAdapter<AsyncMessage<JFrame>, JFrame>(){
+ public boolean isReady()
+ {
+ return frame.isDisplayable();
+ }
+ public JFrame getMedium()
+ {
+ return frame;
+ }
+ public void addMediumReadyListener(final AsyncMessenger messenger)
+ {
+ frame.addWindowStateListener(new WindowStateListener(){
+
+ @Override
+ public void windowStateChanged(WindowEvent e)
+ {
+ if (frame.isDisplayable())
+ messenger.onMediumReady();
+ }
+ });
+ }
+ });
+ }
+
+}
diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java b/logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java
new file mode 100644
index 0000000..a226e64
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java
@@ -0,0 +1,269 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome.settings.tabs;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JOptionPane;
+
+import xlogo.AppSettings;
+import xlogo.gui.components.X4SComponent;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+
+public abstract class AbstractWorkspacePanel extends X4SComponent{
+
+ private ActionListener enterWorkspaceListener;
+ private ActionListener workspaceListChangeListener;
+
+ protected abstract JComboBox getWorkspaceSelection();
+
+ @Override
+ protected void initEventListeners()
+ {
+ getWorkspaceSelection().addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ String wsName = (String) getWorkspaceSelection().getSelectedItem();
+ enterWorkspace(wsName);
+ }
+ }).run();
+ }
+ });
+
+ final GlobalConfig gc = WSManager.getGlobalConfig();
+
+ gc.addWorkspaceListChangeListener(workspaceListChangeListener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ populateWorkspaceList();
+ }
+ });
+
+ gc.addEnterWorkspaceListener(enterWorkspaceListener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ getWorkspaceSelection().setSelectedItem(gc.getLastUsedWorkspace());
+ }
+ });
+ }
+
+ @Override
+ public void stopEventListeners()
+ {
+ super.stopEventListeners();
+ GlobalConfig gc = WSManager.getGlobalConfig();
+ gc.removeEnterWorkspaceListener(enterWorkspaceListener);
+ gc.removeWorkspaceListChangeListener(workspaceListChangeListener);
+
+ }
+ protected abstract void setValues();
+
+ protected abstract void enableComponents();
+
+ protected abstract void disableComponents();
+
+ protected void populateWorkspaceList() {
+ GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance();
+ String[] workspaces = gc.getAllWorkspaces();
+ getWorkspaceSelection().setModel(new DefaultComboBoxModel(workspaces));
+ String lastUsed = gc.getLastUsedWorkspace();
+ enterWorkspace(lastUsed);
+ getWorkspaceSelection().setSelectedItem(lastUsed);
+ }
+
+ protected void deleteWorkspace() {
+ WSManager wsManager = WSManager.getInstance();
+ GlobalConfig gc = wsManager.getGlobalConfigInstance();
+
+ String wsName = (String) getWorkspaceSelection().getSelectedItem();
+ String wsLocation = gc.getWorkspaceDirectory(wsName).toString();
+ String message =
+ translate("ws.settings.want.delete.dir.1")
+ + wsLocation
+ + translate("ws.settings.want.delete.dir.2");
+
+ boolean ans = getUserYesOrNo(message, translate("ws.settings.delete.from.fs"));
+
+ try {
+ wsManager.enterWorkspace(WorkspaceConfig.VIRTUAL_WORKSPACE);
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("ws.error.title"),
+ translate("ws.settings.could.not.enter.virtual.ws") + e.toString());
+ }
+ wsManager.deleteWorkspace(wsName, ans);
+
+ populateWorkspaceList();
+ }
+
+ protected void importWorkspace() {
+ File dir = getUserSelectedDirectory();
+ if (dir == null)
+ return;
+
+ WSManager wsManager = WSManager.getInstance();
+ if (!WSManager.isWorkspaceDirectory(dir))
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("i.am.sorry"),
+ dir.toString() + translate("ws.settings.not.legal.ws.dir"));
+ return;
+ }
+ String newName = dir.getName();
+
+ if (dir.equals(WSManager.getGlobalConfig().getWorkspaceDirectory(newName)))
+ {
+ DialogMessenger.getInstance().dispatchMessage("The workspace was already in the list.");
+ return;
+ }
+
+ File newDir = new File(dir.toString());
+
+ if (WSManager.getGlobalConfig().existsWorkspace(newName) || !Storable.checkLegalName(newName))
+ {
+ do
+ {
+ String msg = WSManager.getGlobalConfig().existsWorkspace(newName) ?
+ "The workspace name " + newName + " already exists. Please choose a new name"
+ : newDir.exists() ? newDir.toString() + " already exists. Please choose a new name."
+ : "The chosen name contains illegal characters.";
+
+ newName = getUserText(msg, "Name Conflict");
+ if (newName == null)
+ return;
+
+ newDir = new File(dir.getParent() + File.separator + newName);
+ }
+ while(WSManager.getGlobalConfig().existsWorkspace(newName)
+ || !Storable.checkLegalName(newName)
+ || newDir.exists());
+ }
+
+ if(!newDir.equals(dir))
+ dir.renameTo(newDir);
+
+ wsManager.importWorkspace(newDir, newName);
+ populateWorkspaceList();
+ }
+
+ protected void enterWorkspace(String wsName) {
+ try {
+ // enter workspace
+ WSManager.getInstance().enterWorkspace(wsName);
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ if (wc == null)
+ {
+ disableComponents();
+ return;
+ }
+ if (wc.isVirtual())
+ disableComponents();
+ else
+ enableComponents();
+ setValues();
+ AppSettings.getInstance().setLanguage(wc.getLanguage());
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("ws.error.title"),
+ translate("ws.settings.could.not.enter.wp") + e.toString());
+ disableComponents();
+ }
+
+ }
+
+ protected void addWorkspace() {
+ WorkspaceCreationPanel wscPanel = new WorkspaceCreationPanel();
+
+ int option = JOptionPane.showConfirmDialog(
+ getComponent(),
+ wscPanel.getComponent(),
+ translate("ws.settings.create.new.wp"),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.PLAIN_MESSAGE);
+
+ if (option != JOptionPane.OK_OPTION)
+ return;
+
+ String wsName = wscPanel.wsNameField.getText();
+ String location = wscPanel.locationField.getText();
+ File dir = new File(location);
+
+ GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance();
+
+ // Make sure that the specified workspace name is non-empty and that it does not exist already
+ if (wsName.length() == 0){
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("i.am.sorry"),
+ translate("ws.settings.wp.name.non.empty"));
+ return;
+ }
+ if (gc.existsWorkspace(wsName)){
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("i.am.sorry"),
+ translate("ws.settings.wp.exists.already"));
+ return;
+ }
+
+ // Make sure dir is an existing directory
+ if(!dir.exists()){
+ if (!dir.mkdirs()){
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("ws.error.title"),
+ translate("ws.settings.could.not.create.directory"));
+ return;
+ }
+ }else if (!dir.isDirectory()){
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("ws.error.title"),
+ translate("ws.settings.need.dir.not.file"));
+ return;
+ }
+ // dir exists & wsName doesn't exist yet => fine to create WS now
+ try {
+ WSManager.getInstance().createWorkspace(dir, wsName);
+ populateWorkspaceList();
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("ws.error.title"),
+ translate("ws.settings.could.not.create.ws"));
+ return;
+ }
+
+ }
+
+}
diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java
new file mode 100644
index 0000000..e2dfa0a
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java
@@ -0,0 +1,197 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome.settings.tabs;
+
+import java.awt.Dimension;
+
+import javax.swing.GroupLayout;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.WorkspaceConfig;
+
+public class ContestTab extends AbstractWorkspacePanel {
+
+ JPanel component;
+ JLabel workspaceLabel;
+ JComboBox workspaceSelection;
+ JLabel nOfFilesLabel;
+ JSpinner nOfFileSpinner;
+ JLabel nOfBonusFilesLabel;
+ JSpinner nOfBonusFileSpinner;
+
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ @Override
+ protected JComboBox getWorkspaceSelection() {
+ return workspaceSelection;
+ }
+
+ @Override
+ protected void initComponent()
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ component = new JPanel();
+
+ workspaceLabel = new JLabel();
+ workspaceSelection = new JComboBox();
+
+ nOfFilesLabel = new JLabel();
+ nOfFileSpinner = new JSpinner(new SpinnerNumberModel(wc.getNOfContestFiles(), 0, 100, 1));
+ JComponent editor = new JSpinner.NumberEditor(nOfFileSpinner);
+ nOfFileSpinner.setEditor(editor);
+
+ nOfBonusFilesLabel = new JLabel();
+ nOfBonusFileSpinner = new JSpinner(new SpinnerNumberModel(wc.getNOfContestBonusFiles(), 0, 100, 1));
+ JComponent editor2 = new JSpinner.NumberEditor(nOfBonusFileSpinner);
+ nOfBonusFileSpinner.setEditor(editor2);
+
+ populateWorkspaceList();
+ }
+
+ @Override
+ protected void layoutComponent()
+ {
+ workspaceSelection.setMinimumSize(new Dimension(150,25));
+ workspaceSelection.setMaximumSize(new Dimension(150,25));
+
+ nOfFileSpinner.setMinimumSize(new Dimension(25,25));
+ nOfFileSpinner.setMaximumSize(new Dimension(50,25));
+
+ nOfBonusFileSpinner.setMinimumSize(new Dimension(25,25));
+ nOfBonusFileSpinner.setMaximumSize(new Dimension(50,25));
+
+ component.add(workspaceLabel);
+ component.add(workspaceSelection);
+ component.add(nOfFilesLabel);
+ component.add(nOfFileSpinner);
+ component.add(nOfBonusFilesLabel);
+ component.add(nOfBonusFileSpinner);
+
+ GroupLayout groupLayout = new GroupLayout(component);
+ component.setLayout(groupLayout);
+
+ groupLayout.setAutoCreateGaps(true);
+ groupLayout.setAutoCreateContainerGaps(true);
+
+ groupLayout.setVerticalGroup(
+ groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceLabel)
+ .addComponent(workspaceSelection)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(nOfFilesLabel)
+ .addComponent(nOfFileSpinner)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(nOfBonusFilesLabel)
+ .addComponent(nOfBonusFileSpinner)
+ )
+ );
+
+ groupLayout.setHorizontalGroup(
+ groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceLabel)
+ .addComponent(nOfFilesLabel)
+ .addComponent(nOfBonusFilesLabel)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceSelection)
+ .addComponent(nOfFileSpinner)
+ .addComponent(nOfBonusFileSpinner)
+ )
+ );
+ }
+
+ @Override
+ protected void setText()
+ {
+ workspaceLabel.setText(translate("ws.settings.workspace"));
+ nOfFilesLabel.setText(translate("contest.number.of.files"));
+ nOfBonusFilesLabel.setText(translate("contest.number.of.bonus.files"));
+ }
+
+ @Override
+ protected void initEventListeners()
+ {
+ super.initEventListeners();
+
+ nOfFileSpinner.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent arg0) {
+ WSManager.getWorkspaceConfig().setNOfContestFiles((Integer) nOfFileSpinner.getValue());
+ }
+ });
+
+ nOfBonusFileSpinner.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ WSManager.getWorkspaceConfig().setNOfContestBonusFiles((Integer) nOfBonusFileSpinner.getValue());
+ }
+ });
+ }
+
+ @Override
+ public void stopEventListeners()
+ {
+ super.stopEventListeners();
+
+ }
+
+ @Override
+ protected void setValues() {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ nOfFileSpinner.setValue(wc.getNOfContestFiles());
+ nOfBonusFileSpinner.setValue(wc.getNOfContestBonusFiles());
+ }
+
+ @Override
+ protected void enableComponents() {
+ nOfFileSpinner.setEnabled(true);
+ nOfBonusFileSpinner.setEnabled(true);
+ }
+
+ @Override
+ protected void disableComponents() {
+ nOfFileSpinner.setEnabled(false);
+ nOfBonusFileSpinner.setEnabled(false);
+ }
+
+}
diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java
new file mode 100644
index 0000000..ecaef9c
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java
@@ -0,0 +1,230 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome.settings.tabs;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.io.IOException;
+
+import javax.swing.GroupLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+
+import xlogo.gui.components.X4SComponent;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+/**
+ * @since June 10th 2014
+ * @author Marko
+ */
+public class GlobalTab extends X4SComponent {
+
+ private JPanel component;
+
+ private JCheckBox askPasswordCb;
+ private JLabel passwordLabel;
+ private JLabel retypeLabel;
+ private JPasswordField passwordField;
+ private JPasswordField retypeField;
+ private JButton save1;
+
+ private String authentification;
+
+ public GlobalTab() {
+ super();
+ }
+
+ public void authenticate(String authentification)
+ {
+ this.authentification = authentification;
+
+ boolean hasPw = authentification != null;
+ askPasswordCb.setSelected(hasPw);
+ showOrHidePw(hasPw);
+ passwordField.setText(authentification);
+ retypeField.setText(authentification);
+ }
+
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ @Override
+ protected void initComponent()
+ {
+ component = new JPanel();
+
+ askPasswordCb = new JCheckBox("Protect these settings with a password.");
+ passwordLabel = new JLabel("Password");
+ retypeLabel = new JLabel("Retype Password");
+ passwordField = new JPasswordField();
+ retypeField = new JPasswordField();
+ save1 = new JButton("Save Password");
+ }
+
+ @Override
+ protected void layoutComponent()
+ {
+ passwordField.setMinimumSize(new Dimension(200, 25));
+ retypeField.setMinimumSize(new Dimension(200, 25));
+ passwordField.setMaximumSize(new Dimension(200, 25));
+ retypeField.setMaximumSize(new Dimension(200, 25));
+
+ component.add(askPasswordCb);
+ component.add(passwordLabel);
+ component.add(passwordField);
+ component.add(retypeLabel);
+ component.add(retypeField);
+ component.add(save1);
+
+ GroupLayout groupLayout = new GroupLayout(component);
+ component.setLayout(groupLayout);
+
+ groupLayout.setAutoCreateGaps(true);
+ groupLayout.setAutoCreateContainerGaps(true);
+
+ groupLayout.setVerticalGroup(
+ groupLayout.createSequentialGroup()
+ .addComponent(askPasswordCb)
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(passwordLabel)
+ .addComponent(passwordField))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(retypeLabel)
+ .addComponent(retypeField))
+ .addComponent(save1)
+ );
+
+ groupLayout.setHorizontalGroup(
+ groupLayout.createParallelGroup()
+ .addComponent(askPasswordCb)
+ .addGroup(groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(passwordLabel)
+ .addComponent(retypeLabel)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(passwordField)
+ .addComponent(retypeField)
+ )
+ )
+ .addComponent(save1)
+ );
+ }
+
+ @Override
+ protected void initEventListeners()
+ {
+ askPasswordCb.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ boolean selected = e.getStateChange() == ItemEvent.SELECTED;
+ showOrHidePw(selected);
+ passwordField.setText(null);
+ retypeField.setText(null);
+ if (!selected)
+ WSManager.getInstance().getGlobalConfigInstance().setNewPassword(authentification, null);
+ }
+ });
+
+ save1.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ try {
+ savePassword();
+ } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("ws.error.title"),
+ translate("ws.settings.cannot.store.pw") + e.toString());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void stopEventListeners()
+ {
+ super.stopEventListeners();
+
+ }
+
+ private void showOrHidePw(boolean show)
+ {
+ askPasswordCb.setSelected(show);
+ passwordLabel.setVisible(show);
+ passwordField.setVisible(show);
+ retypeLabel.setVisible(show);
+ retypeField.setVisible(show);
+ save1.setVisible(show);
+ }
+
+ private void savePassword() throws IOException
+ {
+ GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance();
+
+ if (askPasswordCb.isSelected())
+ {
+ String pw1 = new String(passwordField.getPassword());
+ String pw2 = new String(retypeField.getPassword());
+ if (pw1.equals(pw2))
+ {
+ gc.setNewPassword(authentification, pw1);
+ authentification = pw1;
+ gc.store();
+ }
+ else
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ translate("i.am.sorry"),
+ translate("ws.settings.pw.must.be.equal"));
+ }
+ }else // checkbox not selected
+ {
+ gc.setNewPassword(authentification, null);
+ authentification = null;
+ gc.store();
+ }
+ }
+
+ @Override
+ protected void setText()
+ {
+ askPasswordCb.setText(translate("ws.settings.require_password"));
+ passwordLabel.setText(translate("ws.settings.password"));
+ retypeLabel.setText(translate("ws.settings.retype.password"));
+ save1.setText(translate("ws.settings.save.password"));
+ }
+
+}
diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java
new file mode 100644
index 0000000..c5926f3
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java
@@ -0,0 +1,288 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome.settings.tabs;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.GroupLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextPane;
+
+import xlogo.AppSettings;
+import xlogo.StyledDocument.DocumentLogo;
+import xlogo.gui.components.ColorStyleSelectionPanel;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.SyntaxHighlightConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+
+public class SyntaxHighlightingTab extends AbstractWorkspacePanel{
+ private JPanel component;
+
+ private JLabel workspaceLabel;
+ private JComboBox workspaceSelection;
+ private ColorStyleSelectionPanel commentStyleSelection;
+ private ColorStyleSelectionPanel braceStyleSelection;
+ private ColorStyleSelectionPanel primitiveStyleSelection;
+ private ColorStyleSelectionPanel operandStyleSelection;
+ private JCheckBox activateHighlightingCheckBox;
+ private JLabel activateHighlightingLabel;
+ private JButton restoreDefaultsButton;
+ private DocumentLogo previewLogoDocument;
+ private JTextPane previewTextPane;
+
+ private ActionListener syntaxHighlightChangeListener;
+
+ public SyntaxHighlightingTab() {
+ super();
+ }
+
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ protected void initComponent()
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ //Font font = wc.getFont();
+ component = new JPanel();
+ workspaceLabel = new JLabel();
+ workspaceSelection = new JComboBox();
+ commentStyleSelection=new ColorStyleSelectionPanel(wc.getCommentColor(), wc.getCommentStyle(), translate("pref.highlight.comment"));
+ braceStyleSelection=new ColorStyleSelectionPanel(wc.getBraceColor(), wc.getBraceStyle(), translate("pref.highlight.parenthesis"));
+ primitiveStyleSelection=new ColorStyleSelectionPanel(wc.getPrimitiveColor(), wc.getPrimitiveStyle(), translate("pref.highlight.primitive"));
+ operandStyleSelection=new ColorStyleSelectionPanel(wc.getOperatorColor(), wc.getOperatorStyle(), translate("pref.highlight.operand"));
+
+ previewTextPane=new JTextPane();
+ activateHighlightingCheckBox = new JCheckBox();
+ activateHighlightingLabel=new JLabel();
+ restoreDefaultsButton=new JButton();
+
+ //activateHighlightingLabel.setFont(font);
+ //restoreDefaultsButton.setFont(font);
+ previewTextPane.setOpaque(true);
+ previewTextPane.setBackground(Color.white);
+ previewLogoDocument=new DocumentLogo();
+ previewTextPane.setDocument(previewLogoDocument);
+ previewLogoDocument.setColore_Parenthese(true);
+
+ populateWorkspaceList();
+ setValues();
+ }
+
+ protected void layoutComponent()
+ {
+ component.add(workspaceLabel);
+ component.add(workspaceSelection);
+ component.add(activateHighlightingCheckBox);
+ component.add(activateHighlightingLabel);
+ component.add(commentStyleSelection.getComponent());
+ component.add(primitiveStyleSelection.getComponent());
+ component.add(operandStyleSelection.getComponent());
+ component.add(braceStyleSelection.getComponent());
+ component.add(previewTextPane);
+ component.add(restoreDefaultsButton);
+
+ GroupLayout groupLayout = new GroupLayout(component);
+ component.setLayout(groupLayout);
+
+ groupLayout.setAutoCreateGaps(true);
+ groupLayout.setAutoCreateContainerGaps(true);
+
+ groupLayout.setVerticalGroup(
+ groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceLabel)
+ .addComponent(workspaceSelection))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(activateHighlightingLabel)
+ .addComponent(activateHighlightingCheckBox))
+ .addComponent(commentStyleSelection.getComponent())
+ .addComponent(primitiveStyleSelection.getComponent())
+ .addComponent(operandStyleSelection.getComponent())
+ .addComponent(braceStyleSelection.getComponent())
+ .addComponent(previewTextPane)
+ .addComponent(restoreDefaultsButton)
+ );
+ groupLayout.setHorizontalGroup(
+ groupLayout.createParallelGroup()
+ .addGroup(groupLayout.createSequentialGroup()
+ .addComponent(workspaceLabel)
+ .addComponent(workspaceSelection))
+ .addGroup(groupLayout.createSequentialGroup()
+ .addComponent(activateHighlightingLabel)
+ .addComponent(activateHighlightingCheckBox))
+ .addComponent(commentStyleSelection.getComponent())
+ .addComponent(primitiveStyleSelection.getComponent())
+ .addComponent(operandStyleSelection.getComponent())
+ .addComponent(braceStyleSelection.getComponent())
+ .addComponent(previewTextPane)
+ .addComponent(restoreDefaultsButton)
+ );
+ }
+
+ protected void setText()
+ {
+ workspaceLabel.setText(translate("ws.settings.workspace"));
+ previewTextPane.setText(translate("pref.highlight.example"));
+ commentStyleSelection.setTitle(translate("pref.highlight.comment"));
+ braceStyleSelection.setTitle(translate("pref.highlight.parenthesis"));
+ primitiveStyleSelection.setTitle(translate("pref.highlight.primitive"));
+ operandStyleSelection.setTitle(translate("pref.highlight.operand"));
+ activateHighlightingLabel.setText(translate("pref.highlight.enabled"));
+ restoreDefaultsButton.setText(translate("pref.highlight.init"));
+ previewTextPane.setText(translate("pref.highlight.example"));
+ }
+
+ @Override
+ protected void initEventListeners()
+ {
+ super.initEventListeners();
+ activateHighlightingCheckBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ WSManager.getWorkspaceConfig().setSyntaxHighlightingEnabled(activateHighlightingCheckBox.isSelected());
+ }
+ });
+ restoreDefaultsButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SyntaxHighlightConfig syntaxStyles = new SyntaxHighlightConfig();
+ WSManager.getWorkspaceConfig().setSyntaxHighlightConfig(syntaxStyles);
+ setValues();
+ }
+ });
+ AppSettings.getInstance().addSyntaxHighlightStyleChangeListener(
+ syntaxHighlightChangeListener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ updateSyntaxHighlightingPreview();
+ }
+ });
+
+ operandStyleSelection.addStyleChangeListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ wc.setOperandColor(operandStyleSelection.color());
+ wc.setOperandStyle(operandStyleSelection.style());
+ }
+ });
+
+ braceStyleSelection.addStyleChangeListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ wc.setBraceColor(braceStyleSelection.color());
+ wc.setBraceStyle(braceStyleSelection.style());
+ }
+ });
+
+ primitiveStyleSelection.addStyleChangeListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ wc.setPrimitiveColor(primitiveStyleSelection.color());
+ wc.setPrimitiveStyle(primitiveStyleSelection.style());
+ }
+ });
+
+ commentStyleSelection.addStyleChangeListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ wc.setCommentColor(commentStyleSelection.color());
+ wc.setCommentStyle(commentStyleSelection.style());
+ }
+ });
+ }
+
+ @Override
+ public void stopEventListeners()
+ {
+ super.stopEventListeners();
+ AppSettings.getInstance().removeSyntaxHighlightStyleChangeListener(syntaxHighlightChangeListener);
+
+ }
+
+ @Override
+ protected JComboBox getWorkspaceSelection() {
+ return workspaceSelection;
+ }
+
+ @Override
+ protected void setValues() {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ commentStyleSelection.setColorAndStyle(wc.getCommentColor(), wc.getCommentStyle());
+ braceStyleSelection.setColorAndStyle(wc.getBraceColor(), wc.getBraceStyle());
+ primitiveStyleSelection.setColorAndStyle(wc.getPrimitiveColor(), wc.getPrimitiveStyle());
+ operandStyleSelection.setColorAndStyle(wc.getOperatorColor(), wc.getOperatorStyle());
+
+ updateSyntaxHighlightingPreview();
+ }
+
+ protected void updateSyntaxHighlightingPreview()
+ {
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+
+ boolean isHighlightingEnabled = wc.isSyntaxHighlightingEnabled();
+ activateHighlightingCheckBox.setSelected(isHighlightingEnabled);
+ commentStyleSelection.setEnabled(isHighlightingEnabled);
+ primitiveStyleSelection.setEnabled(isHighlightingEnabled);
+ braceStyleSelection.setEnabled(isHighlightingEnabled);
+ operandStyleSelection.setEnabled(isHighlightingEnabled);
+ restoreDefaultsButton.setEnabled(isHighlightingEnabled);
+ previewLogoDocument.setColoration(isHighlightingEnabled);
+
+ previewLogoDocument.initStyles(
+ wc.getCommentColor(), wc.getCommentStyle(),
+ wc.getPrimitiveColor(), wc.getPrimitiveStyle(),
+ wc.getBraceColor(), wc.getBraceStyle(),
+ wc.getOperatorColor(), wc.getOperatorStyle());
+
+ previewTextPane.setText(translate("pref.highlight.example"));
+ }
+
+ @Override
+ protected void enableComponents() {
+
+ }
+
+ @Override
+ protected void disableComponents() {
+
+ }
+
+}
diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java
new file mode 100644
index 0000000..06f8ad6
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java
@@ -0,0 +1,144 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome.settings.tabs;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+import javax.swing.GroupLayout;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import xlogo.gui.components.X4SComponent;
+import xlogo.storage.WSManager;
+
+class WorkspaceCreationPanel extends X4SComponent
+{
+
+ private JPanel component;
+
+ public JLabel wsNameLabel;
+ public JLabel locationLabel;
+ public JTextField wsNameField;
+ public JTextField locationField;
+ public JButton openFilechooserBtn;
+
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ @Override
+ protected void initComponent() {
+ component = new JPanel();
+
+ wsNameLabel = new JLabel("Workspace Name");
+ locationLabel = new JLabel("Location");
+ wsNameField = new JTextField();
+ locationField = new JTextField();
+ openFilechooserBtn = new JButton("Browse");
+
+ locationField.setText(WSManager.getInstance().getGlobalConfigInstance().getLocation().toString());
+ locationField.setEditable(false);
+ }
+
+ @Override
+ protected void layoutComponent() {
+ component.add(wsNameLabel);
+ component.add(locationLabel);
+ component.add(wsNameField);
+ component.add(locationField);
+ component.add(openFilechooserBtn);
+
+ locationField.setMinimumSize(new Dimension(250,15));
+
+ GroupLayout groupLayout = new GroupLayout(component);
+ component.setLayout(groupLayout);
+
+ groupLayout.setAutoCreateContainerGaps(true);
+ groupLayout.setAutoCreateGaps(true);
+
+ groupLayout.setVerticalGroup(groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(wsNameLabel)
+ .addComponent(wsNameField)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(locationLabel)
+ .addComponent(locationField)
+ .addComponent(openFilechooserBtn)
+ )
+ );
+
+ groupLayout.setHorizontalGroup(groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(wsNameLabel)
+ .addComponent(locationLabel)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(wsNameField)
+ .addGroup(groupLayout.createSequentialGroup()
+ .addComponent(locationField)
+ .addComponent(openFilechooserBtn))
+ )
+ );
+
+ }
+
+ @Override
+ protected void initEventListeners()
+ {
+ openFilechooserBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ File location = getUserSelectedDirectory();
+ if (location != null)
+ locationField.setText(location.toString());
+ }
+ });
+ }
+
+ @Override
+ public void stopEventListeners()
+ {
+ super.stopEventListeners();
+
+ }
+
+ @Override
+ protected void setText()
+ {
+ wsNameLabel.setText(translate("ws.creation.panel.name"));
+ locationLabel.setText(translate("ws.creation.panel.location"));
+ openFilechooserBtn.setText(translate("ws.creation.panel.browse"));
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java
new file mode 100644
index 0000000..3b0e8a4
--- /dev/null
+++ b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java
@@ -0,0 +1,537 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.gui.welcome.settings.tabs;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.GroupLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import xlogo.Logo;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.workspace.Language;
+import xlogo.storage.workspace.NumberOfBackups;
+import xlogo.storage.workspace.WorkspaceConfig;
+
+public class WorkspaceTab extends AbstractWorkspacePanel{
+
+ JPanel component;
+
+ JLabel workspaceLabel;
+ JLabel wsLocationLabel;
+ JLabel wsLanguageLabel;
+ JLabel wsBackupLabel;
+ JLabel userLabel;
+
+ JButton addWorkspaceBtn;
+ JButton addUserBtn;
+
+ JButton removeWorkspaceBtn;
+ JButton removeUserBtn;
+
+ JButton importWorkspaceBtn;
+ JButton importUserBtn;
+
+ JComboBox workspaceSelection;
+ JComboBox userSelection;
+ JLabel wsLocation;
+ JFileChooser wsLocationChooser;
+ JComboBox languageSelection;
+ JComboBox nOfBackupsSelecteion;
+ JCheckBox userCreatable;
+
+ public WorkspaceTab() {
+ super();
+ }
+
+ @Override
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ @Override
+ protected JComboBox getWorkspaceSelection() {
+ return workspaceSelection;
+ }
+
+ protected void initComponent()
+ {
+ component = new JPanel();
+
+ workspaceLabel = new JLabel("Workspace: ");
+ wsLocationLabel = new JLabel("Location: ");
+ wsLanguageLabel = new JLabel("Language: ");
+ wsBackupLabel = new JLabel("Number of Backups: ");
+ userLabel = new JLabel("User: ");
+
+ addWorkspaceBtn = new JButton("Add");
+ addUserBtn = new JButton("Add");
+
+ removeWorkspaceBtn = new JButton("Remove");
+ removeUserBtn = new JButton("Remove");
+
+ importWorkspaceBtn = new JButton("Import");
+ importUserBtn = new JButton("Import");
+
+ workspaceSelection = new JComboBox();
+ userSelection = new JComboBox();
+ wsLocation = new JLabel();
+ wsLocationChooser = new JFileChooser();
+ languageSelection = new JComboBox(Language.values());
+ nOfBackupsSelecteion = new JComboBox(NumberOfBackups.values());
+ userCreatable = new JCheckBox("Allow the users to create new user accounts?");
+
+ populateWorkspaceList();
+ setValues();
+ }
+
+ protected void layoutComponent()
+ {
+ workspaceSelection.setMinimumSize(new Dimension(150,25));
+ workspaceSelection.setMaximumSize(new Dimension(150,25));
+ userSelection.setMinimumSize(new Dimension(150,25));
+ userSelection.setMaximumSize(new Dimension(150,25));
+ languageSelection.setMinimumSize(new Dimension(150,25));
+ languageSelection.setMaximumSize(new Dimension(150,25));
+ nOfBackupsSelecteion.setMinimumSize(new Dimension(75,25));
+ nOfBackupsSelecteion.setMaximumSize(new Dimension(75,25));
+
+ component.add(workspaceLabel);
+ component.add(wsLocationLabel);
+ component.add(wsLanguageLabel);
+ component.add(wsBackupLabel);
+ component.add(userLabel);
+
+ component.add(addWorkspaceBtn);
+ component.add(addUserBtn);
+ component.add(removeWorkspaceBtn);
+ component.add(removeUserBtn);
+ component.add(importWorkspaceBtn);
+ component.add(importUserBtn);
+
+ component.add(workspaceSelection);
+ component.add(userSelection);
+ component.add(wsLocation);
+ component.add(languageSelection);
+ component.add(nOfBackupsSelecteion);
+
+ GroupLayout groupLayout = new GroupLayout(component);
+ component.setLayout(groupLayout);
+
+ groupLayout.setAutoCreateGaps(true);
+ groupLayout.setAutoCreateContainerGaps(true);
+
+ groupLayout.setVerticalGroup(
+ groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceLabel)
+ .addComponent(workspaceSelection)
+ .addComponent(addWorkspaceBtn)
+ .addComponent(removeWorkspaceBtn)
+ .addComponent(importWorkspaceBtn))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(wsLocationLabel)
+ .addComponent(wsLocation))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(userLabel)
+ .addComponent(userSelection)
+ .addComponent(addUserBtn)
+ .addComponent(removeUserBtn)
+ .addComponent(importUserBtn))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(wsLanguageLabel)
+ .addComponent(languageSelection))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(wsBackupLabel)
+ .addComponent(nOfBackupsSelecteion))
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(userCreatable))
+ );
+
+ groupLayout.setHorizontalGroup(
+ groupLayout.createParallelGroup()
+ .addGroup(groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceLabel)
+ .addComponent(userLabel)
+ .addComponent(wsLocationLabel)
+ .addComponent(wsLanguageLabel)
+ .addComponent(wsBackupLabel)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addGroup(groupLayout.createSequentialGroup()
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(workspaceSelection)
+ .addComponent(userSelection)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(addWorkspaceBtn)
+ .addComponent(addUserBtn)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(removeWorkspaceBtn)
+ .addComponent(removeUserBtn)
+ )
+ .addGroup(groupLayout.createParallelGroup()
+ .addComponent(importWorkspaceBtn)
+ .addComponent(importUserBtn)
+ )
+ )
+ .addComponent(wsLocation)
+ .addComponent(languageSelection)
+ .addComponent(nOfBackupsSelecteion)
+ )
+ )
+ .addComponent(userCreatable)
+ );
+ }
+
+ @Override
+ protected void initEventListeners()
+ {
+ /*
+ * WORKSPACE [SELECT in super class]; ADD; REMOVE; IMPORT
+ */
+ super.initEventListeners();
+
+ addWorkspaceBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ addWorkspace();
+ }
+ }).run();
+ }
+ });
+
+ removeWorkspaceBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ deleteWorkspace();
+ }
+ }).run();
+ }
+ });
+
+ importWorkspaceBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ importWorkspace();
+ }
+ }).run();
+ }
+ });
+
+ /*
+ * USER ADD; REMOVE; IMPORT
+ */
+
+ addUserBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ addUser();
+ }
+ }).run();
+ }
+ });
+
+ removeUserBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ removeUser();
+ }
+ }).run();
+ }
+ });
+
+ importUserBtn.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ importUser();
+ }
+ }).run();
+ }
+ });
+
+ /*
+ * LANGUAGE
+ */
+
+ languageSelection.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ new Thread(new Runnable() {
+ public void run() {
+ Language lang = (Language) languageSelection.getSelectedItem();
+ changeLanguage(lang);
+ }
+ }).run();
+ }
+ });
+
+ /*
+ * BACKUP VERSIONS
+ */
+
+ nOfBackupsSelecteion.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ NumberOfBackups n = (NumberOfBackups) nOfBackupsSelecteion.getSelectedItem();
+ WSManager.getInstance().getWorkspaceConfigInstance().setNumberOfBackups(n);
+ }
+ });
+
+ /*
+ * USER CREATION
+ */
+
+ userCreatable.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent arg0) {
+ boolean isAllowed = userCreatable.isSelected();
+ WSManager.getInstance().getWorkspaceConfigInstance().setAllowUserCreation(isAllowed);
+ }
+ });
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * WORKSPACE : ADD, REMOVE, IMPORT
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /**
+ * On creation or when a workspace is set, the input elements must be set according to the current workspace properties.
+ */
+ protected void setValues()
+ {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance();
+
+ // location text
+ if (wc == null)
+ {
+ String wsName = (String) workspaceSelection.getSelectedItem();
+ wsLocation.setText(gc.getWorkspaceDirectory(wsName).toString());
+ wsLocationLabel.setText(Logo.messages.getString("ws.settings.damaged"));
+ disableComponents();
+ return;
+ }else if(wc.isVirtual())
+ wsLocation.setText(Logo.messages.getString("ws.settings.virtual.ws.not.stored"));
+ else
+ wsLocation.setText(wc.getLocation().toString());
+
+ // user list
+ populateUserList();
+
+ // Language
+ languageSelection.setSelectedItem(wc.getLanguage());
+ changeLanguage(wc.getLanguage());
+
+ // Backups
+ nOfBackupsSelecteion.setSelectedItem(wc.getNumberOfBackups());
+
+ // User Creation
+ userCreatable.setSelected(wc.isUserCreationAllowed());
+ }
+
+ /**
+ * Disable controls that depend on the workspace and that cannot be used with a virtual workspace
+ * or a workspace that could not be loaded
+ */
+ protected void disableComponents()
+ {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ removeWorkspaceBtn.setEnabled(wc == null);
+ userSelection.setEnabled(false);
+ addUserBtn.setEnabled(false);
+ removeUserBtn.setEnabled(false);
+ importUserBtn.setEnabled(false);
+ nOfBackupsSelecteion.setEnabled(false);
+ userCreatable.setEnabled(false);
+ }
+
+ @Override
+ /**
+ * Enable if Workspace is successfully entered and if it is not virtual.
+ */
+ protected void enableComponents()
+ {
+ removeWorkspaceBtn.setEnabled(true);
+ userSelection.setEnabled(true);
+ addUserBtn.setEnabled(true);
+ removeUserBtn.setEnabled(true);
+ importUserBtn.setEnabled(true);
+ nOfBackupsSelecteion.setEnabled(true);
+ userCreatable.setEnabled(true);
+ }
+
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * USER : ADD, REMOVE, IMPORT
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+ private void populateUserList()
+ {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ String[] users = wc.getUserList();
+ userSelection.setModel(new DefaultComboBoxModel(users));
+ String lastUser = wc.getLastActiveUser();
+ userSelection.setSelectedItem(lastUser);
+ }
+
+ private void addUser()
+ {
+ String username = getUserText(Logo.messages.getString("ws.settings.enter.user.name"), Logo.messages.getString("ws.settings.create.new.user"));
+
+ if ((username == null) || (username.length() == 0))
+ return;
+
+ if (WSManager.getInstance().getWorkspaceConfigInstance().existsUserLogically(username))
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.settings.user.exists.already"));
+ return;
+ }
+
+ WSManager.getInstance().createUser(username);
+ populateUserList();
+ }
+
+ private void removeUser()
+ {
+ WSManager wsManager = WSManager.getInstance();
+ WorkspaceConfig wc = wsManager.getWorkspaceConfigInstance();
+
+ String username = (String) userSelection.getSelectedItem();
+ if (username == null)
+ return;
+ String userDirectory = wc.getUserDirectroy(username).toString();
+ String message =
+ Logo.messages.getString("ws.settings.want.delete.dir.1")
+ + userDirectory
+ + Logo.messages.getString("ws.settings.want.delete.dir.1");
+
+ boolean ans = getUserYesOrNo(message, Logo.messages.getString("ws.settings.remove.user"));
+
+ WSManager.getInstance().deleteUser(username, ans);
+
+ populateUserList();
+ }
+
+ private void importUser()
+ {
+ File dir = getUserSelectedDirectory();
+ if (dir == null)
+ return;
+
+ if (!WSManager.isUserDirectory(dir))
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("i.am.sorry"),
+ dir.toString() + Logo.messages.getString("ws.settings.not.legal.user.dir"));
+ return;
+ }
+
+ String newName = dir.getName();
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ if (dir.equals(wc.getUserDirectroy(newName)))
+ {
+ DialogMessenger.getInstance().dispatchMessage("This user was already in the list.");
+ return;
+ }
+
+ while (wc.existsUserLogically(newName) || !Storable.checkLegalName(newName))
+ {
+ String msg = wc.existsUserLogically(newName) ?
+ "The user name " + newName + " already exists. Please choose a new name."
+ : "The chosen name contains illegal characters. Please choose a new name.";
+
+ newName = getUserText(msg, "Name Conflict");
+ if (newName == null)
+ return;
+ }
+
+ try {
+ WSManager.getInstance().importUser(dir, newName);
+ } catch (Exception e) {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.settings.could.not.import.user") + e.toString());
+ }
+ populateUserList();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * START : LANGUAGE
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ private void changeLanguage(Language lang) {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+
+ if (wc.getLanguage() != lang)
+ {
+ wc.setLanguage(lang);
+ }
+ }
+
+ protected void setText()
+ {
+ workspaceLabel.setText(Logo.messages.getString("ws.settings.workspace"));
+ wsLocationLabel.setText(Logo.messages.getString("ws.settings.location"));
+ wsLanguageLabel.setText(Logo.messages.getString("ws.settings.language"));
+ wsBackupLabel.setText(Logo.messages.getString("ws.settings.backups"));
+ userLabel.setText(Logo.messages.getString("ws.settings.user"));
+ addWorkspaceBtn.setText(Logo.messages.getString("ws.settings.add"));
+ addUserBtn.setText(Logo.messages.getString("ws.settings.add"));
+ removeWorkspaceBtn.setText(Logo.messages.getString("ws.settings.remove"));
+ removeUserBtn.setText(Logo.messages.getString("ws.settings.remove"));
+ importWorkspaceBtn.setText(Logo.messages.getString("ws.settings.import"));
+ importUserBtn.setText(Logo.messages.getString("ws.settings.import"));
+ userCreatable.setText(Logo.messages.getString("ws.settings.enable.user.account.creation"));
+ }
+}
diff --git a/logo/src/xlogo/interfaces/BasicFileContainer.java b/logo/src/xlogo/interfaces/BasicFileContainer.java
new file mode 100644
index 0000000..5e12d22
--- /dev/null
+++ b/logo/src/xlogo/interfaces/BasicFileContainer.java
@@ -0,0 +1,129 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.interfaces;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface BasicFileContainer
+{
+ public String[] getFileNames();
+
+ public String readFile(String name); // Content
+
+ public String getLastEditedFileName();
+
+ public void createFile(String fileName) throws IOException;
+
+ public String makeUniqueFileName(String base);
+
+ public boolean existsFile(String name);
+
+ public void writeFileText(String fileName, String content) throws IOException;
+
+ public void storeFile(String fileName) throws IOException;
+
+ public void importFile(File filePath) throws IOException;
+
+ public void exportFile(String fileName, File dest) throws IOException;
+
+ public void exportFile(String fileName, File location, String targetName) throws IOException;
+
+ public void renameFile(String oldName, String newName);
+
+ public void removeFile(String fileName);
+
+ public void eraseAll();
+
+ public void openFile(String fileName);
+
+ public void closeFile(String fileName);
+
+ public String getOpenFileName();
+
+ public boolean isFilesListEditable();
+
+ //public boolean hasErrors(String fileName);
+
+ public void addFileListener(FileContainerChangeListener listener);
+
+ public void removeFileListener(FileContainerChangeListener listener);
+
+ public interface FileContainerChangeListener
+ {
+ /**
+ * After the file has been opened
+ * @param file name
+ */
+ public void fileOpened(String file);
+
+ /**
+ * After the file has been closed
+ * @param file name
+ */
+ public void fileClosed(String file);
+
+ /**
+ * After the file has been added
+ * @param file name
+ */
+ public void fileAdded(String file);
+
+ /**
+ * After the file has been removed
+ * @param file name
+ */
+ public void fileRemoved(String file);
+
+ /**
+ * After a file has been renamed
+ * @param oldName
+ * @param newName
+ */
+ public void fileRenamed(String oldName, String newName);
+
+ /**
+ * Applies to the whole container.
+ * @param editEnabled
+ */
+ public void editRightsChanged(boolean editEnabled);
+
+ /**
+ * After a file is updated,
+ * or after a context switch.
+ * @param fileName
+ */
+ //public void errorsDetected(String fileName);
+
+ /**
+ * Only fired if errors existed before a file was updated.
+ * @param fileName
+ */
+ //public void errorsCorrected(String fileName);
+ }
+}
diff --git a/logo/src/xlogo/interfaces/BroadcasterErrorFileContainer.java b/logo/src/xlogo/interfaces/BroadcasterErrorFileContainer.java
new file mode 100644
index 0000000..ceb4bcb
--- /dev/null
+++ b/logo/src/xlogo/interfaces/BroadcasterErrorFileContainer.java
@@ -0,0 +1,40 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.interfaces;
+
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+
+/**
+ * Used only for files list component...
+ * @author Marko
+ *
+ */
+public interface BroadcasterErrorFileContainer extends BasicFileContainer, MessageBroadcaster, FileErrorCollector
+{
+
+}
diff --git a/logo/src/xlogo/interfaces/ErrorDetector.java b/logo/src/xlogo/interfaces/ErrorDetector.java
new file mode 100644
index 0000000..01b59dc
--- /dev/null
+++ b/logo/src/xlogo/interfaces/ErrorDetector.java
@@ -0,0 +1,104 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.interfaces;
+
+import java.util.Collection;
+import java.util.Map;
+
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.procedures.Procedure;
+
+public interface ErrorDetector
+{
+ public boolean hasErrors();
+
+ public Collection<ProcedureErrorMessage> getAllErrors();
+
+ public interface FileErrorCollector extends ErrorDetector
+ {
+ public boolean hasErrors(String fileName);
+
+ public Collection<String> getAllErroneousFiles();
+
+ public void addErrorListener(ErrorListener listener);
+
+ public void removeErrorListener(ErrorListener listener);
+
+ public interface ErrorListener
+ {
+ /**
+ * This event is fired as soon as one or more errors are detected in a file
+ * @param fileName
+ */
+ public void errorsDetected(String fileName);
+
+ /**
+ * This event is fired as soon as all errors are removed from a document
+ * @param fileName
+ */
+ public void allErrorsCorrected(String fileName);
+ }
+ }
+
+ public interface AmbiguityDetector extends ErrorDetector
+ {
+ /**
+ * @return Files whose procedures conflict with procedures of other files.
+ */
+ public Collection<String> getAllConflictingFiles();
+
+ /**
+ * @param fileName
+ * @return whether the specified file conflicts with some other file
+ */
+ boolean hasAmbiguousProcedures(String fileName);
+
+ /**
+ * @param name
+ * @return whether the specified procedure name is defined in more than one file.
+ */
+ public boolean isProcedureAmbiguous(String procedureName);
+
+
+ void addAmbiguityListener(AmbiguityListener listener);
+
+ void removeAmbiguityListener(AmbiguityListener listener);
+
+
+ public interface AmbiguityListener
+ {
+ public void ambiguityDetected(String procedureName, Map<String, Procedure> fileToProcedure);
+
+ /**
+ * @param procedureName
+ * @param fileName When a single file has lost ambiguity for that specific procedure (might still have other ambiguities)
+ */
+ public void ambiguityResolved(String procedureName, String fileName);
+ }
+ }
+}
diff --git a/logo/src/xlogo/interfaces/MessageBroadcaster.java b/logo/src/xlogo/interfaces/MessageBroadcaster.java
new file mode 100644
index 0000000..8021a36
--- /dev/null
+++ b/logo/src/xlogo/interfaces/MessageBroadcaster.java
@@ -0,0 +1,39 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.interfaces;
+
+public interface MessageBroadcaster
+{
+ public void addBroadcastListener(MessageListener listener);
+ public void removeBroadcastListener(MessageListener listener);
+
+ public interface MessageListener
+ {
+ public void messageEvent(String source, String message);
+ }
+}
diff --git a/logo/src/xlogo/interfaces/ProcedureMapper.java b/logo/src/xlogo/interfaces/ProcedureMapper.java
new file mode 100644
index 0000000..32e73bb
--- /dev/null
+++ b/logo/src/xlogo/interfaces/ProcedureMapper.java
@@ -0,0 +1,54 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.interfaces;
+
+import java.util.Collection;
+
+public interface ProcedureMapper
+{
+ public Collection<String> getAllProcedureNames();
+
+ public Collection<String> getAllProcedureNames(String fileName);
+
+ public String getProcedureOwner(String procedureName);
+
+ public void addProcedureMapListener(ProcedureMapListener listener);
+
+ public void removeProcedureMapListener(ProcedureMapListener listener);
+
+ public interface ProcedureMapListener
+ {
+ public void ownerRenamed(String oldName, String newName);
+
+ public void defined(String fileName, Collection<String> procedures);
+ public void defined(String fileName, String procedure);
+
+ public void undefined(String fileName, Collection<String> procedures);
+ public void undefined(String fileName, String procedure);
+ }
+}
diff --git a/logo/src/xlogo/interfaces/X4SModeSwitcher.java b/logo/src/xlogo/interfaces/X4SModeSwitcher.java
new file mode 100644
index 0000000..4ddc248
--- /dev/null
+++ b/logo/src/xlogo/interfaces/X4SModeSwitcher.java
@@ -0,0 +1,82 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.interfaces;
+
+import java.io.IOException;
+
+/**
+ * X4S USER SPACE MODES<br>
+ * 1. User Mode : [lowest priority] (default, a.k.a. root context) cannot be killed, only 1 <br>
+ * 2. Contest Mode : [medium priority] only 0..1 can exist. If this is killed, all current network contexts are killed too.<br>
+ * 3. Network Mode : [highest priority] 0..n can exist, stacked on each other, 1 active at a time.
+ * <p>
+ * General Policy: <br>
+ * 1. Only one context is active at a time. <br>
+ * 2. Priority = if there exists a context which has higher priority, then this context's symbol tables and properties are active. <br>
+ * 3. If there exist multiple contexts of the same priority, then the last created is active. <br>
+ *
+ * @author Marko Zivkovic
+ */
+public interface X4SModeSwitcher
+{
+ public String getSerializedContext();
+
+ public boolean isNetworkMode();
+
+ public boolean isRecordMode();
+
+ public boolean isUserMode();
+
+
+ public void pushNetworkMode(String serializedContext) throws IllegalArgumentException;
+
+ public void popNetworkMode();
+
+
+ public void startRecordMode(String[] fileNames) throws IllegalArgumentException, IllegalStateException, IOException;
+
+ public void stopRecordMode();
+
+
+ public void addModeChangedListener(ModeChangeListener listener);
+
+ public void removeModeChangedListener(ModeChangeListener listener);
+
+
+ public interface ModeChangeListener
+ {
+ public void recordModeStarted();
+
+ public void recordModeStopped();
+
+ public void networkModeStarted();
+
+ public void networkModeStopped();
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/Affichage.java b/logo/src/xlogo/kernel/Affichage.java
new file mode 100644
index 0000000..7da2120
--- /dev/null
+++ b/logo/src/xlogo/kernel/Affichage.java
@@ -0,0 +1,207 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo Description : XLogo is an interpreter for the Logo programming
+ * language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+
+import java.util.Stack;
+import java.util.HashMap;
+import java.awt.event.*;
+
+import javax.swing.SwingUtilities;
+
+import xlogo.Application;
+import xlogo.messages.async.history.HistoryMessenger;
+import xlogo.utils.Utils;
+import xlogo.MemoryChecker;
+import xlogo.Logo;
+
+// Ce thread gère l'animation de la tortue pendant l'exécution
+/**
+ * This Thread is responsible of the turtle animation.
+ * This animation has to be executed in a separated thread, else it will block
+ * the event dispatcher thread
+ */
+public class Affichage extends Thread
+{
+ public static boolean execution_lancee = false;
+ private boolean pause = false;
+ private Application cadre;
+ private StringBuffer instruction;
+ private Souris souris = new Souris();
+ private MemoryChecker memoryChecker = null;
+
+ public Affichage()
+ {
+ }
+
+ public Affichage(Application cadre, StringBuffer instruction)
+ {
+ this.cadre = cadre;
+ this.instruction = instruction;
+ }
+
+ class Souris extends MouseAdapter
+ { // Si on déplace les Scrollbar pendant
+ // le dessin
+ public Souris()
+ {
+ } // Ceci permet d'interrompre momentanément l'exécution
+
+ public void mousePressed(MouseEvent e)
+ {
+ pause = true;
+ }
+
+ public void mouseReleased(MouseEvent e)
+ {
+ pause = false;
+ }
+ }
+
+ public void run()
+ {
+ // currentThread().setPriority(Thread.MIN_PRIORITY);
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run()
+ {
+ cadre.setCommandLine(false);// la ligne de commandes
+ // n'est plus active
+ }
+ });
+ execution_lancee = true;
+ cadre.getDrawPanel().active_souris(); // On active les événements souris
+ // sur
+ // la zone de dessin
+ cadre.scrollArea.getVerticalScrollBar().addMouseListener(souris);
+ cadre.scrollArea.getHorizontalScrollBar().addMouseListener(souris);
+ try
+ {
+ cadre.setCar(-1);
+ cadre.error = false;
+ Interprete.operande = Interprete.operateur = Interprete.drapeau_ouvrante = false;
+ cadre.getKernel().getInstructionBuffer().clear();
+ Interprete.calcul = new Stack<String>();
+ Interprete.nom = new Stack<String>();
+ Interprete.locale = new HashMap<String, String>();
+ Interprete.en_cours = new Stack<String>();
+ memoryChecker = new MemoryChecker(cadre);
+ memoryChecker.start();
+ boolean b = true;
+ while (b)
+ {
+ String st = cadre.getKernel().execute(instruction);
+ if (!st.equals(""))
+ throw new LogoError(Logo.messages.getString("error.whattodo") + " " + st + " ?");
+ if (Interprete.actionInstruction.length() == 0)
+ b = false;
+ else
+ {
+ instruction = Interprete.actionInstruction;
+ Interprete.actionInstruction = new StringBuffer();
+ }
+ }
+ }
+ catch (LogoError e)
+ {
+ // if (st.equals("siwhile")) st=Logo.messages.getString("tantque");
+ while (!Interprete.en_cours.isEmpty() && Interprete.en_cours.peek().equals("("))
+ Interprete.en_cours.pop();
+ if (!cadre.error & !Interprete.en_cours.isEmpty())
+ {
+ HistoryMessenger.getInstance().dispatchError(
+ Logo.messages.getString("dans") + " " + Interprete.en_cours.pop() + ", "
+ + Logo.messages.getString("line") + " " + getLineNumber() + ":\n");
+ }
+ if (!cadre.error)
+ HistoryMessenger.getInstance().dispatchError(Utils.SortieTexte(e.getMessage()) + "\n");
+ abortExecution();
+ }
+ cadre.setCommandLine(true);
+ if (!cadre.viewer3DVisible())
+ cadre.focus_Commande();
+ execution_lancee = false;
+ memoryChecker.kill();
+ cadre.error = false;
+ cadre.scrollArea.getVerticalScrollBar().removeMouseListener(souris);
+ cadre.scrollArea.getHorizontalScrollBar().removeMouseListener(souris);
+ }
+
+ private void abortExecution()
+ {
+ cadre.focus_Commande();
+ cadre.error = true;
+ Interprete.calcul = new Stack<String>();
+ cadre.getKernel().getInstructionBuffer().clear();
+ Primitive.stackLoop = new Stack<LoopProperties>();
+ }
+
+ private int getLineNumber()
+ {
+ String string = Interprete.lineNumber;
+ // System.out.println("bb"+string+"bb");
+ if (string.equals(""))
+ string = cadre.getKernel().getInstructionBuffer().toString();
+ // System.out.println("cc"+string+"cc");
+ int id = string.indexOf("\\l");
+ if (id != -1)
+ {
+ String lineNumber = "";
+ int i = id + 2;
+ char c = string.charAt(i);
+ while (c != ' ')
+ {
+ lineNumber += c;
+ i++;
+ c = string.charAt(i);
+ }
+ // System.out.println(lineNumber);
+ return Integer.parseInt(lineNumber);
+ }
+ return 1;
+ }
+
+ protected boolean isOnPause()
+ {
+ return pause;
+ }
+
+ /**
+ * @param b
+ * @uml.property name="pause"
+ */
+ public void setPause(boolean b)
+ {
+ pause = b;
+ }
+}
diff --git a/logo/src/xlogo/kernel/DrawPanel.java b/logo/src/xlogo/kernel/DrawPanel.java
new file mode 100644
index 0000000..269a0c0
--- /dev/null
+++ b/logo/src/xlogo/kernel/DrawPanel.java
@@ -0,0 +1,2917 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.HeadlessException;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Point;
+
+import javax.swing.JViewport;
+
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Transparency;
+
+import javax.vecmath.Color3f;
+
+import java.awt.FontMetrics;
+import java.awt.Stroke;
+import java.awt.Toolkit;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.CubicCurve2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.QuadCurve2D;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextLayout;
+import java.awt.Dimension;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.CropImageFilter;
+import java.awt.image.ReplicateScaleFilter;
+import java.awt.image.FilteredImageSource;
+
+import com.sun.j3d.utils.geometry.Text2D;
+
+import javax.swing.ImageIcon;
+import javax.swing.SwingUtilities;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+import javax.swing.JPanel;
+import javax.imageio.ImageIO;
+import javax.media.j3d.*;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Matrix3d;
+import javax.vecmath.Point3d;
+
+import java.util.Stack;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.awt.event.*;
+
+import xlogo.Application;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.DrawQuality;
+import xlogo.storage.user.PenShape;
+import xlogo.storage.user.UserConfig;
+import xlogo.utils.Utils;
+import xlogo.Logo;
+import xlogo.kernel.grammar.LogoNumber;
+import xlogo.kernel.grammar.LogoType;
+import xlogo.kernel.grammar.LogoTypeNull;
+import xlogo.kernel.gui.*;
+import xlogo.kernel.perspective.*;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+ public class DrawPanel extends JPanel implements MouseMotionListener,MouseListener {
+ public static final LogoTypeNull nullType=new LogoTypeNull();
+ private static final long serialVersionUID = 1L;
+ public Turtle tortue;
+ public Turtle[] tortues;
+
+ /**
+ * When a turtle is active on screen, its number is added to this stack
+ * @uml.property name="tortues_visibles"
+ * @uml.associationEnd multiplicity="(0 -1)" elementType="java.lang.String"
+ */
+ public Stack<String> tortues_visibles;
+
+ /**
+ * this int indicates the window mode, default 0
+ */
+ protected static int WINDOW_MODE = 0;
+ /**
+ * WINDOW MODE: 0 <br>
+ * Turtles can go out the drawing area
+ */
+ protected static final int WINDOW_CLASSIC=0;
+ /**
+ * WRAP MODE: 1 <br>
+ * Turtles can go out the drawing area and reappear on the other side
+ */
+ protected static final int WINDOW_WRAP=1;
+ /**
+ * CLOSE MODE: 2 <br>
+ * Turtles can't go out the drawing area
+ */
+ protected static final int WINDOW_CLOSE=2;
+ /**
+ * Perspective MODE <br>
+ * The screen is a projection of the 3D universe
+ */
+ protected static final int WINDOW_3D=3;
+
+ /** Boolean for animation mode */
+ public static boolean classicMode=true; // true si classique false si animation
+ /** Animation mode: */
+ public final static boolean MODE_ANIMATION=false;
+ /** Classic mode */
+ public final static boolean MODE_CLASSIC=true;
+
+ /**
+ * default predefined colors
+ */
+ public static final Color[] defaultColors={Color.BLACK,Color.RED,Color.GREEN,Color.YELLOW,Color.BLUE,
+ Color.MAGENTA,Color.CYAN,Color.WHITE,Color.GRAY,Color.LIGHT_GRAY,new Color(128,0,0),new Color(0,128,0),
+ new Color(0,0,128),new Color(255,128,0),Color.PINK,new Color(128,0,255),new Color(153,102,0)};
+
+
+ /**
+ * The id for the drawing font (with primitive label)
+ * @uml.property name="police_etiquette"
+ */
+ protected int police_etiquette;
+ /**
+ * The default drawing area color
+ * @uml.property name="couleurfond"
+ */
+ private Color couleurfond = Color.white;
+ private Shape shape=null;
+ private Line2D line;
+ private Rectangle2D rec;
+ private Application cadre;
+ /** This Image is used for Buffering the drawing*/
+ public static BufferedImage dessin;
+ /**
+ * Graphics of the BufferedImage dessin
+ * @uml.property name="g"
+ */
+ private Graphics2D g;
+ /** The scale for the zoom*/
+ public static double zoom=1;
+
+
+ /**
+ * All Gui Objects on the drawing area are stored in the GuiMap gm
+ * @uml.property name="gm"
+ * @uml.associationEnd multiplicity="(1 1)"
+ */
+ private GuiMap gm;
+ /**
+ * The Stroke for the triangle turtle
+ * @uml.property name="crayon_triangle"
+ */
+ private BasicStroke crayon_triangle = new BasicStroke(1);
+ /**
+ * Tools for 3D Mode
+ * @uml.property name="w3d"
+ * @uml.associationEnd
+ */
+ private World3D w3d=null;
+ /**
+ * Boolean that indicates if the interpreter is recording polygon in 3D Mode
+ */
+ protected static int record3D=0;
+ protected final static int record3D_NONE=0;
+ protected final static int record3D_POLYGON=1;
+ protected final static int record3D_LINE=2;
+ protected final static int record3D_POINT=3;
+ protected final static int record3D_TEXT=4;
+
+ /**
+ * Boolean that indicates if the interpreter is recording polygon in 2D Mode
+ */
+ private static int record2D=0;
+ private final static int record2D_NONE=0;
+ private final static int record2D_POLYGON=1;
+ private Vector<Point2D.Double> stackTriangle;
+
+
+ protected static Element3D poly;
+
+ private double[] coords;
+ private double oldx;
+ private double oldy;
+ private double x1;
+ private double y1;
+ private double x2;
+ private double y2;
+ // Were used for clipping
+ // private double nx,ny,vx,vy,factor,length;
+ // private GeneralPath gp;
+ // private Arc2D clipArc;
+ private Arc2D arc;
+ /**
+ * Button number when user click on the drawing area
+ * @uml.property name="bouton_souris"
+ */
+ private int bouton_souris=0; // Numéro du bouton de souris appuyé sur la zone de dessin
+ /**
+ * Last coords for last mouse event
+ * @uml.property name="possouris"
+ */
+ private String possouris="[ 0 0 ] "; // Coordonnées du point du dernier événement souris
+
+ /**
+ * Notify if a mouse event has occured
+ * @uml.property name="lissouris"
+ */
+ private boolean lissouris=false; //Indique si un événement souris est intervenu depuis le debut du programme
+ /**
+ * The rectangular selection zone
+ * @uml.property name="selection"
+ */
+ private Rectangle selection;
+ /**
+ * Color for the rectangular selection
+ * @uml.property name="colorSelection"
+ */
+ private Color colorSelection;
+ /**
+ * The First clicked point when the rectangular selection is created
+ * @uml.property name="origine"
+ */
+ Point origine;
+ public DrawPanel(Application cadre){
+ this.gm= cadre.getKernel().getWorkspace().getGuiMap();
+ setLayout(null);
+ this.setPreferredSize(new Dimension(
+ (int)(WSManager.getUserConfig().getImageWidth()*zoom),
+ (int)(WSManager.getUserConfig().getImageHeight()*zoom)));
+ this.cadre=cadre;
+ addMouseListener(this);
+ addMouseMotionListener(this);
+ initGraphics();
+ }
+ /**
+ * This method is used to draw for primitive "forward"
+ * @param arg LogoType which represents the number of steps to move
+ */
+ protected LogoType av(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return av(ln.getValue());
+
+ }
+ /**
+ * This method is used to draw for primitive "backward"
+ * @param arg LogoType which represents the number of steps to move
+ */
+ protected LogoType re(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return av(-ln.getValue());
+
+ }
+ /**
+ * This method is used to draw for primitive "right"
+ * @param arg LogoType which represents the number of steps to rotate
+ */
+ protected LogoType td(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return td(ln.getValue());
+
+ } /**
+ * This method is used to draw for primitive "left"
+ * @param arg LogoType which represents the number of steps to rotate
+ */
+ protected LogoType tg(LogoType number){
+ if (number.isException()) return number;
+ LogoNumber ln=(LogoNumber)number;
+ return td(-ln.getValue());
+
+ }
+
+ /**
+ * This method is used to draw for primitive "forward" and "backward"
+ * @param arg Number of steps
+ */
+ protected LogoType av(double arg) {
+ // Graphics2D g=(Graphics2D)dessin.getGraphics();
+
+ oldx = tortue.corX;
+ oldy = tortue.corY;
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_CLASSIC) { //mode fenetre
+ montrecacheTortue(false);
+
+ tortue.corX = tortue.corX + arg
+ * Math.cos(tortue.angle);
+ tortue.corY = tortue.corY - arg
+ * Math.sin(tortue.angle);
+ if (tortue.isPenDown()) {
+ g.setStroke(tortue.stroke); // TODO Marko : fix penerase problem. TODO also for other ops, circle etc
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (null==line) line=new Line2D.Double();
+ /* if (null==gp) gp=new GeneralPath();
+ else gp.reset();*/
+ if (oldx < tortue.corX){
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ if (oldx>tortue.corX){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else if (oldx==tortue.corX){
+ if (oldy<tortue.corY){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else{
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ }
+
+ line.setLine(x1,y1,x2,y2);
+
+ /*
+ // perpendicular vector
+ nx=y1-y2;
+ ny=x2-x1;
+ length=Math.sqrt(nx*nx+ny*ny);
+ if (length!=0){
+ factor=(1+tortue.getPenWidth())/length;
+ vx=x2-x1;
+ vy=y2-y1;
+ gp.moveTo((float)(x1-vx*factor-nx*factor),
+ (float)(y1-vy*factor-ny*factor));
+ gp.lineTo((float)(x1-vx*factor+nx*factor),
+ (float)(y1-vy*factor+ny*factor));
+ gp.lineTo((float)(x2+vx*factor+nx*factor),
+ (float)(y2+vy*factor+ny*factor));
+ gp.lineTo((float)(x2+vx*factor-nx*factor),
+ (float)(y2+vy*factor-ny*factor));
+ gp.lineTo((float)(x1-vx*factor-nx*factor),
+ (float)(y1-vy*factor-ny*factor));
+ }
+ else{
+ float width=tortue.getPenWidth()+0.5f;
+ gp.moveTo((float)(x1-width),
+ (float)(y1-width));
+ gp.lineTo((float)(x1+width),
+ (float)(y1-width));
+ gp.lineTo((float)(x1+width),
+ (float)(y1+width));
+ gp.lineTo((float)(x1-width),
+ (float)(y1+width));
+ gp.lineTo((float)(x1-width),
+ (float)(y1-width));
+ }
+ shape=gp;*/
+ tryRecord2DMode(tortue.corX,tortue.corY);
+ g.draw(line);
+ //if (!tortue.isVisible())
+ clip();
+ // g.dispose();
+ }
+ montrecacheTortue(true);
+ } else if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_WRAP) { //mode enroule
+ trace_enroule(arg, oldx, oldy);
+ } else if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_CLOSE) { //mode clos
+ try {
+ trace_ferme(oldx, oldy, arg);
+ } catch (LogoError e) {
+ }
+ }
+ else if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_3D){
+ montrecacheTortue(false);
+ tortue.X=tortue.X+arg*tortue.getRotationMatrix()[0][1];
+ tortue.Y=tortue.Y+arg*tortue.getRotationMatrix()[1][1];
+ tortue.Z=tortue.Z+arg*tortue.getRotationMatrix()[2][1];
+
+ double tmp[]=new double[3];
+ tmp[0]=tortue.X;
+ tmp[1]=tortue.Y;
+ tmp[2]=tortue.Z;
+
+ tmp=this.toScreenCoord(tmp,true);
+ tortue.corX = tmp[0];
+ tortue.corY = tmp[1];
+
+
+ if (tortue.isPenDown()) {
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (null==line) line=new Line2D.Double();
+
+ if (oldx < tortue.corX){
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ if (oldx>tortue.corX){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else if (oldx==tortue.corX){
+ if (oldy<tortue.corY){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else{
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ }
+
+ line.setLine(x1,y1,x2,y2);
+
+ g.draw(line);
+ clip();
+ }
+ montrecacheTortue(true);
+ }
+ return DrawPanel.nullType;
+ }
+
+ /**
+ * This method is used for drawing with primitive "right" or "left"
+ * @param arg The angle to rotate
+ */
+ protected LogoType td(double arg) {
+// System.out.println(tortue.angle);
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (!enabled3D()){
+ tortue.heading = ((tortue.heading + arg) % 360 + 360) % 360;
+ tortue.angle = Math.toRadians(90 - tortue.heading);
+ }
+ else{
+ tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationZ(-arg)));
+ double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix());
+ tortue.heading=tmp[2];
+ tortue.roll=tmp[1];
+ tortue.pitch=tmp[0];
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande = false;
+
+ return DrawPanel.nullType;
+ }
+ /**
+ * This method is used for drawing with primitive "rightroll" or "leftroll"
+ * @param arg
+ */
+ protected void rightroll(double arg) {
+// System.out.println(tortue.angle);
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (enabled3D()){
+ tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationY(-arg)));
+ double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix());
+ tortue.heading=tmp[2];
+ tortue.roll=tmp[1];
+ tortue.pitch=tmp[0];
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande = false;
+ }
+ /**
+ * This method is used for drawing with primitive "uppitch" or "downpitch"
+ * @param arg
+ */
+ protected void uppitch(double arg) {
+// System.out.println(tortue.angle);
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (enabled3D()){
+ tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationX(arg)));
+ double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix());
+ tortue.heading=tmp[2];
+ tortue.roll=tmp[1];
+ tortue.pitch=tmp[0];
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande = false;
+ }
+ /**
+ * This method set the turtle's Roll
+ * @param arg The new roll
+ */
+ protected void setRoll(double arg){
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.roll=arg;
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ * This method set the turtle's heading
+ * @param arg The new heading
+ */
+ protected void setHeading(double arg){
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.heading=arg;
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ * This method set the turtle's pitch
+ * @param arg The new pitch
+ */
+ protected void setPitch(double arg){
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.pitch=arg;
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ *
+ * This method set the turtle's orientation
+ * @param arg The new orientation
+ * @throws LogoError If the list doesn't contain three numbers
+ */
+ protected void setOrientation(String arg) throws LogoError{
+ initCoords();
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ extractCoords(arg,Utils.primitiveName("3d.setorientation"));
+ tortue.roll = coords[0];
+ tortue.pitch = coords[1];
+ tortue.heading = coords[2];
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ Interprete.operande=false;
+ }
+ /**
+ * Primitive "origine"
+ */
+ protected void origine(){ // primitive origine
+ try {
+ if (!enabled3D())
+ fpos("0 0");
+ else fpos("0 0 0");
+ } catch (LogoError e) {
+ }
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ tortue.heading = 0;
+ tortue.angle = Math.PI / 2;
+ tortue.roll=0;
+ tortue.pitch=0;
+ if (enabled3D())
+ tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading));
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ }
+
+
+ /**
+ * Primitive distance
+ * @param liste The coords
+ * @param nom
+ * @return The distance from the turtle position to this point
+ * @throws LogoError If bad format list
+ */
+ protected double distance(String liste) throws LogoError {
+
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("distance"));
+ double distance;
+ if (!enabled3D()){
+ coords=this.toScreenCoord(coords,false);
+ distance = Math.sqrt(Math.pow(tortue.corX - coords[0], 2)
+ + Math.pow(tortue.corY - coords[1], 2));
+ }
+ else distance= Math.sqrt(Math.pow(tortue.X - coords[0], 2)
+ + Math.pow(tortue.Y - coords[1], 2)+Math.pow(tortue.Z - coords[2], 2));
+ return distance;
+ }
+ protected double[] vers3D(String liste) throws LogoError{
+ double[] tmp=new double [3];
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("vers"));
+ tmp[0]=coords[0]-tortue.X;
+ tmp[1]=coords[1]-tortue.Y;
+ tmp[2]=coords[2]-tortue.Z;
+ double length=Math.sqrt(Math.pow(tmp[0],2)+Math.pow(tmp[1],2)+Math.pow(tmp[2],2));
+ if (length==0) return tmp;
+ tmp[0]=tmp[0]/length;
+ tmp[1]=tmp[1]/length;
+ tmp[2]=tmp[2]/length;
+ double heading=Math.acos(tmp[1]);
+ double f=Math.sin(heading);
+ double tr_x=-tmp[0]/f;
+ double tr_y=-tmp[2]/f;
+ double roll=Math.atan2(tr_y, tr_x);
+ tmp[0]=-Math.toDegrees(roll);
+ tmp[1]=0;
+ tmp[2]=-Math.toDegrees(heading);
+ for (int i=0;i<3;i++){
+ if (tmp[i]<0) tmp[i]+=360;
+ }
+ return tmp;
+ }
+
+ /**
+ * Primitive towards in 2D MODE
+ * @param liste the coordinate for the point
+ * @return the rotation angle
+ * @throws LogoError if Bad format List
+ */
+ protected double vers2D(String liste) throws LogoError{
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("vers"));
+ double angle;
+ coords=this.toScreenCoord(coords, false);
+ if (tortue.corY == coords[1]) {
+ if (coords[0] > tortue.corX)
+ angle = 90;
+ else if (coords[0] == tortue.corX)
+ angle = 0;
+ else
+ angle = 270;
+ }
+ else if (tortue.corX == coords[0]) {
+ if (tortue.corY > coords[1])
+ angle = 0;
+ else
+ angle = 180;
+ }
+ else {
+ angle = Math.toDegrees(Math.atan(Math
+ .abs(coords[0] - tortue.corX)
+ / Math.abs(tortue.corY - coords[1])));
+ // System.out.println(coords[0] - tortue.corX+" "+Math.abs(tortue.corY - coords[1])+" "+angle);
+ if (coords[0] > tortue.corX && coords[1] > tortue.corY)
+ angle = 180 - angle; // 2eme quadrant
+ else if (coords[0] < tortue.corX && coords[1] > tortue.corY)
+ angle = 180 + angle; // 3eme quadrant
+ else if (coords[0] < tortue.corX && coords[1] < tortue.corY)
+ angle = 360 - angle; // 4eme quadrant
+ }
+ return angle;
+ }
+ /**
+ * Draw with the primitive "setposition" in 2D mode or 3D
+ * @param liste The list with the coordinates to move
+ * @throws LogoError If the coordinates are invalid
+ */
+ protected void fpos(String liste) throws LogoError {
+ initCoords();
+ oldx = tortue.corX;
+ oldy = tortue.corY;
+ extractCoords(liste,Utils.primitiveName("drawing.fpos"));
+ montrecacheTortue(false);
+ if (enabled3D()) {
+ tortue.X = coords[0];
+ tortue.Y = coords[1];
+ tortue.Z = coords[2];
+ }
+ coords=toScreenCoord(coords,true);
+
+ tortue.corX=coords[0];
+ tortue.corY=coords[1];
+ if (tortue.isPenDown()) {
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (null==line) line=new Line2D.Double();
+ if (oldx < tortue.corX){
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ if (oldx>tortue.corX){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else if (oldx==tortue.corX){
+ if (oldy<tortue.corY){
+ x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY;
+ }
+ else{
+ x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY;
+ }
+ }
+ line.setLine(x1,y1,x2,y2);
+ tryRecord2DMode(tortue.corX,tortue.corY);
+ g.draw(line);
+ clip();
+ }
+ montrecacheTortue(true);
+ }
+ public void drawEllipseArc(double xAxis,double yAxis, double angleRotation,double xCenter,double yCenter, double angleStart, double angleExtent){
+ montrecacheTortue(false);
+ arc=new Arc2D.Double(-xAxis,-yAxis,2*xAxis,2*yAxis,angleStart,angleExtent,Arc2D.Double.OPEN);
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ double tmpx=WSManager.getUserConfig().getImageWidth()/2+xCenter;
+ double tmpy=WSManager.getUserConfig().getImageHeight()/2-yCenter;
+ g.translate(tmpx, tmpy);
+ g.rotate(-angleRotation);
+ g.draw(arc);
+ g.rotate(angleRotation);
+ g.translate(-tmpx, -tmpy);
+ /* if (null==clipArc) clipArc=new Arc2D.Double();
+ clipArc.setArcByCenter(tortue.corX,tortue.corY,
+ rayon+2+tortue.getPenWidth(),0,360, Arc2D.OPEN);*/
+ clip();
+ montrecacheTortue(true); // on efface la tortue si elle st visible
+ }
+ /**
+ * This method draw an arc on the drawing area
+ * @param rayon The radius
+ * @param pangle Starting angle
+ * @param fangle End angle
+ * @throws LogoError
+ */
+ protected void arc(double rayon, double pangle, double fangle) throws LogoError {
+ // Put fangle and pangle between 0 and 360
+ fangle = ((90 - fangle) % 360);
+ pangle = ((90 - pangle) % 360);
+ if (fangle<0) fangle+=360;
+ if (pangle<0) pangle+=360;
+ // Calculate angle extend
+ double angle=pangle-fangle;
+ if (angle<0) angle+=360;
+ montrecacheTortue(false);
+ if (null==arc) arc=new Arc2D.Double();
+ if (!enabled3D()){
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>();
+ arc2D(tortue.corX,tortue.corY,rayon,fangle,angle);
+
+ /* if (null==gp) gp=new GeneralPath();
+ else gp.reset();
+ gp.moveTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth());
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));
+ gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()),
+ (float)(tortue.corY-rayon-tortue.getPenWidth()));*/
+/* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortue.corX-rayon-tortue.getPenWidth(),
+ tortue.corY-rayon-tortue.getPenWidth(),
+ 2*(rayon+tortue.getPenWidth()),2*(rayon+tortue.getPenWidth()));*/
+ clip();
+
+ }
+ else{
+ arcCircle3D(rayon,fangle,angle);
+ }
+ montrecacheTortue(true);
+ }
+ private void arc2D(double x, double y, double radius,double fangle, double angle){
+ arc.setArcByCenter(x,y,radius,
+ fangle,angle, Arc2D.OPEN);
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ g.draw(arc);
+ clip();
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){
+ if (x+radius>w&& x<=w){
+ pt=new Point2D.Double(-w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(-w+x,y,radius,fangle,angle);
+ }
+ }
+ if (x-radius<0&& x>=0){
+ pt=new Point2D.Double(w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(w+x,y,radius,fangle,angle);
+ }
+ }
+ if (y-radius<0&& y>=0){
+ pt=new Point2D.Double(x,h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(x,h+y,radius,fangle,angle);
+ }
+ }
+ if (y+radius>h&&y<=h){
+ pt=new Point2D.Double(x,-h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ arc2D(x,-h+y,radius,fangle,angle);
+ }
+ }
+ }
+ }
+
+
+ private void arcCircle3D(double radius,double angleStart,double angleExtent) throws LogoError{
+ if (null==arc) arc=new Arc2D.Double();
+ arc.setArcByCenter(0,0,radius,
+ angleStart,angleExtent, Arc2D.OPEN);
+ Shape s=transformShape(arc);
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ g.draw(s);
+ if (DrawPanel.record3D==DrawPanel.record3D_LINE||DrawPanel.record3D==DrawPanel.record3D_POLYGON){
+ recordArcCircle3D(radius,angleStart,angleExtent);
+ }
+ }
+
+
+ /**
+ *
+ * returns the color for the pixel "ob"
+ * @param liste: The list containing the coordinates of the pixel
+ * @return Color of this pixel
+ * @throws LogoError If the list doesn't contain coordinates
+ */
+ protected Color guessColorPoint(String liste) throws LogoError {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("tc"));
+ coords=toScreenCoord(coords,false);
+ int couleur = -1;
+ int x=(int)coords[0];
+ int y=(int)coords[1];
+ if (0 < x && x < w && 0 < y && y < h) {
+ couleur = DrawPanel.dessin.getRGB(x, y);
+ }
+ return new Color(couleur);
+ }
+ /**
+ * This method draw a circle from the turtle position on the drawing area
+ * @param radius The radius of the circle
+ * @throws LogoError
+ */
+ protected void circle(double radius) throws LogoError {
+ montrecacheTortue(false);
+ if (null==arc) arc=new Arc2D.Double();
+ if (!enabled3D()){
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>();
+ circle2D(tortue.corX,tortue.corY,radius);
+ /* if (null==clipArc) clipArc=new Arc2D.Double();
+ clipArc.setArcByCenter(tortue.corX,tortue.corY,
+ rayon+2+tortue.getPenWidth(),0,360, Arc2D.OPEN);*/
+ }
+ else{
+ circle3D(radius);
+ }
+ montrecacheTortue(true); // on efface la tortue si elle st visible
+ }
+ /**
+ * This method draws a circle in 2D mode in WRAP mode, makes recursion to draw all circle part on the screen
+ * @param x x circle center
+ * @param y y circle center
+ * @param circle radius
+ * @uml.property name="pt"
+ */
+ private Point2D.Double pt;
+ /**
+ * @uml.property name="centers"
+ */
+ private Vector <Point2D.Double> centers;
+ private void circle2D(double x,double y, double radius){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ arc.setArcByCenter(x,y,radius,
+ 0,360, Arc2D.OPEN);
+
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ g.draw(arc);
+ clip();
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){
+ if (x+radius>w&& x<=w){
+ pt=new Point2D.Double(-w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(-w+x,y,radius);
+ }
+ }
+ if (x-radius<0&& x>=0){
+ pt=new Point2D.Double(w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(w+x,y,radius);
+ }
+ }
+ if (y-radius<0&& y>=0){
+ pt=new Point2D.Double(x,h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(x,h+y,radius);
+ }
+ }
+ if (y+radius>h&&y<=h){
+ pt=new Point2D.Double(x,-h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ circle2D(x,-h+y,radius);
+ }
+ }
+ }
+ }
+
+ /**
+ * used for drawing with primitive "dot"
+ * @param liste The list with the dot coordinates
+ * @throws LogoError If the list is invalid coordinates
+ */
+ protected void point(String liste) throws LogoError {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ initCoords();
+ extractCoords(liste,Utils.primitiveName("drawing.point"));
+ coords=toScreenCoord(coords,true);
+// System.out.println(coords[0]+" "+coords[1]+" "+h+" "+w);
+ if (coords[0]>0 && coords[1]>0 && coords[0]<w && coords[1] < h) {
+ if (tortue.isPenReverse()) {
+ g.setColor(couleurfond);
+ g.setXORMode(tortue.couleurcrayon);
+
+ } else {
+ g.setColor(tortue.couleurcrayon);
+ g.setPaintMode();
+ }
+ if (rec==null) rec=new Rectangle2D.Double();
+ // High quality
+ if (uc.getQuality()==DrawQuality.HIGH){
+ double width=tortue.getPenWidth();
+ rec.setRect(coords[0]-width+0.5,coords[1]-width+0.5,
+ 2*width,2*width);
+ }
+ // Normal or Low Quality
+ else{
+ // penWidth is 2k or 2k+1??
+ int intWidth=(int)(2*tortue.getPenWidth()+0.5);
+ if (intWidth%2==1){
+ double width=tortue.getPenWidth()-0.5;
+// System.out.println(coords[0]+" "+coords[1]);
+ rec.setRect(coords[0]-width,coords[1]-width,
+ 2*width+1,2*width+1);
+ }
+ else {
+ double width=tortue.getPenWidth();
+ rec.setRect(coords[0]-width,coords[1]-width,
+ 2*width,2*width);
+ }
+ }
+ if (uc.getPenShape()==PenShape.SQUARE){ // MAKE ENUM
+ g.fill(rec);
+ }
+ else if (uc.getPenShape()==PenShape.OVAL){
+ if (null==arc) arc=new Arc2D.Double();
+ arc.setArcByCenter(coords[0],coords[1],0,0,360,Arc2D.OPEN);
+ g.draw(arc);
+ }
+ clip();
+ }
+ }
+
+
+
+ /**
+ * @throws LogoError
+ *
+ */
+ private void circle3D(double radius) throws LogoError{
+
+ // In camera world,
+ // the circle is the intersection of
+ // - a plane with the following equation: ax+by+cz+d=0 <-> f(x,y,z)=0
+ // - and a sphere with the following equation: (x-tx)^2+(y-ty)^2+(z-tz)^2=R^2 <-> g(x,y,z)=0
+ // I found the cone equation resolving f(x/lambda,y/lambda,z/lambda)=0=g(x/lambda,y/lambda,z/lambda)
+
+ double[] v=new double[3];
+ for(int i=0;i<3;i++){
+ v[i]=tortue.getRotationMatrix()[i][2];
+ }
+ v[0]+=w3d.xCamera;
+ v[1]+=w3d.yCamera;
+ v[2]+=w3d.zCamera;
+ w3d.toCameraWorld(v);
+ // Now v contains coordinates of a normal vector to the plane in camera world coordinates
+ double a=v[0];
+ double b=v[1];
+ double c=v[2];
+
+ // We convert the turtle coordinates
+ v[0]=tortue.X;
+ v[1]=tortue.Y;
+ v[2]=tortue.Z;
+ w3d.toCameraWorld(v);
+
+ double x=v[0];
+ double y=v[1];
+ double z=v[2];
+ // We calculate the number d for the plane equation
+ double d=-a*x-b*y-c*z;
+
+ // We have to work with Bigdecimal because of precision problems
+
+ BigDecimal[] big=new BigDecimal[6];
+ BigDecimal bx=new BigDecimal(x);
+ BigDecimal by=new BigDecimal(y);
+ BigDecimal bz=new BigDecimal(z);
+ BigDecimal ba=new BigDecimal(a);
+ BigDecimal bb=new BigDecimal(b);
+ BigDecimal bc=new BigDecimal(c);
+ BigDecimal bd=new BigDecimal(d);
+ BigDecimal deux=new BigDecimal("2");
+ BigDecimal screenDistance=new BigDecimal(w3d.screenDistance);
+ BigDecimal bradius=new BigDecimal(String.valueOf(radius));
+
+ // Now we calculate the coefficient for the conic ax^2+bxy+cy^2+dx+ey+f=0
+ // Saved in an array
+
+ // lambda=(x*x+y*y+z*z-radius*radius);
+ BigDecimal lambda=bx.pow(2).add(by.pow(2)).add(bz.pow(2)).subtract(bradius.pow(2));
+
+ // x^2 coeff
+ // d*d+2*d*x*a+a*a*lambda;
+ big[0]=bd.pow(2).add(bd.multiply(bx).multiply(ba).multiply(deux)).add(ba.pow(2).multiply(lambda));
+ // xy coeff
+ // 2*d*x*b+2*d*y*a+2*a*b*lambda;
+ big[1]=deux.multiply(bd).multiply(bx).multiply(bb).add(deux.multiply(bd).multiply(by).multiply(ba)).add(deux.multiply(ba).multiply(bb).multiply(lambda));
+ // y^2 coeff
+ // d*d+2*d*y*b+b*b*lambda;
+ big[2]=bd.pow(2).add(bd.multiply(by).multiply(bb).multiply(deux)).add(bb.pow(2).multiply(lambda));
+ // x coeff
+ // 2*w3d.screenDistance*(d*x*c+d*z*a+lambda*a*c);
+ big[3]=deux.multiply(screenDistance).multiply(bd.multiply(bx).multiply(bc).add(bd.multiply(bz).multiply(ba)).add(lambda.multiply(ba).multiply(bc)));
+ // y coeff
+ // 2*w3d.screenDistance*(d*y*c+d*z*b+lambda*b*c);
+ big[4]=deux.multiply(screenDistance).multiply(bd.multiply(by).multiply(bc).add(bd.multiply(bz).multiply(bb)).add(lambda.multiply(bb).multiply(bc)));
+ // Numbers
+ // Math.pow(w3d.screenDistance,2)*(d*d+2*d*z*c+lambda*c*c);
+ big[5]=screenDistance.pow(2).multiply(bd.pow(2).add(deux.multiply(bd).multiply(bz).multiply(bc)).add(lambda.multiply(bc.pow(2))));
+ new Conic(this,big);
+ if (DrawPanel.record3D==DrawPanel.record3D_LINE||DrawPanel.record3D==DrawPanel.record3D_POLYGON){
+ recordArcCircle3D(radius,0,360);
+ }
+ }
+ /**
+ * This method records this circle in the polygon's List
+ * @param radius The circle's radius
+ * @param angleStart The starting Angle
+ * @param angleExtent The angle for the sector
+ * @throws LogoError
+ */
+ public void recordArcCircle3D(double radius,double angleStart,double angleExtent) throws LogoError{
+ double[][] d=tortue.getRotationMatrix();
+ Matrix3d m=new Matrix3d(d[0][0],d[0][1],d[0][2],d[1][0],d[1][1],d[1][2],d[2][0],d[2][1],d[2][2]);
+ // Vector X
+ Point3d v1=new Point3d(radius/1000,0,0);
+ Transform3D t=new Transform3D(m,new Vector3d(),1);
+ t.transform(v1);
+ // Vector Y
+ Point3d v2=new Point3d(0,radius/1000,0);
+ t.transform(v2);
+
+ // Turtle position
+ Point3d pos=new Point3d(tortue.X/1000,tortue.Y/1000,tortue.Z/1000);
+ int indexMax=(int)angleExtent;
+ if (indexMax!=angleExtent) indexMax+=2;
+ else indexMax+=1;
+ if (null!=DrawPanel.poly&&DrawPanel.poly.getVertexCount()>1)
+ DrawPanel.poly.addToScene();
+ if (DrawPanel.record3D==DrawPanel.record3D_POLYGON) {
+ DrawPanel.poly=new ElementPolygon(cadre.getViewer3D());
+ DrawPanel.poly.addVertex(pos, tortue.couleurcrayon);
+ }
+ else {
+ DrawPanel.poly=new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ }
+
+ for(int i=0;i<indexMax-1;i++){
+ Point3d tmp1=new Point3d(v1);
+ tmp1.scale(Math.cos(Math.toRadians(angleStart+i)));
+ Point3d tmp2=new Point3d(v2);
+ tmp2.scale(Math.sin(Math.toRadians(angleStart+i)));
+ tmp1.add(tmp2);
+ tmp1.add(pos);
+ DrawPanel.poly.addVertex(tmp1, tortue.couleurcrayon);
+ }
+ Point3d tmp1=new Point3d(v1);
+ tmp1.scale(Math.cos(Math.toRadians(angleStart+angleExtent)));
+ Point3d tmp2=new Point3d(v2);
+ tmp2.scale(Math.sin(Math.toRadians(angleStart+angleExtent)));
+ tmp1.add(tmp2);
+ tmp1.add(pos);
+ DrawPanel.poly.addVertex(tmp1, tortue.couleurcrayon);
+ }
+
+/**
+ * Load an image and draw it on the drawing area
+ * @param image The image to draw
+ */
+ protected void chargeimage(BufferedImage image) {
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ g.setPaintMode();
+ g.translate(tortue.corX, tortue.corY);
+ g.rotate(-tortue.angle);
+ g.drawImage(image, null, 0,0);
+ g.rotate(tortue.angle);
+ g.translate(-tortue.corX, -tortue.corY);
+
+ clip();
+// repaint();
+/* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortue.corX,tortue.corY,
+ image.getWidth(),image.getHeight());*/
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ }
+ /**
+ * To guess the length before going out the drawing area in WRAP mode
+ * @param mini The minimum distance before leaving
+ * @param maxi The maximum distance before leaving
+ * @param oldx The X turtle location
+ * @param oldy The Y turtle location
+ * @return the number of steps (Recursive dichotomy)
+ */
+ private double trouve_longueur(double mini, double maxi, double oldx, double oldy) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ // renvoie la longueur dont on peut encore avancer
+ if (Math.abs(maxi - mini) < 0.5){
+ return (mini);}
+ else {
+ double milieu = (mini + maxi) / 2;
+ double nx = oldx + milieu * Math.cos(tortue.angle);
+ double ny = oldy - milieu * Math.sin(tortue.angle);
+ if (nx < 0 || nx > w|| ny < 0 || ny > h)
+ return trouve_longueur(mini, milieu, oldx, oldy);
+ else
+ return trouve_longueur(milieu, maxi, oldx, oldy);
+ }
+ }
+/**
+ * This method is used for drawing with primitive forward, backward in WRAP MODE
+ * @param arg the length to forward
+ * @param oldx X position
+ * @param oldy Y position
+ */
+ private void trace_enroule(double arg, double oldx, double oldy) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ boolean re = false;
+ if (arg < 0) {
+ re = true;
+ }
+ double diagonale=Math.sqrt(Math.pow(w,2)+Math.pow(h,2))+1;
+ double longueur;
+ if (re)
+ longueur = trouve_longueur(0, -diagonale, oldx, oldy);
+ else
+ longueur = trouve_longueur(0, diagonale, oldx, oldy);
+// System.out.println(diagonale+" "+oldx+" "+oldy);
+ while (Math.abs(longueur) < Math.abs(arg)) {
+ // System.out.println(Math.abs(longueur)+" "+Math.abs(arg));
+ arg -= longueur;
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ av(longueur);
+ //System.out.println(Math.abs(longueur)+" "+Math.abs(arg));
+ if (cadre.error)
+ break; //permet d'interrompre avec le bouton stop
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_WRAP;
+ if (uc.getTurtleSpeed() != 0) {
+ try {
+ Thread.sleep(uc.getTurtleSpeed() * 5);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (tortue.isVisible())
+ this.montrecacheTortue(false);
+ if (re) tortue.heading=(tortue.heading+180)%360;
+ if (tortue.corX > w-1
+ && (tortue.heading < 180 && tortue.heading != 0)) {
+ tortue.corX = 0;
+ if (tortue.corY > h-1
+ && (tortue.heading > 90 && tortue.heading < 270))
+ tortue.corY = 0;
+ else if (tortue.corY < 1
+ && (tortue.heading < 90 || tortue.heading > 270))
+ tortue.corY = h;
+ } else if (tortue.corX < 1 && tortue.heading > 180) {
+ tortue.corX = w;
+ if (tortue.corY > h-1
+ && (tortue.heading > 90 && tortue.heading < 270))
+ tortue.corY = 0;
+ else if (tortue.corY < 1
+ && (tortue.heading < 90 || tortue.heading > 270))
+ tortue.corY = h;
+ } else if (tortue.corY > h-1)
+ tortue.corY = 0;
+ else if (tortue.corY < 1)
+ tortue.corY = h;
+ if (re) tortue.heading=(tortue.heading+180)%360;
+ if (tortue.isVisible())
+ this.montrecacheTortue(true);
+ if (re)
+ longueur = trouve_longueur(0, -diagonale, tortue.corX,
+ tortue.corY);
+ else
+ longueur = trouve_longueur(0, diagonale, tortue.corX,
+ tortue.corY);
+ }
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ if (!cadre.error)
+ av(arg);
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_WRAP;
+ }
+/**
+ * This method is used for drawing with primitive forward, backward in CLOSE MODE
+ * @param oldx X position
+ * @param oldy Y position
+ * @param arg The length to forward
+ * @throws LogoError
+ */
+ private void trace_ferme(double oldx, double oldy, double arg) throws LogoError {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ boolean re = false;
+ double longueur;
+ double diagonale=Math.sqrt(Math.pow(w,2)+Math.pow(h,2))+1;
+ if (arg < 0)
+ re = true;
+ if (re)
+ longueur = trouve_longueur(0, -diagonale, oldx, oldy);
+ else
+ longueur = trouve_longueur(0, diagonale, oldx, oldy);
+ if (Math.abs(longueur) < Math.abs(arg))
+ throw new LogoError(Logo.messages
+ .getString("erreur_sortie1")
+ + "\n"
+ + Logo.messages.getString("erreur_sortie2")
+ + Math.abs((int) (longueur)));
+ else {
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ av(arg);
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLOSE;
+ }
+ }
+ /**
+ * This method extract coords from a list <br>
+ * X is stored in coords(0], Y stored in coords[1], Z Stored in coords[2]
+ * @param liste The list
+ * @param prim The calling primitive
+ * @throws LogoError If List isn't a list coordinate
+ */
+
+ private void extractCoords(String liste,String prim)throws LogoError{
+ StringTokenizer st = new StringTokenizer(liste);
+ try {
+ for(int i=0;i<coords.length;i++){
+ coords[i]=1;
+ if (!st.hasMoreTokens())
+ throw new LogoError(prim
+ + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ String element = st.nextToken();
+ if (element.equals("-")) {
+ if (st.hasMoreTokens())
+ element = st.nextToken();
+ coords[i] = -1;
+ }
+ coords[i] = coords[i] * Double.parseDouble(element);
+ }
+
+ } catch (NumberFormatException e) {
+ throw new LogoError(prim
+ + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ }
+ if (st.hasMoreTokens())
+ throw new LogoError(prim
+ + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ }
+ /**
+ * This method sets the drawing area to perspective mode
+ */
+
+ protected void perspective(){
+ UserConfig uc = WSManager.getUserConfig();
+ if (!enabled3D()) {
+ uc.setDrawXAxis(false);
+ uc.setDrawYAxis(false);
+ uc.setDrawGrid(false);
+ change_image_tortue(cadre,"tortue0.png");
+ montrecacheTortue(false);
+ DrawPanel.WINDOW_MODE=DrawPanel.WINDOW_3D;
+ w3d=new World3D();
+ montrecacheTortue(true);
+ }
+ }
+ /**
+ * This method sets the drawing area to Wrap, Close or Window mode
+ * @param id The window Mode
+ */
+ protected void setWindowMode(int id){
+ if (DrawPanel.WINDOW_MODE!=id) {
+ montrecacheTortue(false);
+ DrawPanel.WINDOW_MODE=id;
+ w3d=null;
+ montrecacheTortue(true);
+ }
+ }
+
+
+ /**
+ * This method converts the coordinates contained in "coords" towards the coords on the drawing area
+ */
+ double[] toScreenCoord(double[] coord,boolean drawPoly){
+ // If Mode perspective is active
+ if (enabled3D()){
+ // w3d.toScreenCoord(coord);
+ // camera world
+ // If we have to record the polygon coordinates
+ if (DrawPanel.record3D!=DrawPanel.record3D_NONE&&DrawPanel.record3D!=DrawPanel.record3D_TEXT&&drawPoly){
+
+ DrawPanel.poly.addVertex(new Point3d(coord[0]/1000,coord[1]/1000,coord[2]/1000),tortue.couleurcrayon);
+ }
+
+ w3d.toCameraWorld(coord);
+
+ // Convert to screen Coordinates
+ w3d.cameraToScreen(coord);
+ }
+ // Mode2D
+ else {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ coord[0]=w/2+coord[0];
+ coord[1]=h/2-coord[1];
+ }
+ return coord;
+ }
+
+
+
+ /**
+ * This method creates an instance of coord with the valid size:<br>
+ * size 2 for 2D coordinates<br>
+ * size 3 for 3D coordinates
+ */
+
+ private void initCoords(){
+
+ if (null==coords) coords=new double[2];
+ if (enabled3D()){
+ if (coords.length!=3) coords=new double[3];
+ }
+ else {
+ if (coords.length!=2) coords=new double[2];
+ }
+ }
+ public boolean enabled3D(){
+ return (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_3D);
+ }
+
+ /**
+ * For hideturtle and showturtle
+ */
+ protected void ct_mt() {
+ if (null == tortue.tort) {
+ g.setXORMode(couleurfond);
+ g.setColor(tortue.couleurcrayon);
+ tortue.drawTriangle();
+ BasicStroke crayon_actuel = (BasicStroke) g.getStroke();
+ if (crayon_actuel.getLineWidth() == 1)
+ g.draw(tortue.triangle);
+ else {
+ g.setStroke(crayon_triangle);
+ g.draw(tortue.triangle);
+ g.setStroke(crayon_actuel);
+ }
+ } else {
+ g.setXORMode(couleurfond);
+ double angle = Math.PI / 2 - tortue.angle;
+ float x = (float) (tortue.corX * Math.cos(angle) + tortue.corY
+ * Math.sin(angle));
+ float y = (float) (-tortue.corX * Math.sin(angle) + tortue.corY
+ * Math.cos(angle));
+ g.rotate(angle);
+ g.drawImage(tortue.tort, (int) x - tortue.largeur / 2,
+ (int) y - tortue.hauteur / 2, this);
+ g.rotate(-angle);
+ }
+/* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortue.corX - tortue.gabarit,
+ tortue.corY - tortue.gabarit,
+ tortue.gabarit * 2,
+ tortue.gabarit * 2);
+ */
+ clip();
+
+/* clip((int) (tortue.corX - tortue.gabarit),
+ (int) (tortue.corY - tortue.gabarit),
+ tortue.gabarit * 2, tortue.gabarit * 2);*/
+ }
+ /**
+ * When the turtle has to be redrawn, this method erase the turtle on the drawing screen
+ *
+ */
+ protected void montrecacheTortue(boolean b) {
+ g.setColor(couleurfond);
+ for (int i = 0; i < tortues_visibles.size(); i++) {
+ int id = Integer.parseInt(tortues_visibles.get(i));
+ // Turtle triangle
+ if (null == tortues[id].tort) {
+ g.setXORMode(couleurfond);
+ g.setColor(tortues[id].couleurmodedessin);
+ tortues[id].drawTriangle();
+ BasicStroke crayon_actuel = (BasicStroke) g.getStroke();
+ if (crayon_actuel.getLineWidth() == 1)
+ g.draw(tortues[id].triangle);
+ else {
+ g.setStroke(crayon_triangle);
+ g.draw(tortues[id].triangle);
+ g.setStroke(crayon_actuel);
+ }
+ } else {
+ // Image turtle
+ g.setXORMode(couleurfond);
+ double angle = Math.PI / 2 - tortues[id].angle;
+ float x = (float) (tortues[id].corX * Math.cos(angle) + tortues[id].corY
+ * Math.sin(angle));
+ float y = (float) (-tortues[id].corX * Math.sin(angle) + tortues[id].corY
+ * Math.cos(angle));
+ g.rotate(angle);
+ g.drawImage(tortues[id].tort, (int) x
+ - tortues[id].largeur / 2, (int) y
+ - tortues[id].hauteur / 2, this);
+ g.rotate(-angle);
+ }
+ /*if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortues[id].corX - tortues[id].gabarit,
+ tortues[id].corY - tortues[id].gabarit,
+ tortues[id].gabarit * 2,
+ tortues[id].gabarit * 2);
+ shape=rec;*/
+ if (b) clip();
+ }
+ }
+
+
+
+/* private void montrecacheTortue() {
+ // Graphics2D g=(Graphics2D)dessin.getGraphics();
+ g.setColor(couleurfond);
+ for (int i = 0; i < tortues_visibles.size(); i++) {
+ int id = Integer.parseInt(String.valueOf(tortues_visibles
+ .get(i)));
+
+ if (null == tortues[id].tort) {
+ g.setXORMode(couleurfond);
+ g.setColor(tortues[id].couleurmodedessin);
+ tortues[id].coord();
+ BasicStroke crayon_actuel = (BasicStroke) g.getStroke();
+ if (crayon_actuel.getLineWidth() == 1)
+ g.draw(tortues[id].triangle);
+ else {
+ g.setStroke(crayon_triangle);
+ g.draw(tortues[id].triangle);
+ g.setStroke(crayon_actuel);
+ }
+ } else {
+ g.setXORMode(couleurfond);
+ double angle = Math.PI / 2 - tortues[id].angle;
+ float x = (float) (tortues[id].corX * Math.cos(angle) + tortues[id].corY
+ * Math.sin(angle));
+ float y = (float) (-tortues[id].corX * Math.sin(angle) + tortues[id].corY
+ * Math.cos(angle));
+ g.rotate(angle);
+ g.drawImage(tortues[id].tort, (int) x
+ - tortues[id].largeur / 2, (int) y
+ - tortues[id].hauteur / 2, cadre.getArdoise());
+ g.rotate(-angle);
+ }
+ /* if (null==rec) rec=new Rectangle2D.Double();
+ rec.setRect(tortues[id].corX - tortues[id].gabarit,
+ tortues[id].corY - tortues[id].gabarit,
+ tortues[id].gabarit * 2,
+ tortues[id].gabarit * 2);
+
+ clip();
+ // g.dispose();
+ }
+ }
+*/
+ /**
+ * Primitive clearscreen
+ */
+ protected void videecran() {
+ // Delete all Gui Component
+ Set<String> set=gm.keySet();
+ Iterator<String> it=set.iterator();
+ while(it.hasNext()){
+ String element=it.next();
+ gui=gm.get(element).getGuiObject();
+ it.remove();
+ if (SwingUtilities.isEventDispatchThread()){
+ remove(gui);
+ validate();
+ }
+ else {
+ try{
+ SwingUtilities.invokeAndWait(new Runnable(){
+ public void run(){
+ remove(gui);
+ validate();
+ }
+ });
+ }
+ catch(Exception e){}
+ }
+ }
+
+
+ // Delete List Polygon in 3D mode
+// DrawPanel.listPoly=new Vector<Shape3D>();
+// DrawPanel.listText=new Vector<TransformGroup>();
+ // Erase the 3d viewer if visible
+ if (null!=cadre.getViewer3D()) {
+ cadre.getViewer3D().clearScreen();
+ System.gc();
+ }
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ g.setPaintMode();
+ couleurfond=uc.getScreencolor();
+ g.setColor(uc.getScreencolor());
+ g.fillRect(0, 0, w,h);
+ stopRecord2DPolygon();
+
+ // Draw Grid
+ g.setStroke(new BasicStroke(1));
+ drawGrid();
+ drawXAxis();
+ drawYAxis();
+ // Init Turtles
+ if (null == tortues[0])
+ tortues[0] = new Turtle(cadre);
+ // The active turtle will be the turtle 0
+ tortue = tortues[0];
+ tortue.id = 0;
+ // We delete all other turtles
+ for (int i = 1; i < tortues.length; i++) {
+ tortues[i] = null;
+ }
+ tortues_visibles.removeAllElements();
+ tortues_visibles.push("0");
+ g.setColor(tortue.couleurcrayon);
+ clip();
+ tortue.init();
+ tortue.setVisible(true);
+ g.setStroke(new BasicStroke(1));
+ montrecacheTortue(true);
+ // Update the selection frame
+ updateColorSelection();
+
+ }
+ /**
+ * Primitive wash
+ */
+ protected void nettoie() {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ stopRecord2DPolygon();
+ g.setPaintMode();
+ g.setColor(couleurfond);
+ g.fillRect(0, 0, w,h);
+
+ drawGrid();
+ /* Réinitialiser les tortues
+ if (null == tortues[0])
+ tortues[0] = new Tortue(cadre);
+ tortue = tortues[0]; //la tortue active sera à présent la
+ // numéro 0
+ tortue.id = 0;
+ for (int i = 1; i < tortues.length; i++) { //On élimine les
+ // autres tortues
+ tortues[i] = null;
+ }
+ tortues_visibles.removeAllElements();
+ tortues_visibles.push("0");*/
+ g.setColor(tortue.couleurcrayon);
+ clip();
+
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+ else
+ tortues_visibles=new Stack<String>();
+ }
+ /**
+ * Used for primitive fillzone
+ * @param x
+ * @param y
+ * @param increment
+ * @param couleur_frontiere
+ * @return
+ */
+
+ private int bornes_remplis_zone(int x, int y, int increment, int couleur_frontiere) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+// System.out.println(x+" "+y);
+ while (!meme_couleur(DrawPanel.dessin.getRGB(x, y) ,couleur_frontiere)) {
+ DrawPanel.dessin.setRGB(x, y, couleur_frontiere);
+ x = x + increment;
+ if (!(x > 0 && x < w-1))
+ break;
+ }
+ return x - increment;
+ }
+ /**
+ * Are the two color equals?
+ * @param col1 The first color
+ * @param col2 The second color
+ * @return true or false
+ */
+ private boolean meme_couleur(int col1,int col2){
+/* if (Config.quality==Logo.QUALITY_HIGH){
+ int rouge1 = (col1 >> 16) & 0xFF;
+ int vert1 = (col1 >> 8) & 0xFF;
+ int bleu1 = col1 & 0xFF;
+ int rouge2 = (col2 >> 16) & 0xFF;
+ int vert2 = (col2 >> 8) & 0xFF;
+ int bleu2 = col2 & 0xFF;
+ int tolerance=120;
+ int diff_rouge=rouge1-rouge2;
+ int diff_bleu=bleu1-bleu2;
+ int diff_vert=vert1-vert2;
+ boolean rouge;boolean vert; boolean bleu;
+ if (rouge1>rouge2){
+ if (rouge1-rouge2< 128 -rouge2/2) rouge=true;
+ else rouge=false;
+ }
+ else{
+ if (rouge2-rouge1<rouge2/2) rouge=true;
+ else rouge=false;
+ }
+ if (vert1>vert2){
+ if (vert1-vert2< 128 -vert2/2) vert=true;
+ else vert=false;
+ }
+ else{
+ if (vert2-vert1<vert2/2) vert=true;
+ else vert=false;
+ }
+ if (bleu1>bleu2){
+ if (bleu1-bleu2< 128 -bleu2/2) bleu=true;
+ else bleu=false;
+ }
+ else{
+ if (bleu2-bleu1<bleu2/2) bleu=true;
+ else bleu=false;
+ }
+
+ return rouge&&bleu&&vert;
+// if (Math.abs(rouge1-rouge2)<tolerance&&Math.abs(vert1-vert2)<tolerance&&Math.abs(bleu1-bleu2)<tolerance&&Math.abs(rouge1+bleu1+vert1-rouge2-bleu2-vert2)<450)
+// return true;
+ // else return false;
+ }
+ else{*/
+ return (col1==col2);
+ //}
+ }
+ /**
+ * Primitive fillzone
+ */
+ protected void rempliszone() {
+ montrecacheTortue(false);
+ int x = (int) (tortue.corX + 0.5);
+ int y = (int) (tortue.corY + 0.5);
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ if (x > 0 & x < w & y > 0 & y < h) {
+ int couleur_origine = DrawPanel.dessin.getRGB(x, y);
+ int couleur_frontiere = tortue.couleurcrayon.getRGB();
+ // System.out.println(couleur_origine+" " +couleur_frontiere);
+ Stack<Point> pile_germes = new Stack<Point>();
+ boolean couleurs_differentes = !meme_couleur(couleur_origine,couleur_frontiere);
+ if (couleurs_differentes)
+ pile_germes.push(new Point(x, y));
+ while (!pile_germes.isEmpty()) {
+
+ Point p = pile_germes.pop();
+ int xgerme = p.x;
+ int ygerme = p.y;
+ int xmax = bornes_remplis_zone(xgerme, ygerme, 1,
+ couleur_frontiere);
+ int xmin=0;
+ if (xgerme>0) xmin = bornes_remplis_zone(xgerme - 1, ygerme, -1,
+ couleur_frontiere);
+ boolean ligne_dessus = false;
+ boolean ligne_dessous = false;
+ for (int i = xmin; i < xmax + 1; i++) {
+ //on recherche les germes au dessus et au dessous
+ if (ygerme > 0
+ && meme_couleur(DrawPanel.dessin.getRGB(i, ygerme - 1) ,couleur_frontiere)) {
+ if (ligne_dessus)
+ pile_germes.push(new Point(i - 1, ygerme - 1));
+ ligne_dessus = false;
+ } else {
+ ligne_dessus = true;
+ if (i == xmax && ygerme > 0)
+ pile_germes.push(new Point(xmax, ygerme - 1));
+ }
+ if (ygerme < h-1
+ && meme_couleur(DrawPanel.dessin.getRGB(i, ygerme + 1),couleur_frontiere)) {
+ if (ligne_dessous)
+ pile_germes.push(new Point(i - 1, ygerme + 1));
+ ligne_dessous = false;
+ } else {
+ ligne_dessous = true;
+ if (i == xmax && ygerme < h-1)
+ pile_germes.push(new Point(xmax, ygerme + 1));
+ }
+ }
+ }
+ clip();
+ montrecacheTortue(true);
+ }
+ }
+ /**
+ * Used for primitive "fill"
+ * @param x
+ * @param y
+ * @param increment
+ * @param couleur_crayon
+ * @param couleur_origine
+ * @return
+ */
+ private int bornes_remplis(int x, int y, int increment, int couleur_crayon,
+ int couleur_origine) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ while (DrawPanel.dessin.getRGB(x, y) == couleur_origine) {
+ DrawPanel.dessin.setRGB(x, y, couleur_crayon);
+ x = x + increment;
+ if (!(x > 0 && x < w-1))
+ break;
+ }
+ return x - increment;
+ }
+ /**
+ * Primitive "fill"
+ */
+ protected void remplis() {
+ montrecacheTortue(false);
+ int x = (int) (tortue.corX + 0.5);
+ int y = (int) (tortue.corY + 0.5);
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (x > 0 & x < w & y > 0 & y < h) {
+ int couleur_origine = DrawPanel.dessin.getRGB(x, y);
+ int couleur_crayon = tortue.couleurcrayon.getRGB();
+ if (x > 0 & x < w & y > 0 & y < h) {
+ Stack<Point> pile_germes = new Stack<Point>();
+ boolean couleurs_differentes = !(couleur_origine == couleur_crayon);
+ if (couleurs_differentes)
+ pile_germes.push(new Point(x, y));
+ while (!pile_germes.isEmpty()) {
+ Point p = pile_germes.pop();
+ int xgerme = p.x;
+ int ygerme = p.y;
+ // System.out.println(xgerme+" "+ygerme);
+ int xmax = bornes_remplis(xgerme, ygerme, 1, couleur_crayon,
+ couleur_origine);
+ int xmin=0;
+ if (xgerme>0) xmin = bornes_remplis(xgerme - 1, ygerme, -1,
+ couleur_crayon, couleur_origine);
+ // System.out.println("xmax "+xmax+"xmin "+xmin);
+ boolean ligne_dessus = false;
+ boolean ligne_dessous = false;
+ for (int i = xmin; i < xmax + 1; i++) {
+ //on recherche les germes au dessus et au dessous
+ if (ygerme > 0
+ && DrawPanel.dessin.getRGB(i, ygerme - 1) != couleur_origine) {
+ if (ligne_dessus)
+ pile_germes.push(new Point(i - 1, ygerme - 1));
+ ligne_dessus = false;
+ } else {
+ ligne_dessus = true;
+ if (i == xmax && ygerme > 0)
+ pile_germes.push(new Point(xmax, ygerme - 1));
+ }
+ if (ygerme < h-1
+ && DrawPanel.dessin.getRGB(i, ygerme + 1) != couleur_origine) {
+ if (ligne_dessous)
+ pile_germes.push(new Point(i - 1, ygerme + 1));
+ ligne_dessous = false;
+ } else {
+ ligne_dessous = true;
+ if (i == xmax && ygerme < h-1)
+ pile_germes.push(new Point(xmax, ygerme + 1));
+ }
+ }
+ }
+ clip();
+ montrecacheTortue(true);
+ }
+ }
+ }
+ /**
+ * Primitive "label"
+ * @param mot The word to write on the drawing area
+ */
+ protected void etiquette(String mot) {
+ // Graphics2D g = (Graphics2D) Ardoise.dessin.getGraphics();
+ montrecacheTortue(false);
+ if (!enabled3D()){
+ double angle = Math.PI / 2 - tortue.angle;
+ if(DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>();
+ etiquette2D(tortue.corX,tortue.corY,angle,mot);
+/* g.rotate(angle);
+ g.setPaintMode();
+ g.setColor(tortue.couleurcrayon);
+ float x = (float) (tortue.corX * Math.cos(angle) + tortue.corY
+ * Math.sin(angle));
+ float y = (float) (-tortue.corX * Math.sin(angle) + tortue.corY
+ * Math.cos(angle));
+ g.setFont(Panel_Font.fontes[police_etiquette]
+ .deriveFont((float) tortue.police));
+ g.drawString(mot, x, y);
+ g.rotate(-angle);*/
+ }
+ else{
+ FontRenderContext frc=g.getFontRenderContext();
+ GlyphVector gv=g.getFont().createGlyphVector(frc, mot);
+ Shape outline=gv.getOutline(0, 0);
+ Shape s=transformShape(outline);
+ g.setPaintMode();
+ g.setColor(tortue.couleurcrayon);
+ g.fill(s);
+ if (record3D==DrawPanel.record3D_TEXT){
+ Text2D text=new Text2D(
+ mot,new Color3f(tortue.couleurcrayon), UserConfig.fontes[police_etiquette].getName(),
+ tortue.police,Font.PLAIN);
+
+ text.setRectangleScaleFactor(0.001f);
+ Appearance appear=text.getAppearance();
+ PolygonAttributes pa=new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+ text.setAppearance(appear);
+// if (null==DrawPanel.listText) DrawPanel.listText=new Vector<TransformGroup>();
+ TransformGroup tg=new TransformGroup();
+ double[][] d=tortue.getRotationMatrix();
+ Matrix3d m=new Matrix3d(d[0][0],d[0][1],d[0][2],d[1][0],d[1][1],d[1][2],d[2][0],d[2][1],d[2][2]);
+ Transform3D t=new Transform3D(m,new Vector3d(tortue.X/1000,tortue.Y/1000,tortue.Z/1000),1);
+ tg.setTransform(t);
+ tg.addChild(text);
+ cadre.getViewer3D().add2DText(tg);
+// DrawPanel.listText.add(tg);
+ }
+
+
+ }
+ montrecacheTortue(true);
+ if (classicMode) repaint();
+ }
+ private void etiquette2D(double x,double y, double angle, String word){
+ if (word.length()==0) return;
+
+ g.setPaintMode();
+ g.setColor(tortue.couleurcrayon);
+ Font f= UserConfig.fontes[police_etiquette]
+ .deriveFont((float) tortue.police);
+ g.setFont(f);
+ g.translate(x, y);
+ g.rotate(angle);
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout layout = new TextLayout(word, f, frc);
+ Rectangle2D bounds = layout.getBounds();
+ float height=(float)bounds.getHeight();
+ float width=(float)bounds.getWidth();
+ float x1=0,y1=0;
+ switch(tortue.getLabelHorizontalAlignment()){
+ case Turtle.LABEL_HORIZONTAL_ALIGNMENT_LEFT:
+ x1=0;
+ break;
+ case Turtle.LABEL_HORIZONTAL_ALIGNMENT_CENTER:
+ x1=-width/2;
+ break;
+ case Turtle.LABEL_HORIZONTAL_ALIGNMENT_RIGHT:
+ x1=-width;
+ break;
+ }
+ switch(tortue.getLabelVerticalAlignment()){
+ case Turtle.LABEL_VERTICAL_ALIGNMENT_BOTTOM:
+ y1=0;
+ break;
+ case Turtle.LABEL_VERTICAL_ALIGNMENT_CENTER:
+ y1=height/2;
+ break;
+ case Turtle.LABEL_VERTICAL_ALIGNMENT_TOP:
+ y1=height;
+ break;
+ }
+ layout.draw(g, x1, y1);
+ g.drawString(word, x1, y1);
+ g.rotate(-angle);
+ g.translate(-x, -y);
+ if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){
+ Rectangle2D.Double rec=new Rectangle2D.Double(0,0,width,height);
+ AffineTransform at=new AffineTransform();
+ at.translate(x, y);
+ at.rotate(angle);
+ Rectangle2D bound =at.createTransformedShape(rec).getBounds2D();
+ double right= bound.getX()+bound.getWidth()-x;
+ double left= x-bound.getX();
+ double up=y-bound.getY();
+ double down=bound.getY()+bound.getHeight()-y;
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (x+right>w&& x<=w){
+ pt=new Point2D.Double(-w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(-w+x,y,angle,word);
+ }
+ }
+ if (x-left<0&& x>=0){
+ pt=new Point2D.Double(w+x,y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(w+x,y,angle,word);
+ }
+ }
+ if (y-up<0&& y>=0){
+ pt=new Point2D.Double(x,h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(x,h+y,angle,word);
+ }
+ }
+ if (y+down>h&&y<=h){
+ pt=new Point2D.Double(x,-h+y);
+ if (! centers.contains(pt)) {
+ centers.add(pt);
+ etiquette2D(x,-h+y,angle,word);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method transform a plane 2D shape in the shape corresponding to the turtle plane
+ * @param s the first shape
+ * @return the new shape after transformation
+ */
+ private Shape transformShape(Shape s){
+ PathIterator it=s.getPathIterator(null);
+ double[] d=new double[6];
+ double[][] coor=new double[3][1];
+ GeneralPath gp=new GeneralPath();
+ double[] end=new double[3];
+ double[] ctl1=new double[3];
+ double[] ctl2=new double[3];
+ boolean b=false;
+ while(!it.isDone()){
+ it.next();
+ int id=0;
+ if (!it.isDone()) id=it.currentSegment(d);
+ else break;
+ coor[0][0]=d[0];
+ coor[1][0]=-d[1];
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+
+ end[0]=coor[0][0]+tortue.X;
+ end[1]=coor[1][0]+tortue.Y;
+ end[2]=coor[2][0]+tortue.Z;
+ w3d.toScreenCoord(end);
+
+ if (id==PathIterator.SEG_MOVETO)
+ gp.moveTo((float)end[0], (float)end[1]);
+ else if (id==PathIterator.SEG_LINETO)
+ {
+ if (!b) {
+ b=true;
+ gp.moveTo((float)end[0], (float)end[1]);
+ }
+ else gp.lineTo((float)end[0], (float)end[1]);
+ }
+ else if (id==PathIterator.SEG_CLOSE){
+ gp.closePath();
+ }
+ else {
+ if (!b) {
+ b=true;
+ Point2D p=null;
+ if (s instanceof Arc2D.Double)
+ p=((Arc2D.Double)s).getStartPoint();
+ else if (s instanceof GeneralPath)
+ p=((GeneralPath)s).getCurrentPoint();
+ coor[0][0]=p.getX();
+ coor[1][0]=-p.getY();
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+ ctl1[0]=coor[0][0]+tortue.X;
+ ctl1[1]=coor[1][0]+tortue.Y;
+ ctl1[2]=coor[2][0]+tortue.Z;
+ w3d.toScreenCoord(ctl1);
+ gp.moveTo((float)ctl1[0], (float)ctl1[1]);
+ }
+ coor[0][0]=d[2];
+ coor[1][0]=-d[3];
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+ ctl1[0]=coor[0][0]+tortue.X;
+ ctl1[1]=coor[1][0]+tortue.Y;
+ ctl1[2]=coor[2][0]+tortue.Z;
+ w3d.toScreenCoord(ctl1);
+ if(id==PathIterator.SEG_QUADTO){
+ QuadCurve2D qc=new QuadCurve2D.Double(gp.getCurrentPoint().getX(),gp.getCurrentPoint().getY()
+ ,end[0], end[1],ctl1[0], ctl1[1]);
+ gp.append(qc, true);}
+ else if (id==PathIterator.SEG_CUBICTO){
+ coor[0][0]=d[4];
+ coor[1][0]=-d[5];
+ coor[2][0]=0;
+ coor=w3d.multiply(tortue.getRotationMatrix(), coor);
+
+ ctl2[0]=coor[0][0]+tortue.X;
+ ctl2[1]=coor[1][0]+tortue.Y;
+ ctl2[2]=coor[2][0]+tortue.Z;
+
+ w3d.toScreenCoord(ctl2);
+ CubicCurve2D qc=new CubicCurve2D.Double(gp.getCurrentPoint().getX(),gp.getCurrentPoint().getY()
+ ,end[0], end[1],ctl1[0], ctl1[1],ctl2[0], ctl2[1]);
+ gp.append(qc, true);
+ }
+ }
+ }
+ return gp;
+ }
+ public World3D getWorld3D(){
+ return w3d;
+ }
+ /**
+ * primitive setscreencolor
+ * @param color The Color of the nackground screen
+ */
+ protected void fcfg(Color color) {
+ couleurfond=color;
+ updateColorSelection();
+ if (enabled3D()){
+ if (cadre.getViewer3D()!=null){
+ cadre.getViewer3D().updateBackGround(couleurfond);
+ }
+ }
+ nettoie();
+ }
+ /**
+ * Primitive setpencolor
+ * @param color The pen Color
+ */
+ protected void fcc(Color color) {
+ if (tortue.isVisible()&&null==tortue.tort) montrecacheTortue(false);
+ tortue.couleurcrayon = color;
+ tortue.couleurmodedessin = color;
+ if (tortue.isVisible()&&null==tortue.tort) montrecacheTortue(true);
+ }
+
+ /**
+ * Primitive "guiposition"
+ * @param id The id for the gui Object
+ * @param liste The Coordinates list
+ * @param name The translated name for the primitive "guiposition"
+ * @throws LogoError If coordinates list is invalid
+ */
+ protected void guiposition(String id, String liste,String name) throws LogoError{
+ if (guiExist(id)){
+ initCoords();
+ extractCoords(liste,name);
+ coords=toScreenCoord(coords,false);
+ gm.get(id).setLocation((int)coords[0],(int)coords[1]);
+ }
+ }
+ /**
+ * Draw the Gui object refered with "id"
+ * @param id The Gui Object Id
+ * @throws LogoError If this object doesn't exist
+ */
+ protected void guiDraw(String id) throws LogoError{
+ if (guiExist(id)){
+ GuiComponent gc=gm.get(id);
+ add(gc.getGuiObject());
+ validate();
+ repaint();
+ // updateGuiComponents();
+ }
+ }
+ /**
+ * @uml.property name="gui"
+ * @uml.associationEnd
+ */
+ private javax.swing.JComponent gui;
+ /**
+ * This method erases a Gui on the drawing area
+ * @param id The Gui Object id
+ * @throws LogoError
+ */
+
+ protected void guiRemove(String id) throws LogoError{
+ if (guiExist(id)){
+ gui=gm.get(id).getGuiObject();
+ gm.remove(id);
+ if (SwingUtilities.isEventDispatchThread()){
+ remove(gui);
+ validate();
+ }
+ else {
+ try{
+ SwingUtilities.invokeAndWait(new Runnable(){
+ public void run(){
+ remove(gui);
+ validate();
+ }
+ });
+ }
+ catch(Exception e){}
+ }
+ repaint();
+ }
+ }
+ private StringBuffer extractList(String list) throws LogoError{
+ StringBuffer sb=new StringBuffer();
+ int compteur=0;
+ int i=0;
+ while(list.length()!=0){
+ char c=list.charAt(i);
+ if (c=='[') compteur++;
+ else if (c==']') {
+ if (compteur==0) return sb;
+ else compteur--;
+ }
+ sb.append(c);
+ i++;
+ }
+ throw new LogoError("[ "+list+" "+Logo.messages.getString("pas_liste"));
+ }
+
+ protected void guiAction(String id, String liste) throws LogoError{
+ if (guiExist(id)){
+ GuiComponent gc=gm.get(id);
+ // If gc is a JButton
+ if (gc.isButton()){
+ ((GuiButton)gc).setAction(Utils.decoupe(liste));
+ if (!gc.hasAction()){
+ ((javax.swing.JButton)gc.getGuiObject()).addActionListener(gc);
+ gc.hasAction=true;
+ }
+ }
+ // gc is a JcomboBox
+ else if (gc.isMenu()){
+ liste=liste.trim();
+ int i=0;
+ while(liste.length()!=0){
+ if (liste.charAt(0)=='['){
+ liste=liste.substring(1).trim();
+ StringBuffer sb=extractList(liste);
+ liste=liste.substring(sb.length()+1).trim();
+ ((GuiMenu)gc).setAction(sb, i);
+ i++;
+ }
+ else throw new LogoError(liste.charAt(0)+" "+Logo.messages.getString("pas_liste"));
+ }
+ GuiMenu gm=(GuiMenu)gc;
+ if (!gm.hasAction){
+ gm.hasAction=true;
+ ((javax.swing.JComboBox)gc.getGuiObject()).addActionListener(gm);
+ }
+ }
+ }
+ }
+ private boolean guiExist(String id) throws LogoError{
+ if (gm.containsKey(id.toLowerCase())) return true;
+ else throw new LogoError(Logo.messages.getString("no_gui")+" "+id);
+ }
+// boolean access=false;
+ private void clip(){
+ if (classicMode){
+
+ //access=true;
+// refresh();
+
+ repaint();
+ /* if (SwingUtilities.isEventDispatchThread()){
+ repaint();
+ }
+ else {
+ try {
+
+ SwingUtilities.invokeLater(new Runnable(){
+ public void run(){
+ repaint();
+ }
+ });
+ }
+ catch(Exception e2){}
+ }*/
+ }
+ /*Rectangle rec1=cadre.jScrollPane1.getViewport().getViewRect();
+ boolean inter=sh.intersects(rec1);
+ if (inter){
+ if (classicMode){
+ repaint();
+ }
+ }*/
+ }
+ public void setQuality(DrawQuality q){
+ /*
+ * Marko Zivkovic
+ * I improved all the qualities for one level. I introduced a super high quality that is assigned to "high"
+ * and dropped the low quality, which is now the old normal/default quality
+ */
+
+ if (q==DrawQuality.HIGH){
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_PURE);
+ }
+ else if(q==DrawQuality.LOW){
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_DEFAULT);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_DEFAULT);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
+ }
+ else { //normal
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ }
+ }
+ public void change_image_tortue(Application cadre, String chemin) {
+ if (tortue.isVisible())
+ montrecacheTortue(false);
+ if (chemin.equals("tortue0.png")) {
+ tortue.tort = null;
+ tortue.largeur = 26;
+ tortue.hauteur = 26;
+ } else {
+ //ON teste tout d'abord si le chemin est valide
+ if (null == Utils.class.getResource(chemin))
+ chemin = "tortue1.png";
+ tortue.tort = Toolkit.getDefaultToolkit().getImage(
+ Utils.class.getResource(chemin));
+ MediaTracker tracker = new MediaTracker(cadre.getFrame());
+ tracker.addImage(tortue.tort, 0);
+ try {
+ tracker.waitForID(0);
+ } catch (InterruptedException e1) {
+ }
+ double largeur_ecran = Toolkit.getDefaultToolkit().getScreenSize()
+ .getWidth();
+ // On fait attention à la résolution de l'utilisateur
+ double facteur = largeur_ecran / 1024.0;
+
+ if ((int) (facteur + 0.001) != 1) {
+ tortue.largeur = tortue.tort.getWidth(cadre.getFrame());
+ tortue.hauteur = tortue.tort.getHeight(cadre.getFrame());
+ tortue.tort = tortue.tort.getScaledInstance(
+ (int) (facteur * tortue.largeur),
+ (int) (facteur * tortue.hauteur),
+ Image.SCALE_SMOOTH);
+ tracker = new MediaTracker(cadre.getFrame());
+ tracker.addImage(tortue.tort, 0);
+ try {
+ tracker.waitForID(0);
+ } catch (InterruptedException e1) {
+ }
+ }
+ tortue.largeur = tortue.tort.getWidth(cadre.getFrame());
+ tortue.hauteur = tortue.tort.getHeight(cadre.getFrame());
+ }
+ tortue.gabarit = Math.max(tortue.hauteur,
+ tortue.largeur);
+ if (tortue.isVisible())
+ montrecacheTortue(true);
+
+ }
+ // animation
+ protected void setAnimation(boolean predic){
+ if (predic==classicMode){
+ if (predic) {
+ cadre.getHistoryPanel().active_animation();
+ }
+ else {
+ cadre.getHistoryPanel().stop_animation();
+ repaint();
+ }
+ }
+ }
+
+ protected void setGraphicsFont(Font f){
+ g.setFont(f);
+ }
+ protected Font getGraphicsFont(){
+ return g.getFont();
+ }
+ protected void setStroke(Stroke st){
+ g.setStroke(st);
+ }
+ public Color getBackgroundColor(){
+ return couleurfond;
+ }
+ protected void setBackgroundColor(Color c){
+ couleurfond=c;
+ }
+ protected void updateColorSelection(){
+ float r=(255-couleurfond.getRed())/255;
+ float v=(255-couleurfond.getGreen())/255;
+ float b=(255-couleurfond.getBlue())/255;
+ colorSelection=new Color(r,v,b,0.2f);
+ }
+ public void setNumberOfTurtles(int max){
+ WSManager.getUserConfig().setMaxTurtles(max);
+ Turtle[] tampon = tortues.clone();
+ tortues = new Turtle[max];
+ int borne_sup=Math.min(tampon.length,tortues.length);
+ for(int i=0;i<borne_sup;i++){
+ tortues[i]=tampon[i];
+ }
+ for(int i=tortues_visibles.size()-1;i>-1;i--){
+ int integer=Integer.parseInt(tortues_visibles.get(i));
+ if (integer>=max){
+ tortues_visibles.remove(i);
+ }
+ }
+ }
+ protected void initGraphics(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ police_etiquette=UserConfig.police_id(WSManager.getWorkspaceConfig().getFont());
+ // init all turtles
+ tortues = new Turtle[uc.getMaxTurtles()];
+ tortues_visibles=new Stack<String>();
+ tortue=new Turtle(cadre);
+ tortues[0] = tortue;
+ tortue.id = 0;
+ tortues_visibles.push("0");
+ for (int i = 1; i < tortues.length; i++) {
+ // All other turtles are null
+ tortues[i] = null;
+ }
+ g=(Graphics2D)dessin.getGraphics();
+ couleurfond=uc.getScreencolor();
+ setQuality(uc.getQuality());
+ g.setColor(uc.getScreencolor());
+ g.fillRect(0,0,w,h);
+ g.setColor(uc.getScreencolor());
+ if (!enabled3D()){
+ drawGrid();
+ drawXAxis();
+ drawYAxis();
+ }
+ MediaTracker tracker;
+ if (0==uc.getActiveTurtle()) {
+ g.setXORMode(couleurfond);
+ tortue.drawTriangle();
+ g.setColor(tortue.couleurcrayon);
+ g.draw(tortue.triangle);
+ }
+ else {
+ g.setXORMode(couleurfond);
+ tracker=new MediaTracker(cadre.getFrame());
+ tracker.addImage(tortue.tort,0);
+ try{tracker.waitForID(0);}
+ catch(InterruptedException e){}
+ if (tracker.checkID(0)) g.drawImage(tortue.tort, w/2 - tortue.largeur / 2,
+ h/2 - tortue.hauteur/2, this);
+ }
+ updateColorSelection();
+ }
+
+ private void resizeAllGuiComponents(double d){
+ // Resize all GuiComponent
+ Set<String> set=gm.keySet();
+ Iterator<String> it=set.iterator();
+ while (it.hasNext()){
+ String element=it.next();
+ GuiComponent gui=gm.get(element);
+ gui.getGuiObject().setSize((int)(gui.getOriginalWidth()*d),
+ (int)(gui.getOriginalHeight()*d) );
+ Font f=gui.getGuiObject().getFont();
+ gui.getGuiObject().setFont(f.deriveFont((float)(WSManager.getWorkspaceConfig().getFont().getSize()*d)));
+ double x=gui.getLocation().x/zoom;
+ double y=gui.getLocation().y/zoom;
+ gui.setLocation((int)(x*d),(int)(y*d));
+
+ }
+
+ }
+
+
+ /**
+ * Make a zoom on the drawing area
+ * @param d The absolute factor
+ */
+ public void zoom(double d, boolean zoomIn){
+ // Disable zoom buttons
+ //cadre.setZoomEnabled(false); // TODO REMOVE ZOOM COMPLETELY?
+
+ javax.swing.JViewport jv=cadre.scrollArea.getViewport();
+ Point p=jv.getViewPosition();
+ Rectangle r=jv.getVisibleRect();
+
+
+ // If a selection rectangle is displaying on the drawing area
+ // And If zoomout has been pressed
+ // Zooming on the rectangular selection
+ if (null!=selection&&cadre.commande_isEditable()&&zoomIn){
+ int originalWidth=jv.getWidth();
+ double width=selection.getWidth();
+ d=zoom*originalWidth/width;
+ p=selection.getLocation();
+ r.width=selection.width;
+ // adjust height in the same ratio as width
+ r.height=r.height*(int)width/originalWidth;
+ // erase selection
+ selection=null;
+ }
+ // Resize all Gui Components on the drawing area
+ resizeAllGuiComponents(d);
+
+ double oldZoom=zoom;
+ zoom=d;
+
+ /*
+ * -------------------------------------
+ * | |
+ * | ------------------------- |
+ * | | | |
+ * | | | |
+ * | | x-- dx----- | --> CenterView Point of the rectangle
+ * | | | | |
+ * | | dy | |
+ * | ------------------------- |
+ * -------------------------------------
+ * */
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ double dx=Math.min(r.width,w*oldZoom)/2;
+ double dy=Math.min(r.height,h*oldZoom)/2;
+ Point centerView=new Point((int)(p.x+dx),(int)(p.y+dy));
+
+ // Dynamically modify the drawing Area size
+ setPreferredSize(new java.awt.Dimension(
+ (int)(w*zoom)
+ ,(int)(h*zoom)));
+
+ SwingUtilities.invokeLater(new PositionJViewport(jv,
+ new Point((int)(centerView.x/oldZoom*zoom-dx),
+ (int)(centerView.y/oldZoom*zoom-dy))));
+
+ }
+ private Color getTransparencyColor(int color,int trans){
+ Color c=new Color(color);
+ return new Color(c.getRed(),c.getGreen(),c.getBlue(),trans);
+ }
+ /**
+ * Draw the horizontal axis
+ */
+ private void drawXAxis(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (uc.isDrawXAxis()){
+ g.setColor(getTransparencyColor(uc.getAxisColor(),128));
+ g.drawLine(0,h/2,w,h/2);
+ for (int i=w/2%uc.getXAxis();i<w;i=i+uc.getXAxis()){
+ g.drawLine(i, h/2-2, i, h/2+2);
+ g.setFont(new Font("Dialog",Font.PLAIN,10));
+ String tick=String.valueOf(i-w/2);
+ FontMetrics fm=g.getFontMetrics();
+ int back=fm.stringWidth(String.valueOf(tick))/2;
+ // if the both axes are drawn, the zero has to translated
+ // So we don't draw the zero
+ if (i!=w/2||!uc.isDrawYAxis()) g.drawString(tick, i-back, h/2+20);
+ }
+ }
+ }
+ /**
+ * Draw the vertical axis
+ */
+ private void drawYAxis(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (uc.isDrawYAxis()){
+ g.setColor(getTransparencyColor(uc.getAxisColor(),128));
+ g.drawLine(w/2,0,w/2,h);
+ for (int i=h/2%uc.getYAxis();i<h;i=i+uc.getYAxis()){
+ g.drawLine( w/2-2, i, w/2+2,i);
+ g.setFont(new Font("Dialog",Font.PLAIN,10));
+ String tick=String.valueOf(h/2-i);
+ // If both axes are drawn, zero is translated
+ if (i==h/2&&uc.isDrawXAxis()) g.drawString("0", w/2+10, i-5);
+ else g.drawString(tick, w/2+10, i+5);
+ }
+ }
+ }
+ private void drawGrid(){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ if (uc.isDrawGrid()){
+ g.setStroke(new BasicStroke(1));
+ g.setColor(getTransparencyColor(uc.getGridColor(),100));
+ for (int i=w/2%uc.getXGrid();i<w;i=i+uc.getXGrid())
+ g.drawLine(i, 0, i, h);
+
+ for (int i=h/2%uc.getYGrid();i<h;i=i+uc.getYGrid())
+ g.drawLine(0,i, w, i);
+ }
+ }
+ // In animation mode, we have to wait for the drawing to be finished before modifying graphics.
+ // Thread must be synchronized.
+ protected synchronized void refresh(){
+ repaint();
+ try{
+ wait();
+ }
+ catch(InterruptedException e){}
+
+ }
+
+ protected synchronized void paintComponent(Graphics graph){
+ super.paintComponent(graph);
+ Graphics2D g2d=(Graphics2D)graph;
+ if (null==shape){
+ g2d.setClip(cadre.scrollArea.getViewport().getViewRect());
+ }
+ else {
+ g2d.setClip(shape);
+ shape=null;
+ }
+ g2d.scale(DrawPanel.zoom,DrawPanel.zoom);
+ g2d.drawImage(dessin,0,0,this);
+ g2d.scale(1/DrawPanel.zoom,1/DrawPanel.zoom);
+ if (!Affichage.execution_lancee&&null!=selection&&cadre.commande_isEditable()){
+ g2d.setColor(colorSelection);
+ g2d.fillRect(selection.x, selection.y, selection.width, selection.height);
+ }
+ notify();
+ }
+ public void active_souris(){
+ lissouris=false;
+ }
+ public boolean get_lissouris(){
+ return lissouris;
+ }
+ public int get_bouton_souris(){
+ lissouris=false;
+ return bouton_souris;
+ }
+ public String get_possouris(){
+ lissouris=false;
+ return possouris;
+ }
+ public void mousePressed(MouseEvent e){
+ if (!Affichage.execution_lancee) {
+ selection=new Rectangle();
+ origine=new Point(e.getPoint());
+ selection.setSize(0, 0);
+ }
+ }
+ public void mouseReleased(MouseEvent e){}
+ public void mouseClicked(MouseEvent ev){
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ if (!Affichage.execution_lancee){
+ selection=null;
+ origine=null;
+ repaint();
+ }
+ else{
+ lissouris=true;
+ bouton_souris=ev.getButton();
+ Point point=ev.getPoint();
+ possouris="[ "+(point.x-w/2)+" "+(h/2-point.y)+" ] ";
+ }
+ }
+
+ public void mouseExited(MouseEvent e){
+ }
+ public void mouseEntered(MouseEvent e){
+ }
+ // Select an export area
+ public void mouseDragged(MouseEvent e){
+ if (!Affichage.execution_lancee&&null!=selection){
+ // First, we test if we need to move the scrollbars
+ Point pos=e.getPoint();
+ javax.swing.JViewport jv=cadre.scrollArea.getViewport();
+ Point viewPosition=jv.getViewPosition();
+ Rectangle r=jv.getVisibleRect();
+ r.setLocation(viewPosition);
+ // Is the point visible on screen?
+ boolean b=r.contains(pos);
+
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ // Move the scroolPane if necessary
+ if (!b){
+ int x,y;
+ if (pos.x<viewPosition.x) x=Math.max(0,pos.x);
+ else if (pos.x>viewPosition.x+r.width) x=Math.min(pos.x-r.width,(int)(w*zoom-r.width));
+ else x=viewPosition.x;
+ if (pos.y<viewPosition.y) y=Math.max(0,pos.y);
+ else if (pos.y>viewPosition.y+r.height) y=Math.min(pos.y-r.height,(int)(h*zoom-r.height));
+ else y=viewPosition.y;
+ jv.setViewPosition(new Point(x,y));
+ }
+
+ // Then , drawing the selection area
+
+ selection.setFrameFromDiagonal(origine, e.getPoint());
+ repaint();
+ }
+ }
+
+ public void mouseMoved(MouseEvent ev) {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+
+ lissouris = true;
+ bouton_souris = 0;
+ Point point = ev.getPoint();
+ possouris = "[ " + (point.x - w / 2) + " " + (h / 2 - point.y) + " ] ";
+ }
+
+ protected void addToGuiMap(GuiComponent gc) throws xlogo.kernel.LogoError {
+ gm.put(gc);
+ }
+
+ // This method modifies all Shape for any turtle on screen
+ protected void updateAllTurtleShape() {
+ for (int i = 0; i < tortues.length; i++) {
+ if (null != tortues[i]) {
+ tortues[i].fixe_taille_crayon(2 * tortues[i].getPenWidth());
+ }
+ }
+ }
+ /**
+ * Saves the a part of the drawing area as an image
+ * @param name The image name
+ * @param coords The upper left corner and the right bottom corner
+ */
+ protected void saveImage(String name, int[] coords){
+ BufferedImage buffer=getImagePart(coords);
+ String lowerName=name.toLowerCase();
+ String format="png";
+ if (lowerName.endsWith(".jpg")||lowerName.endsWith(".jpeg")) {
+ format="jpg";
+ }
+ else if (!lowerName.endsWith(".png")) {
+ name=name+".png";
+ }
+ name=WSManager.getUserConfig().getDefaultFolder()+File.separator+name;
+ try{
+ File f=new File(name);
+ ImageIO.write(buffer, format, f);
+ }
+ catch(IOException e){}
+
+ }
+ /**
+ * Return a part of the drawing area as an image
+ * @return
+ */
+ private BufferedImage getImagePart(int[] coords){
+ Image pic=DrawPanel.dessin;
+ if (zoom!=1){
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new ReplicateScaleFilter((int)(dessin.getWidth()*zoom),(int)(dessin.getHeight()*zoom))));
+ }
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new CropImageFilter(coords[0],coords[1],coords[2],coords[3])));
+ return toBufferedImage(pic);
+ }
+
+
+ public BufferedImage getSelectionImage(){
+ Image pic=DrawPanel.dessin;
+ if (zoom!=1){
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new ReplicateScaleFilter((int)(dessin.getWidth()*zoom),(int)(dessin.getHeight()*zoom))));
+ }
+ if (null!=selection){
+ int x=(int)(selection.getBounds().getX());
+ int y=(int)(selection.getBounds().getY());
+ int width=(int)(selection.getBounds().getWidth());
+ int height=(int)(selection.getBounds().getHeight());
+ pic=createImage(new FilteredImageSource(pic.getSource(),
+ new CropImageFilter(x,y,width,height)));
+ }
+ return toBufferedImage(pic);
+ }
+// This method returns a buffered image with the contents of an image
+ private BufferedImage toBufferedImage(Image image) {
+ if (image instanceof BufferedImage)
+ return (BufferedImage)image;
+
+ // This code ensures that all the pixels in the image are loaded
+ image = new ImageIcon(image).getImage();
+
+
+ // Create a buffered image with a format that's compatible with the screen
+ BufferedImage bimage = null;
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ try {
+ // Determine the type of transparency of the new buffered image
+ int transparency = Transparency.OPAQUE;
+
+ // Create the buffered image
+ GraphicsDevice gs = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gs.getDefaultConfiguration();
+ bimage = gc.createCompatibleImage(
+ image.getWidth(null), image.getHeight(null), transparency);
+ } catch (HeadlessException e) {
+ // The system does not have a screen
+ }
+
+ if (bimage == null) {
+ // Create a buffered image using the default color model
+ int type = BufferedImage.TYPE_INT_RGB;
+ bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
+ }
+
+ // Copy image to buffered image
+ Graphics g = bimage.createGraphics();
+
+ // Paint the image onto the buffered image
+ g.drawImage(image, 0, 0, null);
+ g.dispose();
+
+ return bimage;
+ }
+ class PositionJViewport implements Runnable{
+ JViewport jv;
+ Point p;
+ PositionJViewport(JViewport jv, Point p){
+ this.jv=jv;
+ this.p=p;
+ }
+ public void run(){
+ revalidate();
+ //cadre.calculateMargin(); // TODO here is a zoom bug TODO maybe return this
+ // I have to add those two lines because of a bug I don't understand
+ // zoom 8 zoom 1 zoom 8
+ // Sometimes after the method revalidate(), the left upper corner position
+ // wasn't correct
+ cadre.scrollArea.invalidate();
+ cadre.scrollArea.validate();
+ // End Bug
+
+ jv.setViewPosition(p);
+ repaint();
+
+ //cadre.setZoomEnabled(true);
+ }
+ }
+ private void tryRecord2DMode(double a, double b){
+ if (DrawPanel.record2D==DrawPanel.record2D_POLYGON){
+ // FillPolygon mode
+ if (stackTriangle.size()==3){
+ stackTriangle.remove(0);
+ stackTriangle.add(new Point2D.Double(a,b));
+ }
+ else{
+ stackTriangle.add(new Point2D.Double(a,b));
+ }
+ if (stackTriangle.size()==3){
+ GeneralPath gp=new GeneralPath();
+ Line2D.Double ld=new Line2D.Double(stackTriangle.get(0),stackTriangle.get(1));
+ gp.append(ld,false);
+ ld=new Line2D.Double(stackTriangle.get(1),stackTriangle.get(2));
+ gp.append(ld,true);
+ ld=new Line2D.Double(stackTriangle.get(2),stackTriangle.get(0));
+ gp.append(ld,true);
+ g.fill(gp);
+ }
+ }
+
+ }
+ protected void startRecord2DPolygon(){
+ DrawPanel.record2D=DrawPanel.record2D_POLYGON;
+ stackTriangle=new Vector<Point2D.Double>();
+ stackTriangle.add(new Point2D.Double(tortue.corX,tortue.corY));
+ }
+ protected void stopRecord2DPolygon(){
+ DrawPanel.record2D=DrawPanel.record2D_NONE;
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/InstructionBuffer.java b/logo/src/xlogo/kernel/InstructionBuffer.java
new file mode 100644
index 0000000..befe0d9
--- /dev/null
+++ b/logo/src/xlogo/kernel/InstructionBuffer.java
@@ -0,0 +1,206 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+public class InstructionBuffer {
+ /**
+ * This integer represents the max Characters in the StringBuffer
+ */
+
+ private final static int MAX_CHARACTERS=15000;
+ /**
+ * The main StringBuffer containing instruction
+ */
+ private StringBuffer mainBuffer;
+ /**
+ * If the main StringBuffer is very large, Xlogo could become very slow. If main StringBuffer should contain more than MAX_CHARACTERS, those characters are saved in this StringBuffer
+ */
+ private StringBuffer stock;
+
+ InstructionBuffer(){
+ clear();
+ }
+ InstructionBuffer(String s){
+ mainBuffer=new StringBuffer(s);
+ stock=new StringBuffer();
+ }
+ /**
+ * Inserts some instructions at the beginning of the StringBuffer mainBuffer
+ * @param sb: The code to insert
+ */
+ void insertCode(StringBuffer sb){
+ if (sb.length()>InstructionBuffer.MAX_CHARACTERS) {
+ // insert current mainBuffer to stock
+ if (mainBuffer.length()!=0) stock.insert(0,mainBuffer);
+ // Copy MAX_CHARACTERS into mainBuffer
+ mainBuffer=new StringBuffer(sb.substring(0, InstructionBuffer.MAX_CHARACTERS));
+ // All remaining characters into stock
+ stock.insert(0,sb.substring(InstructionBuffer.MAX_CHARACTERS));
+ }
+ else {
+ mainBuffer.insert(0, sb);
+ }
+
+ }
+ /**
+ * returns the total length of the two Buffer
+ */
+ protected int getLength(){
+ return mainBuffer.length()+stock.length();
+ }
+ /**
+ * Inserts the String s at the beginning of the mainBuffer
+ * @param s
+ */
+ protected void insert(String s){
+ mainBuffer.insert(0, s);
+ }
+ /**
+ * Search for the String s , first in mainBuffer and then in stock
+ * if it isn't found in any buffer returns -1
+ * @param s: The String to search
+ */
+ protected int indexOf(String s){
+ int index=mainBuffer.indexOf(s);
+ if (index==-1){
+ index=stock.indexOf(s);
+ if (index==-1) return -1;
+ else return index+mainBuffer.length();
+ }
+ return index;
+
+ }
+ /**
+ * Search for the String s , first in mainBuffer and then in stock
+ * if not found in any buffer returns -1
+ * @param s: The String to search
+ */
+ protected int indexOf(String s, int fromIndex){
+ int index=-1;
+ if (fromIndex<mainBuffer.length()) index=mainBuffer.indexOf(s,fromIndex);
+ if (index==-1){
+ int from=0;
+ if (fromIndex>=mainBuffer.length()) from=fromIndex-mainBuffer.length();
+ index=stock.indexOf(s,from);
+ if (index==-1) return -1;
+ else return index+mainBuffer.length();
+ }
+ return index;
+
+ }
+
+
+ /**
+ * Delete all code from offset start to offset end
+ * @param start Start offset
+ * @param end End offset
+ */
+ protected void delete(int start, int end){
+ if (end<=mainBuffer.length()) mainBuffer.delete(start, end);
+ else {
+ stock.delete(0, end-mainBuffer.length());
+ mainBuffer=new StringBuffer();
+ transferStock();
+ }
+ if (mainBuffer.length()==0){
+ // if there are instruction in stock yet
+ if (stock.length()!=0) transferStock();
+ }
+
+ }
+ /**
+ * Transfers MAX_CHARCATERS from the buffer stock to mainBuffer
+ */
+
+ private void transferStock(){
+ if (stock.length()>InstructionBuffer.MAX_CHARACTERS){
+ mainBuffer.append(stock.substring(0, InstructionBuffer.MAX_CHARACTERS));
+ stock.delete(0, InstructionBuffer.MAX_CHARACTERS);
+ }
+ else {
+ mainBuffer.append(stock);
+ stock=new StringBuffer();
+ }
+ }
+ /**
+ * Returns next Word
+ * @return a String which represents the next word
+ */
+ protected String getNextWord() {
+ StringBuffer mot = new StringBuffer();
+ char caractere;
+ for (int i = 0; i < mainBuffer.length(); i++) {
+ caractere = mainBuffer.charAt(i);
+ if (caractere == ' ') {
+ return mot.toString();
+ } else
+ mot.append(caractere);
+ if (i==mainBuffer.length()-1&& stock.length()!=0){
+ transferStock();
+ }
+ }
+ // System.out.println("mot: "+mot);
+ return mot.toString();
+ }
+ /**
+ * Deletes the String mot from the mainBuffer instructions
+ * @param mot The string to delete
+ */
+ protected void deleteFirstWord(String mot) {
+ if (mainBuffer.length() > mot.length())
+ mainBuffer = mainBuffer.delete(0, mot.length() + 1);
+ else
+ mainBuffer = new StringBuffer();
+ if (mainBuffer.length()==0){
+ // if there are instruction in stock yet
+ if (stock.length()!=0) transferStock();
+ }
+ }
+ /**
+ * Return Character at the chosen index. this methos search first in mainBuffer and then in stock
+ * @param index The chosen index
+ * @return A character
+ */
+ protected char charAt(int index){
+ if (index<mainBuffer.length()) return mainBuffer.charAt(index);
+ return stock.charAt(index-mainBuffer.length());
+
+ }
+ /**
+ * Clear the both buffers
+ */
+ public void clear(){
+ mainBuffer=new StringBuffer();
+ stock=new StringBuffer();
+ }
+ public String toString(){
+ return mainBuffer.toString()+stock.toString();
+// return "MainBuffer:"+mainBuffer.toString()+":stock:"+stock.toString();
+ }
+}
diff --git a/logo/src/xlogo/kernel/Interprete.java b/logo/src/xlogo/kernel/Interprete.java
new file mode 100644
index 0000000..291674f
--- /dev/null
+++ b/logo/src/xlogo/kernel/Interprete.java
@@ -0,0 +1,981 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/** Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq */
+package xlogo.kernel;
+
+import java.util.Stack;
+import java.util.HashMap;
+
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.kernel.userspace.procedures.Procedure;
+
+public class Interprete
+{
+
+ private LaunchPrimitive lanceprim;
+
+ private Application app;
+
+ private Kernel kernel;
+
+ private UserSpace wp;
+ protected static boolean renvoi_instruction = false;
+ public static Stack<String> calcul = new Stack<String>();
+ protected static Stack<HashMap<String, String>> stockvariable = new Stack<HashMap<String, String>>();
+ protected static boolean stop = false;
+ protected static Stack<String> nom = new Stack<String>();
+ // procédures attendant
+ // parmamètres à recevoir
+ public static Stack<String> en_cours = new Stack<String>();
+ // actuellement en cours
+ // d'exécution
+ /**
+ * This buffer contains all instructions to execute
+ *
+ * @uml.property name="instructionBuffer"
+ * @uml.associationEnd multiplicity="(1 1)"
+ */
+ private InstructionBuffer instructionBuffer = new InstructionBuffer();
+
+ public static StringBuffer actionInstruction = new StringBuffer();
+
+ protected static HashMap<String, String> locale = new HashMap<String, String>();
+ // noms des variables
+ // locales
+
+ protected static boolean operande = false;
+ protected static boolean operateur = false;
+ protected static boolean drapeau_ouvrante = false;
+ protected static boolean drapeau_fermante = false;
+ public static String lineNumber = "";
+
+ // private TreeParser tp;
+ /*
+ * public Interprete(Application cadre){ this.cadre=cadre;
+ * lanceprim=new LaunchPrimitive(cadre); cadre.error=false; }
+ */
+ public Interprete(Application app)
+ {
+ this.kernel = app.getKernel();
+ this.app = app;
+ wp = kernel.getWorkspace();
+ lanceprim = new LaunchPrimitive(app, wp);
+ app.error = false;
+ }
+
+ String execute(StringBuffer instructions) throws LogoError
+ {
+ if (!instructions.equals(""))
+ {
+ instructionBuffer.insertCode(instructions);
+ }
+
+ // Object obca1,obca2,oban;
+ while (instructionBuffer.getLength() != 0)
+ {
+ if (app.error) // TODO && LogoError.lance : before, lance was always false, thus this branch was never taken. check this.
+ throw new LogoError(Logo.messages.getString("stop"));
+ while (app.affichage.isOnPause())
+ { // Si l'on touche aux scrollbars
+ try
+ {
+ wait();
+ }
+ catch (Exception e)
+ {}
+ }
+ // System.out.println("en_cours d'execution "+"\n"+
+ // en_cours+"\n\n");
+ // System.out.println("nom "+nom);
+ // System.out.println("calcul \n"+calcul+"\n\n");
+ // System.out.println("nom "+nom.toString()+" locale "+locale+ "
+ // "+valeur+" stockvariable "+stockvariable);
+ // System.out.println("operande "+calcul+" "+operande+"debut"+instructionBuffer);
+
+ // Is this line really interesting??
+ if (instructionBuffer.getLength() == 0)
+ break;
+ // System.out.print("debut\n"+instructionBuffer+"\nfin\n------------------\n");
+ String element = instructionBuffer.getNextWord();
+ // System.out.println(app.affichage.getPause()+element);
+
+ // System.out.println("/"+instructionBuffer+"/");
+
+ /*
+ * if (element=="")
+ * break;
+ *//* ***********************************************
+ * // si c'est une primitive ou une procedure *******
+ * ***********************************************
+ */
+ String element_minuscule = element.toLowerCase();
+ /*
+ * Marko Zivkovic: In XLogo4Schools, a procedure is identified by its name, not by its position in a stack.
+ * Now, i denotes only Logo primitives. If element_minuscule is a procedure, then i will be set to -2,
+ * s.t. the interpreter can assume as before that it denotes something other than primitive.
+ * If i >= 0, then the interpreter will work just as before, using i.
+ */
+ boolean isProcedure = wp.isExecutable(element_minuscule);
+ int i;
+
+ if (Primitive.primitives.containsKey(element_minuscule) || isProcedure)
+ {
+
+ // identifiant de la primitive
+ if (!isProcedure)
+ {
+ i = Integer.valueOf(Primitive.primitives.get(element_minuscule)).intValue()
+ % Primitive.PRIMITIVE_NUMBER;
+ }
+ else
+ {
+ //i = -i - 2;
+ i = -2; // Marko Zivkovic : i now denotes something else than a primitive : if i < -1 => isProcedure
+ }
+ // if (!calcul.empty()&&nom.isEmpty())
+ // throw new
+ // monException(cadre,Logo.messages.getString("que_faire")+"
+ // "+calcul.pop() +" gdfdsf");
+ // exécuter la procédure ou la primitive.
+ Stack<String> param = new Stack<String>();
+ if (isInfixedOperator(i))
+ { // Si c'est un opérateur infixé
+ deleteLineNumber();
+ operateur = true;
+ operande = false;
+ /*
+ * if (drapeau_ouvrante) { drapeau_ouvrante=false;
+ * if (i!=32&&i!=33) throw new myException(element+"
+ * "+Logo.messages.getString("ne_peut_etre")); else
+ * param.push("0"); }
+ */
+ // else
+ if (calcul.isEmpty())
+ { // Si le + ou le - représente le
+ // signe négatif ou positif
+ if (i != 32 && i != 33)
+ throw new LogoError(element + " " + Logo.messages.getString("error.ne_peut_etre")); // d'un
+ // nombre
+ if (nom.isEmpty())
+ param.push("0");
+ else
+ {
+ String st = nom.peek();
+ if (!testoperateur(st))
+ param.push("0");
+ else if ("*/".indexOf(st) > -1)
+ { // Si le signe -
+ // ou + suit un
+ // * ou /
+ instructionBuffer.deleteFirstWord(element);
+ if (st.equals("*"))
+ instructionBuffer.insert("* ");
+ else
+ instructionBuffer.insert("/ ");
+ if (i == 32)
+ return ("1"); // Si c'est un plus
+ else
+ return ("-1"); // Si c'est un moins
+ }
+ else
+ param.push("0");
+ }
+ }
+ else if (nom.isEmpty())
+ {
+ param.push(calcul.pop());
+ }
+ else
+ {
+ String st = nom.peek();
+ if (testoperateur(st))
+ {
+ // System.out.println("st "+st+" element "+element+"
+ // "+prioriteinf(st,element));
+ if (prioriteinf(st, element))
+ {
+ param.push(calcul.pop());
+ }
+ else
+ return (calcul.pop());
+ }
+ else
+ param.push(calcul.pop());
+ }
+ }// END: INFIX OPERATOR
+ else if (operande && i != 204)
+ {
+ checkParenthesis();
+ operande = false;
+ break;
+ } // Si ce n'est pas l'opérateur de fin de parenthèse, on sort
+
+ /*
+ * Example:
+ * To test | Formatted Form:
+ * fd 5 | fd 5 \l1 rt \l2
+ * rt | --> The \l2 can't be removed before be
+ * end | sure the rt has noproblem
+ */
+ if (!element.equals("\n"))
+ deleteLineNumber();
+ instructionBuffer.deleteFirstWord(element);
+
+ // Case with parenthensis
+ // eg (sum 3 4 5)
+ // eg (myProcedure 2 3 4 5)
+ if (drapeau_ouvrante)
+ {
+ drapeau_ouvrante = false;
+ int constantNumber = -1;
+ if (!hasGeneralForm(element_minuscule, i))
+ {
+ if (!isProcedure)
+ constantNumber = kernel.primitive.parametres[i];
+ else
+ constantNumber = wp.getExecutable(element_minuscule).nbparametre;
+ }
+ // Looking for all arguments (Number undefined)
+ nom.push(element);
+ int j = 0;
+ while (true)
+ {
+ /*
+ * This line fixed the bug for primitive or procedure
+ * without arguments
+ * eg: pr (pi+2)
+ */
+ if (constantNumber == 0)
+ break;
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+ if (instructionBuffer.getNextWord().equals(")"))
+ {
+ if (constantNumber != -1)
+ {
+ // If the primitive or the procedure doesn't
+ // accept optional parameters
+ if (j > constantNumber)
+ {
+ throw new LogoError(Logo.messages.getString("too_much_arguments"));
+ }
+ else if (j < constantNumber)
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek());
+ }
+ break;
+ }
+ String a = execute(new StringBuffer());
+
+ param.push(a);
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ j++;
+ }
+ // If It's a procedure
+
+ if (isProcedure)
+ {
+ if (wp.isProcedureAmbiguous(element_minuscule))
+ throw new LogoError(Logo.messages.getString("error.call.ambiguous.procedure.name"));
+
+ Procedure proc = wp.getExecutable(element_minuscule);
+
+ if (j > proc.nbparametre + proc.optVariables.size())
+ throw new LogoError(Logo.messages.getString("too_much_arguments"));
+ else if (j < proc.nbparametre)
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek());
+ // Searching for optional arguments that are not defined
+
+ if (j < proc.optVariables.size() + proc.nbparametre)
+ {
+ j = j - proc.nbparametre;
+ for (int c = j; c < proc.optVariables.size(); c++)
+ {
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+ String a = execute(proc.optVariablesExp.get(c));
+ param.push(a);
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ }
+ }
+ }
+
+ }
+ // classic case: predefined number of arguments
+ else
+ {
+ drapeau_ouvrante = false;
+ // How many arguments for the procedure or the primitive
+ int nbparametre = 0;
+ // For primitive
+ if (!isProcedure)
+ nbparametre = kernel.primitive.parametres[i];
+ // For procedure
+ else
+ nbparametre = wp.getExecutable(element_minuscule).nbparametre;
+ // Looking for each arguments
+ int j = 0;
+ nom.push(element);
+ while (j < nbparametre)
+ {
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+
+ String a = execute(new StringBuffer());
+ param.push(a);
+ j++;
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ }
+ // System.out.println(instructionBuffer.toString());
+ // System.out.println(nom+"arguments"+param);
+ // Looking for Optional arguments in case of procedure
+ if (isProcedure)
+ {
+ Procedure proc = wp.getExecutable(element_minuscule);
+ nbparametre = proc.optVariables.size();
+ for (j = 0; j < nbparametre; j++)
+ {
+ try
+ {
+ operande = operateur = drapeau_ouvrante = false;
+ String a = execute(proc.optVariablesExp.get(j));
+ param.push(a);
+ }
+ catch (LogoError e)
+ {
+ throw e;
+ }
+ }
+ }
+ }
+
+ // //////////////////////////////////////////////////////////////////////////////////////////
+ // System.out.println(nom+" "+"debut "+instruction+"
+ // fin\n"+param.toString());
+ // System.out.println(nom);
+ nom.pop();
+ if (!app.error)
+ lanceprim.execute(i, element_minuscule, param);
+ if (app.error)
+ break;
+ if (drapeau_fermante && !calcul.empty())
+ {
+ drapeau_fermante = false;
+ operande = false;
+ return calcul.pop();
+ }
+
+ // Tester si la procédure rend quelque chose lorsqu'attendu
+
+ if (!operande)
+ {
+ // dans le cas des primitives exec ou si
+ if (renvoi_instruction)
+ {
+ renvoi_instruction = false;
+ }
+ else
+ {
+ if (!nom.isEmpty() && !app.error && !nom.peek().equals("\n"))
+ {
+ if (!element.equals("\n"))
+ {
+ // If it's the end of a loop
+ // repeat 2 [fd 90 rt]
+ if (element.equals("\\"))
+ {
+ // The loop had been executed, we have to
+ // remove
+ // the loop instruction
+ int offset = instructionBuffer.indexOf(" \\ ");
+ instructionBuffer.delete(0, offset + 1);
+
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek());
+ }
+ // (av 100) ---> OK
+ // av av 20 ----> Bad
+ if (!nom.peek().equals("("))
+ throw new LogoError(element + " " + Logo.messages.getString("ne_renvoie_pas") + " "
+ + nom.peek());
+
+ }
+ }
+ }
+ }
+ else
+ {
+ // The primitive returns a word or a list.
+ // There's no primitive or procedure waiting for it.
+ if (!nom.isEmpty() && nom.peek().equals("\n"))
+ throw new LogoError(Logo.messages.getString("error.whattodo") + " " + calcul.peek() + " ?");
+ }
+ }
+
+ /* ********************************
+ * / IF element IS A VARIABLE
+ * ********************************
+ */
+ else if (element.substring(0, 1).equals(":") && element.length() > 1)
+ {
+ // System.out.println(operande);
+ if (operande)
+ {
+ checkParenthesis();
+ operande = false;
+ break;
+ }
+ else
+ deleteLineNumber();
+ String value;
+ String variableName = element_minuscule.substring(1, element_minuscule.length());
+ // If the variable isn't local
+ if (!locale.containsKey(variableName))
+ {
+ // check it's a global variable
+ if (!wp.getGlobals().getVariables().contains(variableName)) // TODO
+ // CHECK
+ throw new LogoError(variableName + " " + Logo.messages.getString("error.novalue"));
+ else
+ value = wp.getGlobals().getValue(variableName).toString(); // TODO
+ // CHECK
+ }
+ // If the variable is local
+ else
+ {
+ value = locale.get(variableName);
+ }
+
+ if (null == value)
+ throw new LogoError(variableName + " " + Logo.messages.getString("error.novalue"));
+ calcul.push(value);
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ instructionBuffer.deleteFirstWord(element);
+ }
+ else
+ {
+ /* *****************************
+ * IF element IS A NUMBER ******
+ * **************************
+ */
+ try
+ {
+ Double.parseDouble(element);
+ boolean deleteEndZero = false;
+ if (element.endsWith(".0"))
+ {
+ deleteEndZero = true;
+ element = element.substring(0, element.length() - 2);
+ }
+ /*
+ * boolean addStartZero=false;
+ * if (element.startsWith(".") || element.equals("")){
+ * element = "0" + element;
+ * addStartZero=true;
+ * }
+ */
+ calcul.push(element);
+ if (operande)
+ {
+ checkParenthesis();
+ calcul.pop();
+ operande = false;
+ break;
+ }
+ else
+ deleteLineNumber();
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ // if (addStartZero)
+ // instructionBuffer.deleteFirstWord(element.substring(1));
+ if (deleteEndZero)
+ instructionBuffer.deleteFirstWord(element + ".0");
+ else
+ instructionBuffer.deleteFirstWord(element);
+
+ }
+ catch (NumberFormatException e)
+ {
+ /* *********************************
+ * IF element IS A SQUARE BRACKET [
+ * OPEN
+ * **********************************
+ */
+ if (element.equals("["))
+ {
+
+ // Utilité de cette ligne?
+ // if (!calcul.isEmpty()&&operateur==false) break;
+ if (operande)
+ {
+ checkParenthesis();
+ break;
+ }
+ else
+ deleteLineNumber();
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ instructionBuffer.deleteFirstWord(element);
+ String a = chercheListe();
+ calcul.push(a);
+ }
+ /* ***************************
+ * IF element IS A PARENTHESIS
+ * OPEN
+ * ***********************
+ */
+ else if (element.equals("("))
+ {
+ if (operande)
+ {
+ checkParenthesis();
+ break;
+ }
+ else
+ deleteLineNumber();
+ drapeau_ouvrante = true;
+
+ Interprete.en_cours.push("(");
+ int pos = chercheParenthese();
+ if (pos == -1)
+ {
+ try
+ {
+ throw new LogoError(Logo.messages.getString("parenthese_fermante"));
+ }
+ catch (LogoError e1)
+ {}
+ }
+ instructionBuffer.deleteFirstWord(element);
+ // System.out.println("&&"+instruction);
+ Interprete.nom.push("(");
+ }
+ /* **********************************
+ * IF element IS A WORD
+ * **************************
+ */
+ else if (element.substring(0, 1).equals("\""))
+ {
+ try
+ {
+ String el = element.substring(1);
+ Double.parseDouble(el);
+ calcul.push(el);
+ }
+ catch (NumberFormatException e1)
+ {
+ calcul.push(element);
+ }
+ if (operande)
+ {
+ checkParenthesis();
+ calcul.pop();
+ operande = false;
+ break;
+ }
+ else
+ deleteLineNumber();
+ operande = true;
+ operateur = false;
+ drapeau_ouvrante = false;
+ instructionBuffer.deleteFirstWord(element);
+ }
+ // Si c'est le mot pour
+ else if (element_minuscule.equals(Logo.messages.getString("pour")))
+ {
+ instructionBuffer.deleteFirstWord(element);
+ if (instructionBuffer.getLength() != 0)
+ {
+ element = instructionBuffer.getNextWord();
+ element_minuscule = element.toLowerCase();
+ }
+ else
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + "\""
+ + Logo.messages.getString("pour") + "\"");
+ if (Primitive.primitives.containsKey(element_minuscule) || wp.isExecutable(element_minuscule)) // TODO
+ // check
+ throw new LogoError(element + " " + Logo.messages.getString("existe_deja"));
+ else
+ {
+ String definition = Logo.messages.getString("pour") + " " + element + " ";
+ instructionBuffer.deleteFirstWord(element);
+ while (instructionBuffer.getLength() != 0)
+ {
+ element = instructionBuffer.getNextWord().toLowerCase();
+ if (null == element)
+ break;
+ if (!element.substring(0, 1).equals(":") || element.length() == 1)
+ throw new LogoError(element + " " + Logo.messages.getString("pas_argument"));
+ definition += element + " ";
+ instructionBuffer.deleteFirstWord(element);
+ }
+ if (app.editeur.getComponent().isVisible())
+ throw new LogoError(Logo.messages.getString("ferme_editeur"));
+ else
+ {
+ app.editeur.getComponent().setVisible(true);
+ app.editeur.setEditorStyledText(definition + "\n\n" + Logo.messages.getString("fin"));
+ }
+ }
+ }
+ else if (element.startsWith("\\l"))
+ {
+ if (operande)
+ {
+ break;
+ }
+ instructionBuffer.deleteFirstWord(element);
+ lineNumber = element + " ";
+ element = instructionBuffer.getNextWord();
+
+ }
+ else
+ {
+ deleteLineNumber();
+ throw new LogoError(Logo.messages.getString("je_ne_sais_pas") + " " + element);
+ }
+ }
+ }
+ // System.out.println("instruction "+instruction+" calcul "+calcul);
+ }
+ /* ******************************
+ * **** END OF THE MAIN LOOP
+ * *****************************
+ */
+ // S'il n'y a rien à retourner.
+ if (calcul.isEmpty())
+ {
+ if (!nom.isEmpty())
+ {// &&!nom.peek().equals("\n")) {
+ while ((!nom.isEmpty()) && nom.peek().equals("\n"))
+ nom.pop();
+ if (!nom.isEmpty()) { throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek()); }
+ }
+ }
+ // Sinon on retourne la valeur contenue dans la pile de calcul.
+ if (!calcul.isEmpty())
+ {
+ // S'il y a une procédure de lancer
+ // Ex: pour t -- 6 -- fin . Puis, av t.
+ if ((!nom.isEmpty()) && nom.peek().equals("\n"))
+ {
+ String up = "";
+ int id = 0;
+ while (!nom.isEmpty() && nom.peek().equals("\n"))
+ {
+ nom.pop();
+ id++;
+ }
+ if (!nom.isEmpty())
+ {
+ up = nom.peek().toString();
+ try
+ {
+ throw new LogoError(en_cours.get(en_cours.size() - id) + " "
+ + Logo.messages.getString("ne_renvoie_pas") + " " + up);
+ }
+ catch (LogoError e)
+ {}
+ }
+ else
+ {
+ try
+ {
+ throw new LogoError(Logo.messages.getString("error.whattodo") + " " + calcul.peek() + " ?");
+ }
+ catch (LogoError e)
+ {}
+ }
+ /*
+ * }
+ * if (!nom.isEmpty() && nom.peek().equals("\n")) {
+ * up = en_cours.get(en_cours.size() - 2).toString();
+ * try {
+ * throw new myException( en_cours.peek() + " "
+ * + Logo.messages.getString("ne_renvoie_pas")
+ * + " " + up);
+ * } catch (myException e) {
+ * }
+ * } else if (!nom.isEmpty()) {
+ * up = nom.peek().toString();
+ * try {
+ * throw new myException( en_cours.peek() + " "
+ * + Logo.messages.getString("ne_renvoie_pas")
+ * + " " + up);
+ * } catch (myException e) {
+ * }
+ * } else {
+ * try {
+ * throw new myException( Logo.messages
+ * .getString("que_faire")
+ * + " " + calcul.peek() + " ?");
+ * } catch (myException e) {
+ * }}
+ */
+
+ }
+ // ///////////
+ else
+ {
+ operande = false;
+ return (calcul.pop());
+ }
+ }
+ return ("");
+ }
+
+ private int chercheParenthese()
+ { // position ou s'arrete la prochaine
+ // parenthese
+ boolean continuer = true;
+ int of_ouvrant;
+ int of_fermant = 0;
+ int from_index_ouvrant = 1;
+ int from_index_fermant = 1;
+ while (continuer)
+ {
+ of_ouvrant = instructionBuffer.indexOf("(", from_index_ouvrant);
+ of_fermant = instructionBuffer.indexOf(")", from_index_fermant);
+ if (of_fermant == -1)
+ break;
+ if (of_ouvrant != -1 && of_ouvrant < of_fermant)
+ {
+ from_index_ouvrant = of_ouvrant + 1;
+ from_index_fermant = of_fermant + 1;
+ }
+ else
+ continuer = false;
+ ;
+ }
+ return of_fermant;
+ }
+
+ protected String chercheListe() throws LogoError
+ {
+ String liste = "[ ";
+ String element = "";
+ while (instructionBuffer.getLength() != 0)
+ {
+ element = instructionBuffer.getNextWord();
+ // SI crochet ouvrant, on l'empile dans la pile de calcul
+ if (element.equals("["))
+ {
+ calcul.push("[");
+ instructionBuffer.deleteFirstWord(element);
+ liste += "[ ";
+ }
+
+ else if (element.equals("]"))
+ { // Si on atteint un crochet fermant
+ instructionBuffer.deleteFirstWord(element);
+ // if (((Stack)instruction.peek()).isEmpty()) instruction.pop();
+ liste += "] ";
+ if (calcul.empty())
+ {
+ return (liste);
+ } // 1er cas: rien dans la pile de calcul, on renvoie la liste
+ else if (!calcul.peek().toString().equals("["))
+ {
+ return (liste);
+ } // 2eme cas: pas de crochet ouvrant en haut de la pile, idem
+ else
+ calcul.pop(); // 3eme cas: un crochet ouvrant en haut de
+ // la pile, on l'enleve
+ }
+ else
+ {
+ instructionBuffer.deleteFirstWord(element);
+ liste += element + " ";
+ }
+ }
+ throw new LogoError(Logo.messages.getString("erreur_crochet"));
+ }
+
+ private boolean testoperateur(String st)
+ { // l'élément trouvé est-il un
+ // opérateur
+ int i = "+-*/<>=!&|".indexOf(st);
+ if (i == -1)
+ return (false);
+ return (true);
+ }
+
+ /**
+ * This method compares the two operators op and str.<br>
+ * Cette methode teste si l'operateur op (sommet de la pile d'appel)
+ * est de priorite strictement inferieur a str
+ *
+ * @param op
+ * The first operator
+ * @param str
+ * The second operator
+ * @return true if op has a minor priority than str.
+ */
+
+ private boolean prioriteinf(String op, String str)
+ { /*
+ * if (parenthesefermante>0) return(false); //
+ * else
+ */
+ if (isTimesDiv(str) && !isTimesDiv(op))
+ return (true);
+ else if (isPlusMinus(str) && isLogicOperator(op))
+ return (true);
+ else if (">=<=".indexOf(str) > -1 && "|&".indexOf(op) > -1)
+ return (true);
+ return (false);
+ }
+
+ /*
+ * private int isProcedure(String mot) { // vérifie si mot est une
+ * procédure
+ * for (int i = 0; i < wp.getNumberOfProcedure(); i++) {
+ * if (wp.getProcedure(i).name.equals(mot))
+ * return (i);
+ * }
+ * return (-1);
+ * }
+ */
+
+ protected void setWorkspace(UserSpace workspace)
+ {
+ wp = workspace;
+ lanceprim.setWorkspace(workspace);
+ }
+
+ private boolean hasGeneralForm(String name, int i)
+ {
+ // If it's a procedure
+ if (wp.isExecutable(name))
+ return !wp.getExecutable(name).optVariables.isEmpty();
+ return kernel.primitive.generalForm[i];
+ }
+
+ private void checkParenthesis() throws LogoError
+ {
+ if (!nom.isEmpty())
+ {
+ String name = nom.peek();
+ if (name.equals("(")) { throw new LogoError(Logo.messages.getString("too_much_arguments"));
+
+ }
+ }
+
+ }
+
+ private void deleteLineNumber()
+ {
+ lineNumber = "";
+ }
+
+ /**
+ * This method indicates if a primitive is an infixed operator<br>
+ * Infixed operators are for example: +,-,*-,/,&,>=.....
+ *
+ * @param id
+ * The integer identifiant for the primitive
+ * @return true or false if it's an infixed operator
+ */
+ private boolean isInfixedOperator(int id)
+ {
+ boolean b1 = (29 < id) && (id < 39);
+ boolean b2 = (id == 273) || (id == 274);
+ return b1 || b2;
+ }
+
+ /**
+ * This metods tests if the String op is a logic operator, ie a string like
+ * |,&,<,>,=,>=,<=
+ *
+ * @param op
+ * The operator to test
+ * @return true if op is a logic operator
+ */
+ private boolean isLogicOperator(String op)
+ {
+ return ("|&>=<=".indexOf(op) != -1);
+
+ }
+
+ /**
+ * This metods tests if the String op is + or -
+ *
+ * @param op
+ * The operator to test
+ * @return true if op is + or -
+ */
+ private boolean isPlusMinus(String op)
+ {
+ return (op.equals("+") || op.equals("-"));
+ }
+
+ /**
+ * This metods tests if the String op is / or *
+ *
+ * @param op
+ * The operator to test
+ * @return true if op is * or /
+ */
+ private boolean isTimesDiv(String op)
+ {
+ return (op.equals("*") || op.equals("/"));
+ }
+
+ /**
+ * @return
+ * @uml.property name="instructionBuffer"
+ */
+ protected InstructionBuffer getInstructionBuffer()
+ {
+ return instructionBuffer;
+ }
+}
diff --git a/logo/src/xlogo/kernel/Kernel.java b/logo/src/xlogo/kernel/Kernel.java
new file mode 100644
index 0000000..108fef4
--- /dev/null
+++ b/logo/src/xlogo/kernel/Kernel.java
@@ -0,0 +1,211 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.util.ArrayList;
+import java.awt.Color;
+
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.storage.user.DrawQuality;
+import xlogo.storage.workspace.Language;
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+import xlogo.Application;
+
+public class Kernel
+{
+ protected static long chrono = 0;
+
+ protected ArrayFlow flows = new ArrayFlow(); // Contient les
+ // flux de
+ // lecture ou
+ // d'écriture
+
+ protected static boolean mode_trace = false; // true si le
+ // mode trace
+ // est
+ // enclenchée
+ // (permet de
+ // suivre les
+ // procédures)
+
+ // interprete the user command and launch primitive and procedure
+ private Interprete interprete;
+ // For all drawing operation
+ // protected DrawPanel dg;
+ // For primitive
+ protected Primitive primitive = null;
+ private UserSpace userSpace;
+ private Application app;
+ private MP3Player mp3Player;
+ private MyCalculator myCalculator;
+
+ public Kernel(Application app, UserSpace userSpace)
+ {
+ this.app = app;
+ this.userSpace = userSpace;
+ initCalculator(-1);
+ }
+
+ public UserSpace getWorkspace()
+ {
+ return userSpace;
+ }
+
+ public void setWorkspace(UserSpace workspace)
+ {
+ userSpace = workspace;
+ interprete.setWorkspace(userSpace);
+ }
+
+ protected String listSearch() throws xlogo.kernel.LogoError
+ {
+ return interprete.chercheListe();
+ }
+
+ public void fcfg(Color color)
+ {
+ app.getDrawPanel().fcfg(color);
+ }
+
+ public Turtle getActiveTurtle()
+ {
+ return app.getDrawPanel().tortue;
+ }
+
+ public MyCalculator getCalculator()
+ {
+ return myCalculator;
+ }
+
+ public void fcc(Color color)
+ {
+ app.getDrawPanel().fcc(color);
+ }
+
+ public void vide_ecran()
+ {
+ app.getDrawPanel().videecran();
+ }
+
+ public void setNumberOfTurtles(int i)
+ {
+ app.getDrawPanel().setNumberOfTurtles(i);
+ }
+
+ public void setDrawingQuality(DrawQuality q)
+ {
+ app.getDrawPanel().setQuality(q);
+ }
+
+ public Color getScreenBackground()
+ {
+ return app.getDrawPanel().getBackgroundColor();
+ }
+
+ public void change_image_tortue(String chemin)
+ {
+ app.getDrawPanel().change_image_tortue(app, chemin);
+ }
+
+ public void initGraphics()
+ {
+ app.getDrawPanel().initGraphics();
+ }
+
+ public void buildPrimitiveTreemap(Language lang)
+ {
+ primitive.buildPrimitiveTreemap(lang);
+ }
+
+ public String execute(StringBuffer st) throws LogoError
+ {
+ return interprete.execute(st);
+ }
+
+ protected void initCalculator(int s)
+ {
+ myCalculator = new MyCalculator(s);
+
+ }
+
+ public void initPrimitive()
+ {
+ primitive = new Primitive(app);
+ }
+
+ public void initInterprete()
+ {
+ interprete = new Interprete(app);
+ }
+
+ /**
+ * Returns the InstructionBuffer containing all commands to execute
+ */
+ public InstructionBuffer getInstructionBuffer()
+ {
+ return interprete.getInstructionBuffer();
+ }
+
+ public void setMp3Player(MP3Player mp3Player)
+ {
+ this.mp3Player = mp3Player;
+ }
+
+ public MP3Player getMp3Player()
+ {
+ return mp3Player;
+ }
+
+ class ArrayFlow extends ArrayList<MyFlow>
+ {
+ ArrayFlow()
+ {
+ super();
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ protected int search(int id)
+ {
+ for (int i = 0; i < size(); i++)
+ {
+ if (get(i).getId() == id)
+ return i;
+ }
+ return -1;
+ }
+
+ }
+}
diff --git a/logo/src/xlogo/kernel/LaunchPrimitive.java b/logo/src/xlogo/kernel/LaunchPrimitive.java
new file mode 100644
index 0000000..47827cd
--- /dev/null
+++ b/logo/src/xlogo/kernel/LaunchPrimitive.java
@@ -0,0 +1,4751 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+
+import java.util.Stack;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.util.Calendar;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import javax.imageio.*;
+
+import java.io.*;
+
+import javax.swing.JOptionPane;
+import javax.swing.ImageIcon;
+import javax.swing.Icon;
+import javax.vecmath.Point3d;
+
+import java.math.BigDecimal;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.DrawQuality;
+import xlogo.storage.user.PenShape;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.Language;
+import xlogo.utils.Utils;
+import xlogo.gui.Lis;
+import xlogo.gui.MyTextAreaDialog;
+import xlogo.kernel.DrawPanel;
+import xlogo.gui.HistoryPanel;
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.network.*;
+import xlogo.kernel.gui.*;
+import xlogo.kernel.perspective.ElementPolygon;
+import xlogo.kernel.perspective.ElementLine;
+import xlogo.kernel.perspective.ElementPoint;
+import xlogo.kernel.userspace.UserSpace;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.messages.async.history.HistoryMessenger;
+
+/*******************************************************************************
+ * When a primitive or a procedure has all arguments, LauchPrimitive executes
+ * the appropriate code.
+ ******************************************************************************/
+public class LaunchPrimitive
+{
+ /**
+ * Default Application frame
+ */
+ private Application cadre;
+ /**
+ * Default kernel
+ */
+ private Kernel kernel;
+ /**
+ * Default workspace
+ */
+ private UserSpace wp;
+
+ private Procedure procedure;
+ // private MathContext mc=MathContext.DECIMAL64;
+ /**
+ * This is the start for the String returned by primitive or procedure.<br>
+ * It is "\"" for words and "" for numbers. <br>
+ * <br>
+ * Ceci est le début de la chaine générique renvoyé par les primitives<br>
+ * Elle vaut "\"" pour les mots et "" pour les nombres<br>
+ */
+ private String debut_chaine = "";
+ /**
+ * When we launch the primitive "listentcp", we have to save workspaces
+ */
+ //private Stack<LogoContext> savedWorkspace;
+
+ /**
+ * @param cadre
+ * Default frame Application
+ * @param wp
+ * Default workspace
+ */
+ public LaunchPrimitive(Application cadre, UserSpace wp)
+ {
+ this.wp = wp;
+ this.cadre = cadre;
+ this.kernel = cadre.getKernel();
+ }
+
+ /**
+ * Execute the primitive number "id" with the arguments contained in "param"<br>
+ * <ul>
+ * <li>if id<0: it is a procedure. <br>
+ * For example, if id=-3, it is procedure number -i-2=-(-3)-2=1</li>
+ * <li>if d>=0: it is primitive number "id"</li>
+ * </ul>
+ *
+ * @param id
+ * The number representing the procedure or the primitive
+ * @param param
+ * The Stack that contains all arguments
+ */
+ protected void execute(int id, String element_minuscule, Stack<String> param) throws LogoError
+ {
+ UserConfig uc = WSManager.getUserConfig();
+ int w = uc.getImageWidth();
+ int h = uc.getImageHeight();
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ String defaultFolder;
+
+ // identifiant procédure ou primitive, valeur des paramètres
+ if (id < 0)
+ {
+ procedure = wp.getExecutable(element_minuscule);
+ Interprete.stockvariable.push(Interprete.locale);
+ Interprete.locale = new HashMap<String, String>();
+ // Read local Variable
+ int optSize = procedure.optVariables.size();
+ int normSize = procedure.variable.size();
+ for (int j = 0; j < optSize + normSize; j++)
+ {
+ // Add local Variable
+ if (j < normSize)
+ {
+ Interprete.locale.put(procedure.variable.get(j), param.get(j));
+ } // add optional variables
+ else
+ {
+ String value = "";
+ if (j < param.size())
+ value = param.get(j);
+ else
+ value = procedure.optVariablesExp.get(j - param.size()).toString();
+ Interprete.locale.put(procedure.optVariables.get(j - normSize), value);
+
+ }
+ }
+ // Add Optional variable
+ if (Kernel.mode_trace)
+ {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < Interprete.en_cours.size(); i++)
+ buffer.append(" ");
+ buffer.append(procedure.name);
+ for (int i = 0; i < param.size(); i++)
+ buffer.append(" " + Utils.SortieTexte(param.get(i)));
+ String msg = buffer + "\n";
+ HistoryMessenger.getInstance().dispatchMessage(msg);
+ }
+ Interprete.en_cours.push(procedure.name);
+ procedure.decoupe();
+ // Add Procedure code in Interprete.instruction
+ kernel.getInstructionBuffer().insert("\n ");
+ kernel.getInstructionBuffer().insertCode(procedure.instr);
+ // System.out.println("instr " +Interprete.instruction);
+ // System.out.println("stock "+Interprete.stockInstruction);
+ // System.out.println("a"+Interprete.instruction+"a");
+ Interprete.nom.push("\n");
+ }
+ else
+ {
+ Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage();
+
+ switch (id)
+ {
+ case 0: // av
+ delay();
+ cadre.getDrawPanel().av(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 1: // re
+ delay();
+ cadre.getDrawPanel().av(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 2: // td
+ delay();
+ cadre.getDrawPanel().td(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 3: // tg
+ delay();
+ cadre.getDrawPanel().td(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 4: // arithmetic.power puissance
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().power(param.get(0), param.get(1)));
+ break;
+ case 5: // repete controls.repeat
+ String liste = getList(param.get(1));
+ kernel.primitive.repete(kernel.getCalculator().getInteger(param.get(0)), liste);
+ break;
+ case 6: // ve
+ cadre.getDrawPanel().videecran();
+ break;
+ case 7: // ct
+ if (kernel.getActiveTurtle().isVisible())
+ {
+ cadre.getDrawPanel().ct_mt();
+ cadre.getDrawPanel().tortues_visibles.remove(String.valueOf(kernel.getActiveTurtle().id));
+ }
+ kernel.getActiveTurtle().setVisible(false);
+ break;
+ case 8: // mt
+ if (!kernel.getActiveTurtle().isVisible())
+ {
+ cadre.getDrawPanel().ct_mt();
+ cadre.getDrawPanel().tortues_visibles.push(String.valueOf(kernel.getActiveTurtle().id));
+ }
+ kernel.getActiveTurtle().setVisible(true);
+ break;
+ case 9: // ecris, ec
+ int size = param.size();
+ String result = "";
+ String mot;
+ for (int i = 0; i < size; i++)
+ {
+ String par = param.get(i).trim();
+ if (isList(par))
+ par = formatList(par.substring(1, par.length() - 1));
+ mot = getWord(param.get(i));
+ if (null == mot)
+ result += Utils.SortieTexte(par) + " ";
+ else
+ result += Utils.SortieTexte(mot) + " ";
+ }
+ HistoryMessenger.getInstance().dispatchLogoOutput(result + "\n");
+ break;
+ case 10: // si // if
+
+ liste = getList(param.get(1));
+ liste = new String(Utils.decoupe(liste));
+ String liste2 = null;
+ boolean predicat = predicat(param.get(0));
+ InstructionBuffer instruction = cadre.getKernel().getInstructionBuffer();
+ if (instruction.getLength() != 0)
+ {
+ try
+ {
+ String element = instruction.getNextWord();
+ // System.out.println("a"+element+"a");
+ if (element.startsWith("\\l"))
+ {
+ instruction.deleteFirstWord(element);
+ Interprete.lineNumber = element + " ";
+ }
+ if (instruction.charAt(0) == '[')
+ {
+ instruction.deleteFirstWord("[");
+ liste2 = getFinalList(kernel.listSearch());
+ liste2 = new String(Utils.decoupe(liste2));
+ }
+ }
+ catch (Exception e)
+ {}
+ }
+ kernel.primitive.si(predicat, liste, liste2);
+ Interprete.renvoi_instruction = true;
+ break;
+ case 11: // STOP
+ kernel.primitive.stop();
+ break;
+ case 12: // origine
+ delay();
+ cadre.getDrawPanel().origine();
+ break;
+ case 13: // fpos
+ delay();
+ String list = getFinalList(param.get(0));
+ cadre.getDrawPanel().fpos(list);
+ break;
+ case 14: // fixex
+ delay();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double x = kernel.getCalculator().numberDouble(param.get(0));
+ double y = h / 2 - kernel.getActiveTurtle().corY;
+ cadre.getDrawPanel().fpos(x + " " + y);
+ }
+ else
+ cadre.getDrawPanel().fpos(
+ kernel.getCalculator().numberDouble(param.get(0)) + " " + kernel.getActiveTurtle().Y
+ + " " + kernel.getActiveTurtle().Z);
+ break;
+ case 15: // fixey
+ delay();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double y = kernel.getCalculator().numberDouble(param.get(0));
+ double x = kernel.getActiveTurtle().corX - w / 2;
+ cadre.getDrawPanel().fpos(x + " " + y);
+ }
+ else
+ cadre.getDrawPanel().fpos(
+ kernel.getActiveTurtle().X + " " + kernel.getCalculator().numberDouble(param.get(0))
+ + " " + kernel.getActiveTurtle().Z);
+ break;
+ case 16: // fixexy
+ delay();
+ primitive2D("drawing.fixexy");
+ cadre.getDrawPanel().fpos(
+ kernel.getCalculator().numberDouble(param.get(0)) + " "
+ + kernel.getCalculator().numberDouble(param.get(1)));
+ break;
+ case 17: // fixecap
+ delay();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ cadre.getDrawPanel().td(
+ 360 - kernel.getActiveTurtle().heading
+ + kernel.getCalculator().numberDouble(param.pop()));
+ else
+ {
+ cadre.getDrawPanel().setHeading(kernel.getCalculator().numberDouble(param.pop()));
+ }
+ break;
+ case 18: // lc
+ kernel.getActiveTurtle().setPenDown(false);
+ break;
+ case 19: // bc
+ kernel.getActiveTurtle().setPenDown(true);
+ break;
+ case 20: // gomme
+ kernel.getActiveTurtle().setPenDown(true);
+ // if mode penerase isn't active yet
+ if (kernel.getActiveTurtle().couleurmodedessin.equals(kernel.getActiveTurtle().couleurcrayon))
+ {
+ kernel.getActiveTurtle().couleurmodedessin = kernel.getActiveTurtle().couleurcrayon;
+ kernel.getActiveTurtle().couleurcrayon = cadre.getDrawPanel().getBackgroundColor();
+ kernel.getActiveTurtle().stroke = new BasicStroke(3); // TODO
+ // make
+ // member
+ }
+ break;
+ case 21: // inversecrayon
+ kernel.getActiveTurtle().setPenDown(true);
+ kernel.getActiveTurtle().setPenReverse(true);
+ break;
+ case 22: // dessine
+ kernel.getActiveTurtle().setPenReverse(false);
+ kernel.getActiveTurtle().setPenDown(true);
+ kernel.getActiveTurtle().couleurcrayon = kernel.getActiveTurtle().couleurmodedessin;
+ kernel.getActiveTurtle().stroke = new BasicStroke(1); // TODO
+ // make
+ // member
+ break;
+ case 23: // somme
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().add(param));
+ break;
+
+ case 24: // difference
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().substract(param));
+ break;
+ case 25: // arithmetic.minus moins (opposé)
+ Interprete.calcul.push(kernel.getCalculator().minus(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 26: // produit
+ Interprete.calcul.push(kernel.getCalculator().multiply(param));
+ Interprete.operande = true;
+ break;
+ case 27: // div
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().divide(param));
+ break;
+ case 28: // reste
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().remainder(param.get(0), param.get(1)));
+ break;
+ case 29: // retourne
+ kernel.primitive.retourne(param.get(0));
+ break;
+ case 30: // *
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().multiply(param));
+ break;
+ case 31: // diviser /
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().divide(param));
+ break;
+ case 32: // +
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().add(param));
+ break;
+ case 33: // -
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().substract(param));
+ break;
+ case 34: // =
+ equal(param);
+ break;
+ case 35: // <
+ inf(param);
+ break;
+ case 36: // >
+ sup(param);
+ break;
+ case 37: // |
+ boolean b1 = predicat(param.get(0));
+ boolean b2 = predicat(param.get(1));
+ b1 = b1 | b2;
+ if (b1)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ break;
+ case 38: // &
+ b1 = predicat(param.get(0));
+ b2 = predicat(param.get(1));
+ b1 = b1 & b2;
+ if (b1)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ break;
+ case 39: // opérateur interne \n signalant une fin de
+ // procédure
+ Interprete.locale = Interprete.stockvariable.pop();
+ if (Interprete.nom.peek().equals("\n"))
+ {
+ Interprete.nom.pop();
+ Interprete.lineNumber = "";
+ }
+ else
+ {
+ /*
+ * Example
+ * to bug
+ * av
+ * end
+ */
+ throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + Interprete.nom.peek());
+ }
+ /*
+ * to bug [:a] | (bug 10)
+ * av :a |
+ * end |
+ */
+ if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n")
+ && !Interprete.nom.peek().equals("("))
+ {
+ if (!cadre.error)
+ throw new LogoError(Interprete.en_cours.peek() + " "
+ + Logo.messages.getString("ne_renvoie_pas") + " "
+ + Interprete.nom.peek().toString());
+ }
+ if (!Interprete.en_cours.isEmpty())
+ Interprete.en_cours.pop();
+ break;
+ case 40: // opérateur interne \ signalant une fin de boucle
+
+ LoopProperties loop = Primitive.stackLoop.peek();
+ // LOOP REPEAT
+ if (loop.isRepeat())
+ {
+ BigDecimal compteur = loop.getCounter();
+ BigDecimal fin = loop.getEnd();
+ if (compteur.compareTo(fin) < 0)
+ {
+ loop.incremente();
+ Primitive.stackLoop.pop();
+ Primitive.stackLoop.push(loop);
+ cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " ");
+ }
+ else if (compteur.compareTo(fin) == 0)
+ {
+ Primitive.stackLoop.pop();
+ }
+ }
+ // LOOP FOR or LOOP FOREACH
+ else if (loop.isFor() || loop.isForEach())
+ {
+ BigDecimal inc = loop.getIncrement();
+ BigDecimal compteur = loop.getCounter();
+ BigDecimal fin = loop.getEnd();
+ if ((inc.compareTo(BigDecimal.ZERO) == 1 && (compteur.add(inc).compareTo(fin) <= 0))
+ || (inc.compareTo(BigDecimal.ZERO) == -1 && (compteur.add(inc).compareTo(fin) >= 0)))
+ {
+ loop.incremente();
+ ((LoopFor) loop).AffecteVar(false);
+ Primitive.stackLoop.pop();
+ Primitive.stackLoop.push(loop);
+ cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " ");
+ }
+ else
+ {
+ ((LoopFor) loop).DeleteVar();
+ Primitive.stackLoop.pop();
+ }
+ }
+ // LOOP FOREVER
+ else if (loop.isForEver())
+ {
+ cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " ");
+ }
+ // LOOP FILL POLYGON
+ else if (loop.isFillPolygon())
+ {
+ cadre.getDrawPanel().stopRecord2DPolygon();
+ Primitive.stackLoop.pop();
+ }
+ break;
+ case 41: // pos
+ Interprete.operande = true;
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double a = kernel.getActiveTurtle().corX - w / 2;
+ double b = h / 2 - kernel.getActiveTurtle().corY;
+ Interprete.calcul.push("[ " + MyCalculator.teste_fin_double(a) + " "
+ + MyCalculator.teste_fin_double(b) + " ] ");
+ }
+ else
+ {
+ Interprete.calcul.push("[ " + kernel.getActiveTurtle().X + " " + kernel.getActiveTurtle().Y
+ + " " + kernel.getActiveTurtle().Z + " ] ");
+
+ }
+ break;
+ case 42: // cap
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().heading));
+ break;
+ case 43: // arrondi
+ Interprete.operande = true;
+ Interprete.calcul
+ .push(String.valueOf(Math.round(kernel.getCalculator().numberDouble(param.get(0)))));
+ break;
+ case 44: // log10
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().log10(param.get(0)));
+ break;
+ case 45: // arithmetic.sin
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().sin(param.get(0)));
+ break;
+ case 46: // arithmetic.cos
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().cos(param.get(0)));
+ break;
+ case 47: // ou
+ ou(param);
+ break;
+ case 48: // et
+ et(param);
+ break;
+ case 49: // non
+ Interprete.operande = true;
+ b1 = predicat(param.get(0));
+ if (b1)
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ break;
+ case 50: // liste
+ liste = "[ ";
+ Interprete.operande = true;
+ String mot2;
+ for (int i = 0; i < param.size(); i++)
+ {
+ mot2 = param.get(i);
+ mot = getWord(param.get(i));
+ if (null == mot)
+ {
+ liste += mot2;
+ // System.out.println("a"+mot2+"a");
+ }
+ else
+ {
+ if (mot.equals(""))
+ mot = "\\v";
+ liste += mot + " ";
+ }
+ }
+ Interprete.calcul.push(liste + "] ");
+ break;
+ case 51: // phrase
+ liste = "[ ";
+ Interprete.operande = true;
+ for (int i = 0; i < param.size(); i++)
+ {
+ mot = getWord(param.get(i));
+ mot2 = param.get(i).trim();
+ if (null == mot)
+ {
+ if (isList(mot2))
+ liste += mot2.substring(1, mot2.length() - 1).trim() + " ";
+ else
+ liste += mot2 + " ";
+ }
+ else
+ {
+ if (mot.equals(""))
+ mot = "\\v";
+ liste += mot + " ";
+ }
+ }
+ Interprete.calcul.push(liste + "] ");
+ break;
+ case 52: // metspremier
+ liste = getFinalList(param.get(1));
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ {
+ if (!liste.equals(""))
+ Interprete.calcul.push("[ " + param.get(0).trim() + " " + liste.trim() + " ] ");
+ else
+ Interprete.calcul.push("[ " + param.get(0).trim() + " ] ");
+ }
+ else
+ {
+ if (!liste.equals(""))
+ Interprete.calcul.push("[ " + mot + " " + liste.trim() + " ] ");
+ else
+ Interprete.calcul.push("[ " + mot + " ] ");
+ }
+ break;
+ case 53: // metsdernier
+ liste = getFinalList(param.get(1)).trim();
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ { // Si c'est une liste
+ Interprete.calcul.push(("[ " + liste).trim() + " " + param.get(0).trim() + " ] ");
+
+ }
+ else
+ Interprete.calcul.push(("[ " + liste).trim() + " " + mot + " ] ");
+
+ break;
+ case 54: // inverse liste
+ liste = getFinalList(param.get(0)).trim();
+ Interprete.operande = true;
+ StringTokenizer st = new StringTokenizer(liste);
+ liste = " ] ";
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ if (element.equals("["))
+ element = extractList(st);
+ liste = " " + element + liste;
+ }
+ Interprete.calcul.push("[" + liste);
+ break;
+ case 55: // choix
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0));
+ int nombre = (int) Math.floor(numberOfElements(liste) * Math.random()) + 1;
+ String tmp = item(liste, nombre);
+ if (tmp.equals("\"\\v"))
+ tmp = "\"";
+ Interprete.calcul.push(tmp);
+ }
+ else
+ {
+ int nombre = (int) Math.floor(Math.random() * getWordLength(mot)) + 1;
+ String str = "";
+ try
+ {
+ str = itemWord(nombre, mot);
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e1)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ break;
+ case 56: // enleve
+ Interprete.operande = true;
+ liste = getFinalList(param.get(1));
+ st = new StringTokenizer(liste);
+ liste = "[ ";
+ mot = getWord(param.get(0));
+ String str;
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = param.get(0).trim();
+
+ while (st.hasMoreTokens())
+ {
+ str = st.nextToken();
+ if (str.equals("["))
+ str = extractList(st);
+ if (!str.equals(mot))
+ liste += str + " ";
+ }
+ Interprete.calcul.push(liste.trim() + " ] ");
+ break;
+ case 57: // item
+ Interprete.operande = true;
+ mot = getWord(param.get(1));
+ if (null == mot)
+ Interprete.calcul.push(item(getFinalList(param.get(1)),
+ kernel.getCalculator().getInteger(param.get(0))));
+ else
+ {
+ int i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 1 || i > getWordLength(mot))
+ throw new LogoError(Utils.primitiveName("item") + " "
+ + Logo.messages.getString("n_aime_pas") + i + " "
+ + Logo.messages.getString("comme_parametre") + ".");
+ else
+ {
+ str = itemWord(i, mot);
+ try
+ {
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e1)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ }
+ break;
+ case 58: // saufdernier
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0)).trim();
+ element = item(liste, numberOfElements(liste));
+ int longueur = element.length();
+
+ if (element.startsWith("\"") || element.startsWith("["))
+ longueur--;
+ Interprete.calcul.push("[ " + liste.substring(0, liste.length() - longueur) + "] ");
+ }
+ else if (mot.equals(""))
+ {
+ throw new LogoError(Logo.messages.getString("mot_vide"));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push("\"");
+ else
+ {
+ String tmp = mot.substring(0, mot.length() - 1);
+ if (tmp.endsWith("\\"))
+ tmp = tmp.substring(0, tmp.length() - 1);
+ try
+ {
+ Double.parseDouble(tmp);
+ Interprete.calcul.push(tmp);
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(debut_chaine + tmp);
+ }
+ }
+ break;
+ case 59: // saufpremier
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0)).trim();
+ element = item(liste, 1);
+ int longueur = element.length();
+ if (element.startsWith("\"") || element.startsWith("["))
+ longueur--;
+ Interprete.calcul.push("[" + liste.substring(longueur, liste.length()) + " ] ");
+ }
+ else if (mot.equals(""))
+ {
+ throw new LogoError(Logo.messages.getString("mot_vide"));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push("\"");
+ else
+ {
+ if (!mot.startsWith("\\"))
+ mot = mot.substring(1);
+ else
+ mot = mot.substring(2);
+ try
+ {
+ Double.parseDouble(mot);
+ Interprete.calcul.push(mot);
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(debut_chaine + mot);
+ }
+ }
+
+ break;
+ case 60: // dernier
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ { // Si c'est une liste
+ liste = getFinalList(param.get(0));
+ Interprete.calcul.push(item(liste, numberOfElements(liste)));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push(debut_chaine + mot);
+ else
+ {
+ str = "";
+ try
+ {
+ str = itemWord(getWordLength(mot), mot);
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e1)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ break;
+ case 61: // premier first
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ { // SI c'est une liste
+ liste = getFinalList(param.get(0));
+ // System.out.println("b"+item(liste, 1)+"b");
+ Interprete.calcul.push(item(liste, 1));
+ }
+ else if (getWordLength(mot) == 1)
+ Interprete.calcul.push(debut_chaine + mot);
+ else
+ {
+ str = "";
+ try
+ {
+ str = itemWord(1, mot);
+ Double.parseDouble(str);
+ Interprete.calcul.push(str);
+ }
+ catch (NumberFormatException e2)
+ {
+ Interprete.calcul.push("\"" + str);
+ }
+ }
+ break;
+ case 62: // compte
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ liste = getFinalList(param.get(0));
+ Interprete.calcul.push(String.valueOf(numberOfElements(liste)));
+ }
+ else
+ Interprete.calcul.push(String.valueOf(getWordLength(mot)));
+ break;
+ case 63: // mot?
+ mot = getWord(param.get(0));
+ if (null == mot)
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ Interprete.operande = true;
+ break;
+ case 64: // nombre?
+ try
+ {
+ Double.parseDouble(param.get(0));
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ Interprete.operande = true;
+ break;
+ case 65: // liste?
+ liste = param.get(0).trim();
+ if (isList(liste))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ break;
+ case 66: // vide?
+ liste = param.get(0).trim();
+ mot = getWord(param.get(0));
+ if (null == mot)
+ { // si c'est une liste ou un nombre
+ liste = getFinalList(liste).trim();
+ if (liste.equals(""))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ else
+ { // Si c'est un mot
+ if (mot.equals(""))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ Interprete.operande = true;
+ break;
+ case 67: // egal?
+ equal(param);
+ break;
+ case 68: // precede?
+ precede(param);
+ break;
+ case 69: // membre ?
+ membre(param, id);
+ break;
+ case 70: // racine arithmetic.sqrt
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().sqrt(param.get(0)));
+ break;
+ case 71: // membre
+ membre(param, id);
+ break;
+ case 72: // donne
+ donne(param);
+ Interprete.operande = false;
+
+ break;
+ case 73: // locale
+ locale(param);
+ Interprete.operande = false;
+ break;
+ case 74: // donnelocale
+ locale(param);
+ donne(param);
+ Interprete.operande = false;
+ break;
+ case 75: // fcc
+ Color color = null;
+ if (isList(param.get(0)))
+ {
+ color = rgb(param.get(0), Utils.primitiveName("fcc"));
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ color = DrawPanel.defaultColors[coul];
+ }
+ cadre.getDrawPanel().fcc(color);
+ break;
+ case 76: // fcfg setscreencolor
+ color = null;
+ if (isList(param.get(0)))
+ {
+ color = rgb(param.get(0), Utils.primitiveName("fcfg"));
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ color = DrawPanel.defaultColors[coul];
+ }
+ cadre.getDrawPanel().fcfg(color);
+ break;
+ case 77: // hasard
+ Interprete.operande = true;
+ int i = kernel.getCalculator().getInteger(param.get(0));
+ i = (int) Math.floor(Math.random() * i);
+ Interprete.calcul.push(String.valueOf(i));
+ break;
+ case 78: // attends
+ try
+ {
+ int temps = kernel.getCalculator().getInteger(param.get(0));
+ if (temps < 0)
+ {
+ String attends = Utils.primitiveName("attends");
+ throw new LogoError(attends + " " + Logo.messages.getString("attend_positif"));
+ }
+ else
+ {
+ int nbsecondes = temps / 60;
+ int reste = temps % 60;
+ for (i = 0; i < nbsecondes; i++)
+ {
+ Thread.sleep(1000);
+ if (cadre.error)
+ break;
+ }
+ if (!cadre.error)
+ Thread.sleep(reste * 50 / 3);
+ }
+
+ }
+ catch (InterruptedException e2)
+ {}
+ break;
+ case 79: // procedures
+ Interprete.operande = true;
+ Interprete.calcul.push(new String(getAllProcedures()));
+ break;
+ case 80: // effaceprocedure efp
+ erase(param.get(0), "procedure");
+ break;
+
+ case 81: // effacevariable
+ erase(param.get(0), "variable");
+ break;
+ case 82: // effacetout erall
+ /*
+ * Marko Zivkovic:
+ * In XLogo4Schools, we delete all files (together with the
+ * procedures) and clear all variables and property lists.
+ */
+ wp.eraseAll();
+ break;
+ case 83: // mot
+ Interprete.operande = true;
+ result = "";
+ for (i = 0; i < param.size(); i++)
+ {
+ mot = getWord(param.get(i));
+ if (null == mot)
+ throw new LogoError(param.get(i) + " " + Logo.messages.getString("error.word"));
+ result += mot;
+ }
+ try
+ {
+ Double.parseDouble(result);
+ }
+ catch (NumberFormatException e)
+ {
+ result = "\"" + result;
+ }
+ Interprete.calcul.push(result);
+ break;
+ case 84: // etiquette
+ String par = param.get(0).trim();
+ if (isList(par))
+ par = formatList(par.substring(1, par.length() - 1));
+ mot = getWord(param.get(0));
+ if (null == mot)
+ cadre.getDrawPanel().etiquette(Utils.SortieTexte(par));
+ else
+ cadre.getDrawPanel().etiquette(Utils.SortieTexte(mot));
+ break;
+ case 85: // /trouvecouleur
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(false);
+
+ liste = getFinalList(param.get(0));
+ Color r = cadre.getDrawPanel().guessColorPoint(liste);
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + r.getRed() + " " + r.getGreen() + " " + r.getBlue() + " ] ");
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(true);
+ break;
+ case 86: // fenetre
+ cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_CLASSIC);
+ break;
+ case 87: // enroule
+ cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_WRAP);
+ break;
+ case 88: // clos
+ cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_CLOSE);
+ break;
+ case 89: // videtexte
+ cadre.getHistoryPanel().vide_texte();
+ break;
+ case 90: // chargeimage
+ BufferedImage image = null;
+
+ primitive2D("ci");
+ image = getImage(param.get(0));
+ if (null != image)
+ cadre.getDrawPanel().chargeimage(image);
+ break;
+ case 91: // ftc, fixetaillecrayon
+ double nombre = kernel.getCalculator().numberDouble(param.get(0));
+ if (nombre < 0)
+ nombre = Math.abs(nombre);
+ if (DrawPanel.record3D == DrawPanel.record3D_LINE || DrawPanel.record3D == DrawPanel.record3D_POINT)
+ {
+ if (kernel.getActiveTurtle().getPenWidth() != (float) nombre)
+ DrawPanel.poly.addToScene();
+ }
+ kernel.getActiveTurtle().fixe_taille_crayon((float) nombre);
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon);
+ if (DrawPanel.record3D == DrawPanel.record3D_LINE)
+ {
+ DrawPanel.poly = new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ DrawPanel.poly.addVertex(
+ new Point3d(kernel.getActiveTurtle().X / 1000, kernel.getActiveTurtle().Y / 1000,
+ kernel.getActiveTurtle().Z / 1000), kernel.getActiveTurtle().couleurcrayon);
+ }
+ else if (DrawPanel.record3D == DrawPanel.record3D_POINT)
+ {
+ DrawPanel.poly = new ElementPoint(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ }
+ break;
+ case 92: // tantque
+ String li1 = getList(param.get(0));
+ li1 = new String(Utils.decoupe(li1));
+ String li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ String instr = "\\siwhile " + li1 + "[ " + li2 + "] ";
+ LoopWhile bp = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr);
+ Primitive.stackLoop.push(bp);
+ cadre.getKernel().getInstructionBuffer().insert(instr + Primitive.END_LOOP + " ");
+
+ break;
+ case 93: // lis
+ liste = getFinalList(param.get(0));
+ mot = getWord(param.get(1));
+ if (null == mot)
+ throw new LogoError(Logo.messages.getString("error.word"));
+ java.awt.FontMetrics fm = cadre.getFrame().getGraphics().getFontMetrics(font);
+ int longueur = fm.stringWidth(liste) + 100;
+ Lis lis = new Lis(liste, longueur);
+ while (lis.isVisible())
+ {
+ try
+ {
+ Thread.sleep(50);
+ }
+ catch (InterruptedException e)
+ {}
+ }
+ param = new Stack<String>();
+ param.push("\"" + mot);
+ String phrase = lis.getText();
+ // phrase="[ "+Logo.rajoute_backslash(phrase)+" ] ";
+ StringBuffer tampon = new StringBuffer();
+ for (int j = 0; j < phrase.length(); j++)
+ {
+ char c = phrase.charAt(j);
+ if (c == '\\')
+ tampon.append("\\\\");
+ else
+ tampon.append(c);
+ }
+ int offset = tampon.indexOf(" ");
+ if (offset != -1)
+ {
+ tampon.insert(0, "[ ");
+ tampon.append(" ] ");
+ }
+ else
+ {
+ try
+ {
+ Double.parseDouble(phrase);
+ }
+ catch (NumberFormatException e)
+ {
+ tampon.insert(0, "\"");
+ }
+ }
+ phrase = new String(tampon);
+ param.push(phrase);
+ donne(param);
+ String texte = liste + "\n" + phrase;
+ HistoryMessenger.getInstance().dispatchComment(Utils.SortieTexte(texte) + "\n");
+ cadre.focus_Commande();
+ lis.dispose();
+ cadre.focus_Commande();
+ break;
+ case 94: // touche?
+ Interprete.operande = true;
+ if (cadre.getCar() != -1)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 95: // siwhile --> Evalue l'expression test du while
+ liste = getFinalList(param.get(1));
+ boolean pred = predicat(param.get(0));
+ kernel.primitive.whilesi(pred, liste);
+ break;
+ case 96: // liscar
+ while (cadre.getCar() == -1)
+ {
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {}
+ //if (LogoError.lance) //TODO this was always false
+ // break;
+ }
+ Interprete.calcul.push(String.valueOf(cadre.getCar()));
+ Interprete.operande = true;
+ cadre.setCar(-1);
+ break;
+ case 97: // remplis
+ cadre.getDrawPanel().remplis();
+ break;
+ case 98: // point
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(false);
+
+ cadre.getDrawPanel().point(getFinalList(param.get(0)));
+ if (kernel.getActiveTurtle().isVisible())
+ cadre.getDrawPanel().montrecacheTortue(true);
+ break;
+ case 99: // vers=towards vers
+
+ Interprete.operande = true;
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ double angle = cadre.getDrawPanel().vers2D(getFinalList(param.get(0)));
+ Interprete.calcul.push(MyCalculator.teste_fin_double(angle));
+ }
+ else
+ {
+ double[] orientation = cadre.getDrawPanel().vers3D(getFinalList(param.get(0)));
+ Interprete.calcul.push("[ " + orientation[0] + " " + orientation[1] + " " + orientation[2]
+ + " ] ");
+ }
+ break;
+ case 100: // distance
+ Interprete.operande = true;
+ double distance = cadre.getDrawPanel().distance(getFinalList(param.get(0)));
+ Interprete.calcul.push(MyCalculator.teste_fin_double(distance));
+ break;
+ case 101: // couleurcrayon
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + kernel.getActiveTurtle().couleurcrayon.getRed() + " "
+ + kernel.getActiveTurtle().couleurcrayon.getGreen() + " "
+ + kernel.getActiveTurtle().couleurcrayon.getBlue() + " ] ");
+ break;
+ case 102: // couleurfond
+ Interprete.operande = true;
+ color = cadre.getDrawPanel().getBackgroundColor();
+ Interprete.calcul.push("[ " + color.getRed() + " " + color.getGreen() + " " + color.getBlue()
+ + " ] ");
+ break;
+ case 103: // bc?
+ Interprete.operande = true;
+ if (kernel.getActiveTurtle().isPenDown())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 104: // visible?
+ Interprete.operande = true;
+ if (kernel.getActiveTurtle().isVisible())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 105: // prim?
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ if (Primitive.primitives.containsKey(mot))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 106: // proc?
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (wp.isExecutable(mot))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 107: // exec
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ mot = getList(param.get(0).trim());
+ mot = new String(Utils.decoupe(mot));
+ }
+ else
+ mot = mot + " ";
+ cadre.getKernel().getInstructionBuffer().insert(mot);
+ Interprete.renvoi_instruction = true;
+ break;
+ case 108: // catalogue
+ defaultFolder = uc.getDefaultFolder();
+ str = Utils.SortieTexte(defaultFolder);
+ File f = new File(str);
+ String fichier = "";
+ String dossier = "";
+ int nbdossier = 0;
+ int nbfichier = 0;
+ String[] l = f.list();
+ for (i = 0; i < l.length; i++)
+ {
+ if ((new File(str + File.separator + l[i])).isDirectory())
+ {
+ nbdossier++;
+ if (nbdossier % 5 == 0)
+ dossier += l[i] + "\n";
+ else
+ dossier += l[i] + " ";
+ }
+ else
+ {
+ nbfichier++;
+ if (nbfichier % 5 == 0)
+ fichier += l[i] + "\n";
+ else
+ fichier += l[i] + " ";
+ }
+ }
+ texte = "";
+ if (!dossier.equals(""))
+ texte += Logo.messages.getString("repertoires") + ":\n" + dossier + "\n";
+ if (!fichier.equals(""))
+ texte += Logo.messages.getString("fichiers") + ":\n" + fichier + "\n";
+ HistoryMessenger.getInstance().dispatchComment(texte);
+ break;
+ case 109: // frepertoire
+ liste = getWord(param.get(0));
+ if (null == liste)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ String chemin = Utils.SortieTexte(liste);
+ if ((new File(chemin)).isDirectory() && !chemin.startsWith(".."))
+ {
+ defaultFolder = Utils.rajoute_backslash(chemin);
+ uc.setDefaultFolder(defaultFolder);
+ }
+ else
+ throw new LogoError(liste + " " + Logo.messages.getString("erreur_pas_repertoire"));
+ break;
+ case 110: // repertoire
+ Interprete.operande = true;
+ defaultFolder = uc.getDefaultFolder();
+ Interprete.calcul.push("\"" + defaultFolder);
+ break;
+ case 111: // sauve
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ st = new StringTokenizer(liste);
+ Stack<String> pile = new Stack<String>();
+ while (st.hasMoreTokens())
+ pile.push(st.nextToken());
+ saveProcedures(mot, pile);
+ break;
+ case 112: // sauved
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ saveProcedures(mot, null);
+ break;
+ case 113: // ramene load
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ defaultFolder = uc.getDefaultFolder();
+ String path = Utils.SortieTexte(defaultFolder) + File.separator + mot;
+ String fileName = wp.makeUniqueFileName(mot);
+ try
+ {
+ String txt = Utils.readLogoFile(path); // TODO this
+ // does not
+ // work...
+ // should
+ // load from
+ // path....
+ // later:
+ // looks ok,
+ // not?
+ wp.createFile(fileName);
+ wp.writeFileText(fileName, txt);
+ }
+ catch (IOException e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+
+ break;
+ case 114: // pi
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().pi());
+ break;
+ case 115: // tangente arithmetic.tan
+ Interprete.operande = true;
+
+ Interprete.calcul.push(kernel.getCalculator().tan(param.get(0)));
+
+ break;
+ case 116: // acos
+
+ Interprete.calcul.push(kernel.getCalculator().acos(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 117: // asin
+
+ Interprete.calcul.push(kernel.getCalculator().asin(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 118: // atan
+ Interprete.calcul.push(kernel.getCalculator().atan(param.get(0)));
+ Interprete.operande = true;
+ break;
+ case 119: // vrai
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ break;
+ case 120: // faux
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 121: // forme
+ primitive2D("turtle.forme");
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().getShape()));
+ break;
+ case 122: // fixeforme setshape
+ primitive2D("turtle.fforme");
+ i = kernel.getCalculator().getInteger(param.get(0)) % 7;
+ if (kernel.getActiveTurtle().id == 0)
+ {
+ uc.setActiveTurtle(i);
+ }
+ chemin = "tortue" + i + ".png";
+ kernel.change_image_tortue(chemin);
+ break;
+ case 123: // definis workspace.define
+ define(param); // Method extracted by Marko Zivkovic
+ // 21.6.2013
+ break;
+
+ case 124: // tortue
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().id));
+ break;
+ case 125: // tortues
+ Interprete.operande = true;
+ String li = "[ ";
+ for (i = 0; i < cadre.getDrawPanel().tortues.length; i++)
+ {
+ if (null != cadre.getDrawPanel().tortues[i])
+ li += String.valueOf(i) + " ";
+ }
+ li += "]";
+ Interprete.calcul.push(li);
+ break;
+ case 126: // fixetortue
+ try
+ {
+ i = Integer.parseInt(param.get(0));
+ if (i > -1 && i < uc.getMaxTurtles())
+ {
+ if (null == cadre.getDrawPanel().tortues[i])
+ {
+ cadre.getDrawPanel().tortues[i] = new Turtle(cadre);
+ cadre.getDrawPanel().tortues[i].id = i;
+ cadre.getDrawPanel().tortues[i].setVisible(false);
+ }
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[i];
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon);
+ String police = cadre.getDrawPanel().getGraphicsFont().getName();
+ cadre.getDrawPanel().setGraphicsFont(
+ new java.awt.Font(police, java.awt.Font.PLAIN, kernel.getActiveTurtle().police));
+
+ }
+ else
+ {
+ throw new LogoError(Logo.messages.getString("tortue_inaccessible"));
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ kernel.getCalculator().getInteger(param.get(0));
+ }
+ break;
+ case 127: // police
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().police));
+ break;
+ case 128: // fixetaillepolice
+ int taille = kernel.getCalculator().getInteger(param.get(0));
+ kernel.getActiveTurtle().police = taille;
+ Font police = font;
+ cadre.getDrawPanel().setGraphicsFont(police.deriveFont((float) kernel.getActiveTurtle().police));
+
+ break;
+ case 129: // tuetortue
+ try
+ {
+ id = Integer.parseInt(param.get(0));
+ if (id > -1 && id < uc.getMaxTurtles())
+ {
+ // On compte le nombre de tortues à l'écran
+ int compteur = 0;
+ int premier_dispo = -1;
+ for (i = 0; i < uc.getMaxTurtles(); i++)
+ {
+ if (null != cadre.getDrawPanel().tortues[i])
+ {
+ if (i != id && premier_dispo == -1)
+ premier_dispo = i;
+ compteur++;
+ }
+ }
+ // On vérifie que ce n'est pas la seule tortue
+ // dispopnible:
+ if (null != cadre.getDrawPanel().tortues[id])
+ {
+ if (compteur > 1)
+ {
+ int tortue_utilisee = kernel.getActiveTurtle().id;
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[id];
+ cadre.getDrawPanel().ct_mt();
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[tortue_utilisee];
+ cadre.getDrawPanel().tortues[id] = null;
+ if (cadre.getDrawPanel().tortues_visibles.search(String.valueOf(id)) > 0)
+ cadre.getDrawPanel().tortues_visibles.remove(String.valueOf(id));
+ if (kernel.getActiveTurtle().id == id)
+ {
+ cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[premier_dispo];
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon); // on
+ // adapte
+ // le
+ // nouveau
+ // crayon
+ str = cadre.getDrawPanel().getGraphicsFont().getName();
+ cadre.getDrawPanel().setFont(
+ new java.awt.Font(str, java.awt.Font.PLAIN,
+ kernel.getActiveTurtle().police));
+
+ }
+ }
+ else
+ {
+ throw new LogoError(Logo.messages.getString("seule_tortue_dispo"));
+ }
+ }
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ kernel.getCalculator().getInteger(param.get(0));
+ }
+ break;
+ case 130: // sequence
+ liste = getFinalList(param.get(0));
+ cadre.getSon().cree_sequence(Utils.decoupe(liste).toString());
+ break;
+ case 131: // instrument
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(cadre.getSon().getInstrument()));
+ break;
+ case 132: // fixeinstrument
+ i = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getSon().setInstrument(i);
+
+ break;
+ case 133: // joue
+ cadre.getSon().joue();
+ break;
+ case 134: // effacesequence
+ cadre.getSon().efface_sequence();
+ break;
+ case 135: // indexsequence
+ Interprete.operande = true;
+ double d = (double) cadre.getSon().getTicks() / 64;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(d));
+
+ break;
+ case 136: // fixeindexsequence
+ i = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getSon().setTicks(i * 64);
+ break;
+ case 137:// fpt
+ i = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getHistoryPanel().getDsd().fixepolice(i);
+ break;
+ case 138: // ptexte
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(cadre.getHistoryPanel().police()));
+ break;
+ case 139: // fct,fixecouleurtexte
+ if (isList(param.get(0)))
+ {
+ cadre.getHistoryPanel().getDsd().fixecouleur(rgb(param.get(0), Utils.primitiveName("fct")));
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ cadre.getHistoryPanel().getDsd().fixecouleur(DrawPanel.defaultColors[coul]);
+ }
+ break;
+ case 140: // couleurtexte
+ Interprete.operande = true;
+ Color c = cadre.getHistoryPanel().getCouleurtexte();
+ Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] ");
+ break;
+ case 141: // lissouris readmouse
+ while (!cadre.getDrawPanel().get_lissouris())
+ {
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {}
+ //if (LogoError.lance) //TODO Marko: this was always false
+ // break;
+ }
+ Interprete.calcul.push(String.valueOf(cadre.getDrawPanel().get_bouton_souris()));
+ Interprete.operande = true;
+ break;
+ case 142: // possouris
+ Interprete.calcul.push(cadre.getDrawPanel().get_possouris());
+ Interprete.operande = true;
+ break;
+ case 143: // msg message
+ liste = getFinalList(param.get(0));
+ st = new StringTokenizer(liste); // On
+ // découpe
+ // le
+ // message
+ // en
+ // tranche
+ // de
+ // longueurs
+ // acceptables
+ fm = cadre.getFrame().getGraphics().getFontMetrics(font);
+ liste = "";
+ String buf = "";
+ while (st.hasMoreTokens())
+ {
+ buf += st.nextToken() + " ";
+ if (fm.stringWidth(buf) > 200)
+ {
+ liste += buf + "\n";
+ buf = "";
+ }
+ }
+ liste += buf;
+ liste = Utils.SortieTexte(liste);
+
+ MyTextAreaDialog jt = new MyTextAreaDialog(liste, cadre.getHistoryPanel().getDsd());
+ ImageIcon icone = new ImageIcon(Utils.class.getResource("icone.png"));
+ JOptionPane.showMessageDialog(cadre.getFrame(), jt, "", JOptionPane.INFORMATION_MESSAGE,
+ (Icon) icone);
+ break;
+ case 144: // date
+ Interprete.operande = true;
+ Calendar cal = Calendar.getInstance(lang.getLocale());
+ int jour = cal.get(Calendar.DAY_OF_MONTH);
+ int mois = cal.get(Calendar.MONTH) + 1;
+ int annee = cal.get(Calendar.YEAR);
+ Interprete.calcul.push("[ " + jour + " " + mois + " " + annee + " ] ");
+ break;
+ case 145: // heure
+ Interprete.operande = true;
+ cal = Calendar.getInstance(lang.getLocale());
+ int heure = cal.get(Calendar.HOUR_OF_DAY);
+ int minute = cal.get(Calendar.MINUTE);
+ int seconde = cal.get(Calendar.SECOND);
+ Interprete.calcul.push("[ " + heure + " " + minute + " " + seconde + " ] ");
+ break;
+ case 146: // temps
+ Interprete.operande = true;
+ long heure_actuelle = Calendar.getInstance().getTimeInMillis();
+ Interprete.calcul.push(String.valueOf((heure_actuelle - uc.getHeure_demarrage()) / 1000));
+ break;
+ case 147: // debuttemps
+ int temps = kernel.getCalculator().getInteger(param.get(0));
+ Kernel.chrono = Calendar.getInstance().getTimeInMillis() + 1000 * temps;
+ break;
+ case 148: // fintemps?
+ Interprete.operande = true;
+ if (Calendar.getInstance().getTimeInMillis() > Kernel.chrono)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 149: // fnp fixenompolice
+ int int_police = kernel.getCalculator().getInteger(param.get(0));
+ cadre.getDrawPanel().police_etiquette = int_police % UserConfig.fontes.length;
+ break;
+ case 150: // np nompolice
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + cadre.getDrawPanel().police_etiquette + " [ "
+ + UserConfig.fontes[cadre.getDrawPanel().police_etiquette].getFontName() + " ] ] ");
+ break;
+ case 151: // fnpt fixenompolicetexte
+ int_police = kernel.getCalculator().getInteger(param.get(0));
+ HistoryPanel.fontPrint = int_police % UserConfig.fontes.length;
+ cadre.getHistoryPanel().getDsd().fixenompolice(int_police);
+
+ break;
+ case 152: // npt nompolicetexte
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + HistoryPanel.fontPrint + " [ "
+ + UserConfig.fontes[HistoryPanel.fontPrint].getFontName() + " ] ] ");
+ break;
+ case 153: // listeflux
+ liste = "[ ";
+ for (MyFlow flow : kernel.flows)
+ {
+ liste += "[ " + flow.getId() + " " + flow.getPath() + " ] ";
+ }
+ liste += "] ";
+ Interprete.operande = true;
+ Interprete.calcul.push(liste);
+ break;
+ case 154: // lisligneflux
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowReader flowReader;
+ // If the flow is a writable flow, throw error
+ if (flow.isWriter())
+ throw new LogoError(Logo.messages.getString("flux_lecture"));
+ // else if the flow is a readable flow, convert to
+ // MyFlowReader
+ else if (flow.isReader())
+ {
+ flowReader = ((MyFlowReader) flow);
+ }
+ // else the flow isn't yet defined, initialize
+ else
+ flowReader = new MyFlowReader(flow);
+
+ if (flowReader.isFinished())
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+ // Reading line
+ String line = flowReader.readLine();
+ if (null == line)
+ {
+ flow.setFinished(true);
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+ }
+ Interprete.operande = true;
+ Interprete.calcul.push("[ " + Utils.decoupe(line.trim()) + " ] ");
+ kernel.flows.set(index, flowReader);
+ }
+ catch (FileNotFoundException e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+ catch (IOException e2)
+ {}
+ break;
+ case 155: // liscaractereflux
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowReader flowReader;
+ // If the flow is a writable flow, throw error
+ if (flow.isWriter())
+ throw new LogoError(Logo.messages.getString("flux_lecture"));
+ // else if the flow is reader, convert to MyFlowReader
+ else if (flow.isReader())
+ {
+ flowReader = ((MyFlowReader) flow);
+ }
+ // else the flow isn't yet defined, initialize
+ else
+ flowReader = new MyFlowReader(flow);
+
+ if (flowReader.isFinished())
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+
+ int character = ((MyFlowReader) flow).readChar();
+ if (character == -1)
+ {
+ flow.setFinished(true);
+ throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident);
+ }
+ Interprete.operande = true;
+ String car = String.valueOf(character);
+ if (car.equals("\\"))
+ car = "\\\\";
+ Interprete.calcul.push(car);
+ kernel.flows.set(index, flowReader);
+ }
+ catch (FileNotFoundException e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+ catch (IOException e2)
+ {}
+ break;
+ case 156: // ecrisligneflux
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ liste = getFinalList(param.get(1));
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowWriter flowWriter;
+ // If the flow is a readable flow, throw an error
+ if (flow.isReader())
+ throw new LogoError(Logo.messages.getString("flux_ecriture"));
+ // Else if the flow is a writable flow , convert to
+ // MrFlowWriter
+ else if (flow.isWriter())
+ flowWriter = (MyFlowWriter) flow;
+ // Else the flow isn't defined yet, initialize
+ else
+ flowWriter = new MyFlowWriter(flow);
+
+ // System.out.println(flow.isReader()+" "+flow.isWriter());
+ // Write the line
+ flowWriter.write(Utils.SortieTexte(liste));
+ kernel.flows.set(index, flowWriter);
+ }
+ catch (FileNotFoundException e1)
+ {}
+ catch (IOException e2)
+ {}
+ break;
+ case 157: // finficher?
+ try
+ {
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ else
+ {
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowReader flowReader = null;
+ // If the flow isn't defined yet, initialize
+ if (!flow.isWriter() && !flow.isReader())
+ {
+ flowReader = new MyFlowReader(flow);
+ }
+ else if (flow.isReader())
+ flowReader = (MyFlowReader) flow;
+ if (null != flowReader)
+ {
+ if (flow.isFinished())
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ }
+ else
+ {
+ int read = flowReader.isReadable();
+ if (read == -1)
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ flow.setFinished(true);
+ }
+ else
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ }
+ }
+ else
+ throw new LogoError(Logo.messages.getString("flux_lecture"));
+ }
+ }
+ catch (FileNotFoundException e1)
+ {}
+ catch (IOException e2)
+ {}
+ break;
+ case 158: // ouvreflux
+ mot = getWord(param.get(1));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ defaultFolder = uc.getDefaultFolder();
+ liste = Utils.SortieTexte(defaultFolder) + File.separator + Utils.SortieTexte(mot);
+ int ident = kernel.getCalculator().getInteger(param.get(0));
+ if (kernel.flows.search(ident) == -1)
+ kernel.flows.add(new MyFlow(ident, liste, false));
+ else
+ throw new LogoError(ident + " " + Logo.messages.getString("flux_existant"));
+ break;
+ case 159: // fermeflux
+ try
+ {
+ ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ // If the flow is a readable flow
+ if (flow.isReader())
+ ((MyFlowReader) flow).close();
+ // Else if it's a writable flow
+ else if (flow.isWriter())
+ ((MyFlowWriter) flow).close();
+ kernel.flows.remove(index);
+ }
+ catch (IOException e2)
+ {}
+ break;
+ case 160: // ajouteligneflux
+ try
+ {
+ ident = kernel.getCalculator().getInteger(param.get(0));
+ int index = kernel.flows.search(ident);
+ liste = getFinalList(param.get(1));
+ if (index == -1)
+ throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident);
+ MyFlow flow = kernel.flows.get(index);
+ MyFlowWriter flowWriter;
+ // If the flow is a readable flow, throw an error
+ if (flow.isReader())
+ throw new LogoError(Logo.messages.getString("flux_ecriture"));
+ // Else if the flow is a writable flow , convert to
+ // MrFlowWriter
+ else if (flow.isWriter())
+ flowWriter = (MyFlowWriter) flow;
+ // Else the flow isn't defined yet, initialize
+ else
+ flowWriter = new MyFlowWriter(flow);
+
+ // Write the line
+ flowWriter.append(Utils.SortieTexte(liste));
+ kernel.flows.set(index, flowWriter);
+ }
+ catch (FileNotFoundException e1)
+ {}
+ catch (IOException e2)
+ {}
+ break;
+ case 161: // souris?
+ Interprete.operande = true;
+ if (cadre.getDrawPanel().get_lissouris())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 162: // variables
+ Interprete.operande = true;
+ Interprete.calcul.push(new String(getAllVariables()));
+ break;
+ case 163: // chose thing
+ mot = getWord(param.get(0));
+ if (null == mot)
+ {
+ throw new LogoError(Logo.messages.getString("error.word"));
+ } // si c'est une liste
+ else if (debut_chaine.equals("")) { throw new LogoError(
+ Logo.messages.getString("erreur_variable")); } // si
+ // c'est
+ // un
+ // nombre
+ Interprete.operande = true;
+ String value;
+ mot = mot.toLowerCase();
+ if (!Interprete.locale.containsKey(mot))
+ {
+ if (!wp.getGlobals().getVariables().contains(mot))
+ throw new LogoError(mot + " " + Logo.messages.getString("erreur_variable"));
+ else
+ value = wp.getGlobals().getValue(mot);
+ }
+ else
+ {
+ value = Interprete.locale.get(mot);
+ }
+ if (null == value)
+ throw new LogoError(mot + " " + Logo.messages.getString("erreur_variable"));
+ Interprete.calcul.push(value);
+ break;
+ case 164: // nettoie
+ cadre.getDrawPanel().nettoie();
+ break;
+ case 165: // tape
+ par = param.get(0).trim();
+ if (isList(par))
+ par = formatList(par.substring(1, par.length() - 1));
+ mot = getWord(param.get(0));
+ if (null == mot)
+ HistoryMessenger.getInstance().dispatchLogoOutput(Utils.SortieTexte(par));
+ else
+ HistoryMessenger.getInstance().dispatchLogoOutput(Utils.SortieTexte(mot));
+ break;
+ case 166: // cercle
+ cadre.getDrawPanel().circle((kernel.getCalculator().numberDouble(param.pop())));
+ break;
+ case 167: // arc
+ cadre.getDrawPanel().arc(kernel.getCalculator().numberDouble(param.get(0)),
+ kernel.getCalculator().numberDouble(param.get(1)),
+ kernel.getCalculator().numberDouble(param.get(2)));
+ break;
+ case 168: // rempliszone
+ cadre.getDrawPanel().rempliszone();
+ break;
+ case 169: // animation
+ cadre.getDrawPanel().setAnimation(true);
+ Interprete.operande = false;
+ break;
+ case 170: // rafraichis
+ if (DrawPanel.classicMode == DrawPanel.MODE_ANIMATION)
+ {
+ cadre.getDrawPanel().refresh();
+ }
+ break;
+
+ case 171: // tailledessin
+ Interprete.operande = true;
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ sb.append(w);
+ sb.append(" ");
+ sb.append(h);
+ sb.append(" ] ");
+ Interprete.calcul.push(new String(sb));
+ break;
+ case 172: // quotient
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().quotient(param.get(0), param.get(1)));
+ break;
+ case 173: // entier?
+ Interprete.operande = true;
+ double ent = kernel.getCalculator().numberDouble(param.get(0));
+ if ((int) ent == ent)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 174: // fixeseparation
+ nombre = kernel.getCalculator().numberDouble(param.get(0));
+ if (nombre < 0 || nombre > 1)
+ throw new LogoError(nombre + " " + Logo.messages.getString("entre_zero_un"));
+ cadre.drawingAndHistory.setResizeWeight(nombre);
+ cadre.drawingAndHistory.setDividerLocation(nombre);
+ break;
+ case 175: // separation
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(cadre.drawingAndHistory.getResizeWeight()));
+ break;
+ case 176: // tronque
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().truncate(param.get(0)));
+ break;
+ case 177: // trace
+ Kernel.mode_trace = true;
+ Interprete.operande = false;
+ break;
+ case 178:// changedossier
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ chemin = "";
+ defaultFolder = uc.getDefaultFolder();
+ if (defaultFolder.endsWith(File.separator))
+ chemin = Utils.SortieTexte(defaultFolder + mot);
+ else
+ chemin = Utils.SortieTexte(defaultFolder + Utils.rajoute_backslash(File.separator) + mot);
+ if ((new File(chemin)).isDirectory())
+ {
+ try
+ {
+ defaultFolder = Utils.rajoute_backslash((new File(chemin)).getCanonicalPath());
+ uc.setDefaultFolder(defaultFolder);
+ }
+ catch (NullPointerException e1)
+ {}
+ catch (IOException e2)
+ {}
+ }
+ else
+ throw new LogoError(Utils.rajoute_backslash(chemin) + " "
+ + Logo.messages.getString("erreur_pas_repertoire"));
+
+ break;
+ case 179:// unicode
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ else if (getWordLength(mot) != 1)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("un_caractere"));
+ else
+ {
+ Interprete.operande = true;
+ str = String.valueOf((int) Utils.SortieTexte(itemWord(1, mot)).charAt(0));
+ Interprete.calcul.push(str);
+ }
+ break;
+ case 180:// caractere
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0 || i > 65535)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("nombre_unicode"));
+ else
+ {
+ str = "";
+ Interprete.operande = true;
+ if (i == 92)
+ str = "\"\\\\";
+ else if (i == 10)
+ str = "\"\\n";
+ else if (i == 32)
+ str = "\"\\e";
+ else
+ {
+ str = String.valueOf((char) i);
+ try
+ {
+ Double.parseDouble(str);
+ }
+ catch (NumberFormatException e)
+ {
+ str = "\"" + str;
+ }
+ }
+ Interprete.calcul.push(str);
+ }
+ break;
+ case 181: // stoptout
+ cadre.error = true;
+ break;
+ case 182: // compteur
+ boolean erreur = false;
+ if (!Primitive.stackLoop.isEmpty())
+ {
+ LoopProperties lp = Primitive.stackLoop.peek();
+ if (lp.isRepeat())
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(lp.getCounter().toString());
+ }
+ else
+ erreur = true;
+ }
+ else
+ erreur = true;
+ if (erreur) { throw new LogoError(Logo.messages.getString("erreur_compteur")); }
+ break;
+ case 183: // controls.for repetepour
+ li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ li1 = getFinalList(param.get(0));
+ int nb = numberOfElements(li1);
+ if (nb < 3 || nb > 4)
+ throw new LogoError(Logo.messages.getString("erreur_repetepour"));
+ st = new StringTokenizer(li1);
+ String var = st.nextToken().toLowerCase();
+ BigDecimal deb = kernel.getCalculator().numberDecimal(st.nextToken());
+ BigDecimal fin = kernel.getCalculator().numberDecimal(st.nextToken());
+ BigDecimal increment = BigDecimal.ONE;
+ if (nb == 4)
+ increment = kernel.getCalculator().numberDecimal(st.nextToken());
+ if (var.equals(""))
+ throw new LogoError(Logo.messages.getString("variable_vide"));
+ try
+ {
+ Double.parseDouble(var);
+ throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable"));
+ }
+ catch (NumberFormatException e)
+ {
+ LoopFor lf = new LoopFor(deb, fin, increment, li2, var);
+ lf.AffecteVar(true);
+
+ if ((increment.compareTo(BigDecimal.ZERO) == 1 && fin.compareTo(deb) >= 0)
+ || (increment.compareTo(BigDecimal.ZERO) == -1 && fin.compareTo(deb) <= 0))
+ {
+ cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " ");
+ Primitive.stackLoop.push(lf);
+ }
+ }
+ break;
+ case 184: // absolue
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().abs(param.get(0)));
+ break;
+ case 185: // remplace
+ String reponse = "";
+ liste = getFinalList(param.get(0));
+ int entier = kernel.getCalculator().getInteger(param.get(1));
+ mot = getWord(param.get(2));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = "[ " + getFinalList(param.get(2)) + "]";
+ char ch;
+ int compteur = 1;
+ boolean espace = true;
+ boolean crochet = false;
+ boolean error = true;
+ for (int j = 0; j < liste.length(); j++)
+ {
+ if (compteur == entier)
+ {
+ error = false;
+ compteur = j;
+ break;
+ }
+ ch = liste.charAt(j);
+ if (ch == '[')
+ {
+ if (espace)
+ crochet = true;
+ espace = false;
+ }
+ if (ch == ' ')
+ {
+ espace = true;
+ if (crochet)
+ {
+ crochet = false;
+ j = extractList(liste, j);
+ }
+ compteur++;
+ }
+ }
+ if (error)
+ throw new LogoError(Logo.messages.getString("y_a_pas") + " " + entier + " "
+ + Logo.messages.getString("element_dans_liste") + liste + "]");
+ reponse = "[ " + liste.substring(0, compteur) + mot;
+ // On extrait le mot suivant
+ if (compteur + 1 < liste.length() && liste.charAt(compteur) == '['
+ && liste.charAt(compteur + 1) == ' ')
+ {
+ compteur = extractList(liste, compteur + 2);
+ reponse += liste.substring(compteur) + "] ";
+
+ }
+ else
+ {
+ for (i = compteur + 1; i < liste.length(); i++)
+ {
+ if (liste.charAt(i) == ' ')
+ {
+ compteur = i;
+ break;
+ }
+ }
+ reponse += liste.substring(compteur) + "] ";
+ }
+ Interprete.operande = true;
+ Interprete.calcul.push(reponse);
+ break;
+ case 186: // ajoute
+ reponse = "";
+ liste = getFinalList(param.get(0));
+ entier = kernel.getCalculator().getInteger(param.get(1));
+ mot = getWord(param.get(2));
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = "[ " + getFinalList(param.get(2)) + "]";
+ compteur = 1;
+ espace = true;
+ crochet = false;
+ error = true;
+ for (int j = 0; j < liste.length(); j++)
+ {
+ if (compteur == entier)
+ {
+ error = false;
+ compteur = j;
+ break;
+ }
+ ch = liste.charAt(j);
+ if (ch == '[')
+ {
+ if (espace)
+ crochet = true;
+ espace = false;
+ }
+ if (ch == ' ')
+ {
+ espace = true;
+ if (crochet)
+ {
+ crochet = false;
+ j = extractList(liste, j);
+ }
+ compteur++;
+ if (j == liste.length() - 1 && compteur == entier)
+ {
+ error = false;
+ compteur = liste.length();
+ }
+ }
+ }
+ if (error && entier != compteur)
+ throw new LogoError(Logo.messages.getString("y_a_pas") + " " + entier + " "
+ + Logo.messages.getString("element_dans_liste") + liste + "]");
+ if (!liste.trim().equals(""))
+ reponse = "[ " + liste.substring(0, compteur) + mot + " " + liste.substring(compteur) + "] ";
+ else
+ reponse = "[ " + mot + " ] ";
+ Interprete.operande = true;
+ Interprete.calcul.push(reponse);
+ break;
+ case 187: // gris
+ colorCode(8);
+ break;
+ case 188: // grisclair
+ colorCode(9);
+ break;
+ case 189: // rougefonce
+ colorCode(10);
+ break;
+ case 190: // vertfonce
+ colorCode(11);
+ break;
+ case 191: // bleufonce
+ colorCode(12);
+ break;
+ case 192: // orange
+ colorCode(13);
+ break;
+ case 193: // rose
+ colorCode(14);
+ break;
+ case 194: // violet
+ colorCode(15);
+ break;
+ case 195: // marron
+ colorCode(16);
+ break;
+ case 196: // noir
+ colorCode(0);
+ break;
+ case 197: // rouge
+ colorCode(1);
+ break;
+ case 198: // vert
+ colorCode(2);
+ break;
+ case 199: // jaune
+ colorCode(3);
+ break;
+ case 200: // bleu
+ colorCode(4);
+ break;
+ case 201: // magenta
+ colorCode(5);
+ break;
+ case 202: // cyan
+ colorCode(6);
+ break;
+ case 203: // blanc
+ colorCode(7);
+ break;
+ case 204: // Parenthese fermante
+ // Distinguons les deux cas : (3)*2 et (4+3)*2
+ // Le 3 est ici a retourner au +
+ boolean a_retourner = true;
+ // On enleve le "(" correspondant a la parenthese ouvrante
+ // de la
+ // pile nom
+ // a condition que l'element attendant de la pile nom ne
+ // soit
+ // pas une procedure
+ boolean est_procedure = false;
+ int pos = Interprete.nom.lastIndexOf("(");
+ if (pos == -1)
+ {
+ // Parenthese fermante sans parenthese ouvrante au
+ // prealable
+ throw new LogoError(Logo.messages.getString("parenthese_ouvrante"));
+ }
+ else
+ { // Evitons l'erreur en cas de par exemple: "ec )"
+ // (parenthese fermante sans ouvrante)--> else a
+ // executer qu'en cas de non erreur
+ if (Interprete.drapeau_ouvrante)
+ {
+ // parenthese vide
+ throw new LogoError(Logo.messages.getString("parenthese_vide"));
+
+ }
+ for (int j = pos; j < Interprete.nom.size(); j++)
+ {
+ String proc = Interprete.nom.get(j).toLowerCase();
+ if (Primitive.primitives.containsKey(proc))
+ est_procedure = true;
+ else
+ {
+ if (wp.isExecutable(proc)) // TODO changed
+ {
+ est_procedure = true;
+ break;
+ }
+ }
+ }
+ }
+ // Si une procedure est presente dans la pile nom, on garde
+ // les parenteses
+ // System.out.println(Primitive.primitives.containsKey("puissance")+"
+ // "+est_procedure);
+ if (est_procedure)
+ {
+ cadre.getKernel().getInstructionBuffer().insert(") ");
+ }
+ // Sinon on les enleve avec leurs imbrications eventuelles
+ else
+ {
+ if (Interprete.en_cours.isEmpty() || !Interprete.en_cours.peek().equals("("))
+ {
+ throw new LogoError(Logo.messages.getString("parenthese_ouvrante"));
+ }
+ else
+ Interprete.en_cours.pop();
+ if (!Interprete.nom.isEmpty())
+ {
+ if (Interprete.nom.peek().equals("("))
+ a_retourner = false;
+ pos = Interprete.nom.lastIndexOf("(");
+ if (pos == -1)
+ {
+ // Parenthese fermante sans parenthese ouvrante
+ // au prelable
+ throw new LogoError(Logo.messages.getString("parenthese_ouvrante"));
+ }
+ else
+ {
+ Interprete.nom.removeElementAt(pos);
+ // S'il y a imbrication de parentheses (((20)))
+ pos--;
+ instruction = cadre.getKernel().getInstructionBuffer();
+ while (instruction.getNextWord().equals(")") && (pos > -1))
+ {
+ if (!Interprete.nom.isEmpty() && Interprete.nom.get(pos).equals("("))
+ {
+ instruction.deleteFirstWord(")");
+ Interprete.nom.removeElementAt(pos);
+ pos--;
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ if (Interprete.calcul.isEmpty())
+ {
+ Interprete.operande = false;
+ }
+ else
+ {
+ Interprete.operande = true;
+ Interprete.drapeau_fermante = a_retourner;
+ }
+ break;
+ case 205: // fixestyle
+ boolean gras = false;
+ boolean italique = false;
+ boolean souligne = false;
+ boolean exposant = false;
+ boolean indice = false;
+ boolean barre = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ liste = getFinalList(param.get(0));
+ else
+ liste = mot;
+ if (liste.trim().equals(""))
+ liste = Logo.messages.getString("style.none");
+ st = new StringTokenizer(liste);
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken().toLowerCase();
+ if (element.equals(Logo.messages.getString("style.underline").toLowerCase()))
+ {
+ souligne = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.bold").toLowerCase()))
+ {
+ gras = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.italic").toLowerCase()))
+ {
+ italique = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.exposant").toLowerCase()))
+ {
+ exposant = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.subscript").toLowerCase()))
+ {
+ indice = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.strike").toLowerCase()))
+ {
+ barre = true;
+ }
+ else if (element.equals(Logo.messages.getString("style.none").toLowerCase()))
+ {}
+ else
+ throw new LogoError(Logo.messages.getString("erreur_fixestyle"));
+ }
+ cadre.getHistoryPanel().getDsd().fixegras(gras);
+ cadre.getHistoryPanel().getDsd().fixeitalique(italique);
+ cadre.getHistoryPanel().getDsd().fixesouligne(souligne);
+ cadre.getHistoryPanel().getDsd().fixeexposant(exposant);
+ cadre.getHistoryPanel().getDsd().fixeindice(indice);
+ cadre.getHistoryPanel().getDsd().fixebarre(barre);
+ break;
+ case 206: // style
+ StringBuffer buffer = new StringBuffer();
+ compteur = 0;
+ if (cadre.getHistoryPanel().getDsd().estgras())
+ {
+ buffer.append(Logo.messages.getString("style.bold").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estitalique())
+ {
+ buffer.append(Logo.messages.getString("style.italic").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estsouligne())
+ {
+ buffer.append(Logo.messages.getString("style.underline").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estexposant())
+ {
+ buffer.append(Logo.messages.getString("style.exposant").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estindice())
+ {
+ buffer.append(Logo.messages.getString("style.subscript").toLowerCase() + " ");
+ compteur++;
+ }
+ if (cadre.getHistoryPanel().getDsd().estbarre())
+ {
+ buffer.append(Logo.messages.getString("style.strike").toLowerCase() + " ");
+ compteur++;
+ }
+ Interprete.operande = true;
+ if (compteur == 0)
+ Interprete.calcul.push("\"" + Logo.messages.getString("style.none").toLowerCase());
+ else if (compteur == 1)
+ Interprete.calcul.push("\"" + new String(buffer).trim());
+ else if (compteur > 1)
+ Interprete.calcul.push("[ " + new String(buffer) + "]");
+ break;
+ case 207: // listaillefenetre
+ Interprete.operande = true;
+ java.awt.Point p = cadre.scrollArea.getViewport().getViewPosition();
+ Rectangle rec = cadre.scrollArea.getVisibleRect();
+ sb = new StringBuffer();
+ int x1 = p.x - w / 2;
+ int y1 = h / 2 - p.y;
+ int x2 = x1 + rec.width - cadre.scrollArea.getVerticalScrollBar().getWidth();
+ int y2 = y1 - rec.height + cadre.scrollArea.getHorizontalScrollBar().getHeight();
+ sb.append("[ ");
+ sb.append(x1);
+ sb.append(" ");
+ sb.append(y1);
+ sb.append(" ");
+ sb.append(x2);
+ sb.append(" ");
+ sb.append(y2);
+ sb.append(" ] ");
+ Interprete.calcul.push(new String(sb));
+ break;
+ case 208: // LongueurEtiquette
+ mot = getWord(param.get(0));
+ if (null != mot)
+ mot = Utils.SortieTexte(mot);
+ else
+ mot = getFinalList(param.get(0)).trim();
+ Interprete.operande = true;
+ fm = cadre.getDrawPanel().getGraphics().getFontMetrics(cadre.getDrawPanel().getGraphicsFont());
+ longueur = fm.stringWidth(mot);
+ Interprete.calcul.push(String.valueOf(longueur));
+ break;
+ case 209: // envoietcp // sendtcp // enviatcp etcp
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); }
+ mot = mot.toLowerCase();
+ liste = "";
+ liste = getFinalList(param.get(1));
+ NetworkClientSend ncs = new NetworkClientSend(mot, liste);
+ Interprete.calcul.push("[ " + ncs.getAnswer() + " ] ");
+ /*
+ * {
+ * liste = "[ "; mot2 =
+ * getFinalList(param.get(0).toString()); liste += mot2 + "
+ * ]"; String rip = liste.substring(2,17); // cadre.ecris("perso
+ * ", rip + "\n");
+ * //para debug String rdat = "_" + liste.substring(18,23) +
+ * "*\n\r"; //
+ * cadre.ecris("perso", rdat + "\n"); //para debug Socket
+ * echoSocket = null;
+ * DataOutputStream tcpout = null; BufferedReader tcpin =
+ * null; String resp =
+ * null; { echoSocket = new Socket(rip, 1948); tcpout =
+ * new
+ * DataOutputStream(echoSocket.getOutputStream()); tcpin=
+ * new BufferedReader(new
+ * InputStreamReader(echoSocket.getInputStream()));
+ * tcpout.writeBytes(rdat);
+ * resp = tcpin.readLine(); // readLine detiene el programa
+ * hasta que recibe una
+ * respuesta del robot. Que hacer si no recibe nada?
+ * tcpout.close();
+ * tcpin.close(); echoSocket.close(); } catch
+ * (UnknownHostException e) { throw
+ * new myException( Logo.messages.getString("erreur_tcp"));
+ * } catch
+ * (IOException e) { throw new myException(
+ * Logo.messages.getString("erreur_tcp")); }
+ * Interprete.calcul.push("[ " + resp + "
+ * ]"); } catch(myException e){}
+ */
+ break;
+ case 210: // ecoutetcp
+ /**
+ * Marko Zivkovic : new implementation of context switch.
+ * saving of workspace (now context) is done in {@link ContextManager}
+ */
+ Interprete.operande = false;
+ new NetworkServer(cadre);
+
+ break;
+ case 211: // executetcp
+ mot = getWord(param.get(0));
+ if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); }
+ mot = mot.toLowerCase();
+ liste = "";
+ liste = getFinalList(param.get(1));
+ new NetworkClientExecute(cadre.getKernel(), mot, liste);
+ break;
+ case 212: // \x internal operator to specify
+ // the end of network instructions with
+ // "executetcp"
+ // have to replace workspace
+ Interprete.operande = false;
+
+ /*
+ * Marko Zivkovic : new implementation of network context
+ */
+ wp.popNetworkMode();
+
+ break;
+ case 213: // chattcp
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); }
+ mot = mot.toLowerCase();
+ liste = "";
+ liste = getFinalList(param.get(1));
+ new NetworkClientChat(cadre, mot, liste);
+ break;
+ case 214: // init resetall
+ Interprete.operande = false;
+ // resize drawing zone if necessary
+ if (h != 1000 || w != 1000)
+ {
+ h = 1000;
+ w = 1000;
+ cadre.resizeDrawingZone();
+ }
+ uc.setDrawGrid(false);
+ uc.setDrawXAxis(false);
+ uc.setDrawYAxis(false);
+ cadre.getDrawPanel().origine();
+ kernel.getActiveTurtle().stroke = new BasicStroke(1);
+ cadre.getDrawPanel().setBackgroundColor(Color.WHITE);
+ if (kernel.getActiveTurtle().id == 0)
+ {
+ uc.setActiveTurtle(0);
+ }
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ chemin = "tortue0.png";
+ kernel.change_image_tortue(chemin);
+ cadre.getDrawPanel().fcfg(Color.WHITE);
+ cadre.getDrawPanel().fcc(Color.BLACK);
+ cadre.getDrawPanel().setAnimation(false);
+ font = new Font("dialog", Font.PLAIN, 12);
+ WSManager.getWorkspaceConfig().setFont(font);
+ kernel.getActiveTurtle().police = 12;
+ cadre.getDrawPanel().setGraphicsFont(font);
+ HistoryPanel.fontPrint = UserConfig.police_id(font);
+ cadre.getHistoryPanel().getDsd().fixepolice(12);
+ cadre.getHistoryPanel().getDsd().fixenompolice(HistoryPanel.fontPrint);
+ cadre.getHistoryPanel().getDsd().fixecouleur(Color.black);
+ uc.setPenShape(PenShape.SQUARE);
+ uc.setQuality(DrawQuality.NORMAL);
+ kernel.setDrawingQuality(uc.getQuality());
+ kernel.setNumberOfTurtles(16);
+ uc.setTurtleSpeed(0);
+ Kernel.mode_trace = false;
+ DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC;
+ cadre.getDrawPanel().zoom(1, false);
+ break;
+ case 215: // tc taillecrayon
+ Interprete.operande = true;
+ double penwidth = 2 * kernel.getActiveTurtle().getPenWidth();
+ Interprete.calcul.push(String.valueOf(MyCalculator.teste_fin_double(penwidth)));
+ break;
+ case 216: // setpenshape=ffc fixeformecrayon
+ Interprete.operande = false;
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i != PenShape.OVAL.getValue() && i != PenShape.SQUARE.getValue())
+ {
+ str = Utils.primitiveName("setpenshape") + " " + Logo.messages.getString("error_bad_values");
+ str += " " + PenShape.SQUARE.getValue() + " " + PenShape.OVAL.getValue();
+ throw new LogoError(str);
+ }
+ uc.setPenShape(PenShape.getPenShape(i));
+ cadre.getDrawPanel().updateAllTurtleShape();
+ cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon);
+ break;
+ case 217: // penshape=fc formecrayon
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(uc.getPenShape().getValue()));
+ break;
+ case 218: // setdrawingquality=fqd fixequalitedessin
+ Interprete.operande = false;
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i != DrawQuality.NORMAL.getValue() && i != DrawQuality.HIGH.getValue()
+ && i != DrawQuality.LOW.getValue())
+ {
+ str = Utils.primitiveName("setdrawingquality") + " "
+ + Logo.messages.getString("error_bad_values") + " 0 1 2";
+ throw new LogoError(str);
+ }
+ uc.setQuality(DrawQuality.getDrawQuality(i));
+ kernel.setDrawingQuality(uc.getQuality());
+ break;
+ case 219: // drawingquality=qd qualitedessin
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(uc.getQuality().getValue()));
+ break;
+ case 220: // setturtlesnumber=fmt fixemaxtortues
+ Interprete.operande = false;
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String fmt = Utils.primitiveName("setturtlesnumber");
+ throw new LogoError(fmt + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i == 0)
+ i = 1;
+ kernel.setNumberOfTurtles(i);
+ break;
+ case 221: // turtlesnumber=maxtortues
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(uc.getMaxTurtles()));
+
+ break;
+ case 222: // setscreensize=ftd fixetailledessin
+ Interprete.operande = false;
+
+ String prim = Utils.primitiveName("setscreensize");
+ liste = getFinalList(param.get(0));
+ int width,
+ height;
+ st = new StringTokenizer(liste);
+ try
+ {
+ if (!st.hasMoreTokens())
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ width = Integer.parseInt(st.nextToken().toString());
+ if (!st.hasMoreTokens())
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ height = Integer.parseInt(st.nextToken().toString());
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ }
+ if (st.hasMoreTokens())
+ throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste
+ + Logo.messages.getString("comme_parametre"));
+ boolean changement = false;
+ if (height != h)
+ changement = true;
+ int tmp_hauteur = h;
+ h = height;
+ if (width != w)
+ changement = true;
+ int tmp_largeur = w;
+ w = width;
+ if (w < 100 || h < 100)
+ {
+ w = 1000;
+ h = 1000;
+ }
+ if (changement)
+ {
+ int memoire_necessaire = w * h * 4 / 1024 / 1024;
+ int memoire_image = tmp_hauteur * tmp_largeur * 4 / 1024 / 1024;
+ long free = Runtime.getRuntime().freeMemory() / 1024 / 1024;
+ long total = Runtime.getRuntime().totalMemory() / 1024 / 1024;
+ /*
+ * System.out.println("memoire nécessaire
+ * "+memoire_necessaire); System.out.println("memoire
+ * image
+ * "+memoire_image); System.out.println("memoire
+ * libre "+free); System.out.println("memoire totale
+ * "+total); System.out.println("memoire envisagee
+ * "+(total-free+memoire_necessaire-memoire_image));
+ * System.out.println();
+ */
+ if (total - free + memoire_necessaire - memoire_image < GlobalConfig.getMaximumMemory() * 0.8)
+ {
+ cadre.resizeDrawingZone();
+ }
+ else
+ {
+ w = tmp_largeur;
+ h = tmp_hauteur;
+ long conseil = 64 * ((total - free + memoire_necessaire - memoire_image) / 64) + 64;
+ if (total - free + memoire_necessaire - memoire_image > 0.8 * conseil)
+ conseil += 64;
+ if (conseil == GlobalConfig.getMaximumMemory())
+ conseil += 64;
+ String message = Logo.messages.getString("erreur_memoire") + " " + conseil + "\n"
+ + Logo.messages.getString("relancer");
+ jt = new MyTextAreaDialog(message);
+ JOptionPane.showMessageDialog(cadre.getFrame(), jt, Logo.messages.getString("erreur"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ break;
+ case 223: // guibutton guibouton
+ String identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot = getWord(param.get(1));
+ if (null == mot)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ GuiButton gb = new GuiButton(identifier.toLowerCase(), mot, cadre);
+ cadre.getDrawPanel().addToGuiMap(gb);
+ break;
+ case 224: // guiaction
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ cadre.getDrawPanel().guiAction(identifier, liste);
+ break;
+ case 225: // guiremove
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ cadre.getDrawPanel().guiRemove(identifier);
+ break;
+ case 226: // guiposition
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ cadre.getDrawPanel().guiposition(identifier, liste, Utils.primitiveName("guiposition"));
+ break;
+ case 227: // guidraw
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ cadre.getDrawPanel().guiDraw(identifier);
+ break;
+ case 228: // zoom
+ Interprete.operande = false;
+ d = kernel.getCalculator().numberDouble(param.get(0));
+ if (d <= 0)
+ {
+ String name = Utils.primitiveName("zoom");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ cadre.getDrawPanel().zoom(d, false);
+ break;
+ case 229: // grille
+ Interprete.operande = false;
+ primitive2D("grille");
+ int[] args = new int[2];
+ for (i = 0; i < 2; i++)
+ {
+ args[i] = kernel.getCalculator().getInteger(param.get(i));
+ if (args[i] < 0)
+ {
+ String grille = Utils.primitiveName("grille");
+ throw new LogoError(grille + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (args[i] == 0)
+ {
+ args[i] = 1;
+ }
+ }
+ uc.setDrawGrid(true);
+ uc.setXGrid(args[0]);
+ uc.setYGrid(args[1]);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 230: // stopgrille
+ Interprete.operande = false;
+ uc.setDrawGrid(false);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 231: // stopanimation
+ cadre.getDrawPanel().setAnimation(false);
+ Interprete.operande = false;
+ break;
+ case 232: // stoptrace
+ Kernel.mode_trace = false;
+ Interprete.operande = false;
+ break;
+ case 233: // guimenu
+ identifier = getWord(param.get(0));
+ if (null == identifier)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ liste = getFinalList(param.get(1));
+ GuiMenu gm = new GuiMenu(identifier.toLowerCase(), liste, cadre);
+ cadre.getDrawPanel().addToGuiMap(gm);
+ break;
+ case 234: // axis
+
+ Interprete.operande = false;
+
+ primitive2D("axis");
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String name = Utils.primitiveName("axis");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i < 25)
+ i = 25;
+ uc.setDrawXAxis(true);
+ uc.setXAxis(i);
+ uc.setDrawYAxis(true);
+ uc.setYAxis(i);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 235: // xaxis
+ Interprete.operande = false;
+ primitive2D("xaxis");
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String name = Utils.primitiveName("xaxis");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i < 25)
+ i = 25;
+ uc.setDrawXAxis(true);
+ uc.setXAxis(i);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 236: // yaxis
+ Interprete.operande = false;
+ primitive2D("yaxis");
+ i = kernel.getCalculator().getInteger(param.get(0));
+ if (i < 0)
+ {
+ String name = Utils.primitiveName("yaxis");
+ throw new LogoError(name + " " + Logo.messages.getString("attend_positif"));
+ }
+ else if (i < 25)
+ i = 25;
+ uc.setDrawYAxis(true);
+ uc.setYAxis(i);
+ cadre.getDrawPanel().videecran();
+ break;
+ case 237: // stopaxis
+ uc.setDrawXAxis(false);
+ uc.setDrawYAxis(false);
+ Interprete.operande = false;
+ cadre.getDrawPanel().videecran();
+ break;
+ case 238: // bye
+ cadre.closeWindow();
+ break;
+ case 239: // var? variable?
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot = mot.toLowerCase();
+ if (wp.getGlobals().getVariables().contains(mot) || Interprete.locale.containsKey(mot)) // TODO
+ // changed
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 240: // axiscolor= couleuraxes
+ Interprete.operande = true;
+ c = new Color(uc.getAxisColor());
+ Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] ");
+
+ break;
+ case 241: // gridcolor=couleurgrille
+ Interprete.operande = true;
+ c = new Color(uc.getGridColor());
+ Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] ");
+ break;
+ case 242: // grid?=grille?
+ Interprete.operande = true;
+ if (uc.isDrawGrid())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 243: // xaxis?=axex?
+ Interprete.operande = true;
+ if (uc.isDrawXAxis())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 244: // yaxis?=axey?
+ Interprete.operande = true;
+ if (uc.isDrawYAxis())
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ break;
+ case 245: // setgridcolor=fcg fixecouleurgrille
+ Interprete.operande = false;
+ if (isList(param.get(0)))
+ {
+ uc.setGridColor(rgb(param.get(0), Utils.primitiveName("setgridcolor")).getRGB());
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ uc.setGridColor(DrawPanel.defaultColors[coul].getRGB());
+ }
+ break;
+ case 246: // setaxiscolor=fca fixecouleuraxes
+ Interprete.operande = false;
+ if (isList(param.get(0)))
+ {
+ uc.setAxisColor(rgb(param.get(0), Utils.primitiveName("setaxiscolor")).getRGB());
+ }
+ else
+ {
+ int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length;
+ if (coul < 0)
+ coul += DrawPanel.defaultColors.length;
+ uc.setAxisColor(DrawPanel.defaultColors[coul].getRGB());
+ }
+ break;
+ case 247: // perspective
+
+ cadre.getDrawPanel().perspective();
+
+ break;
+ case 248:// rightroll=rd roulisdroite
+ delay();
+ primitive3D("3d.rightroll");
+ cadre.getDrawPanel().rightroll(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 249:// uppitch=cabre
+ delay();
+ primitive3D("3d.uppitch");
+ cadre.getDrawPanel().uppitch(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 250:// leftroll=rg roulisgauche
+ delay();
+ primitive3D("3d.leftroll");
+ cadre.getDrawPanel().rightroll(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 251:// downpitch=pique
+ delay();
+ primitive3D("3d.downpitch");
+ cadre.getDrawPanel().uppitch(-kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 252:// roll=roulis
+ primitive3D("3d.roll");
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().roll));
+ break;
+ case 253:// pitch=cabrement tangage
+ primitive3D("3d.pitch");
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().pitch));
+ break;
+ case 254:// setroll=fixeroulis
+ primitive3D("3d.setroll");
+ delay();
+ cadre.getDrawPanel().setRoll(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 255:// setpitch=fixetangage
+ primitive3D("3d.setpitch");
+ delay();
+ cadre.getDrawPanel().setPitch(kernel.getCalculator().numberDouble(param.pop()));
+ break;
+ case 256:// setorientation=fixeorientation
+ primitive3D("3d.setorientation");
+ delay();
+ cadre.getDrawPanel().setOrientation(getFinalList(param.pop()));
+ break;
+ case 257: // orientation=orientation
+ primitive3D("3d.orientation");
+ Interprete.operande = true;
+ String pitch = MyCalculator.teste_fin_double(kernel.getActiveTurtle().pitch);
+ String roll = MyCalculator.teste_fin_double(kernel.getActiveTurtle().roll);
+ String heading = MyCalculator.teste_fin_double(kernel.getActiveTurtle().heading);
+ Interprete.calcul.push("[ " + roll + " " + pitch + " " + heading + " ] ");
+ break;
+ case 258: // setxyz=fposxyz
+ primitive3D("3d.setxyz");
+ cadre.getDrawPanel().fpos(
+ kernel.getCalculator().numberDouble(param.get(0)) + " "
+ + kernel.getCalculator().numberDouble(param.get(1)) + " "
+ + kernel.getCalculator().numberDouble(param.get(2)));
+ break;
+ case 259: // setz=fixez
+ delay();
+ primitive3D("3d.setz");
+ cadre.getDrawPanel().fpos(
+ kernel.getActiveTurtle().X + " " + kernel.getActiveTurtle().Y + " "
+ + kernel.getCalculator().numberDouble(param.get(0)));
+ break;
+ case 260: // pprop=dprop
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot2 = getWord(param.get(1));
+ if (null == mot2)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ wp.getPropertyLists().addPropList(mot, mot2, param.get(2)); // TODO
+ // changed
+ break;
+ case 261: // gprop=rprop
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot2 = getWord(param.get(1));
+ if (null == mot2)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ value = wp.getPropertyLists().getPropList(mot, mot2); // TODO
+ // CHANGED
+ if (value.startsWith("["))
+ value += " ";
+ Interprete.calcul.push(value);
+ break;
+ case 262: // remprop=efprop
+ Interprete.operande = false;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot2 = getWord(param.get(1));
+ if (null == mot2)
+ throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word"));
+ wp.getPropertyLists().removePropList(mot, mot2); // TODO
+ // CHANGED
+ break;
+ case 263: // plist=lprop
+ Interprete.operande = true;
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ Interprete.calcul.push(wp.getPropertyLists().displayPropList(mot)); // TODO
+ // CHANGED
+
+ break;
+ case 264: // polystart=polydef
+ DrawPanel.record3D = DrawPanel.record3D_POLYGON;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new
+ // java.util.Vector<Shape3D>();
+ DrawPanel.poly = new ElementPolygon(cadre.getViewer3D());
+ break;
+ case 265: // polyend=polyfin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ DrawPanel.poly.addToScene();
+ break;
+ case 266: // polyview=polyaf vue3d
+ primitive3D("3d.polyview");
+ cadre.viewerOpen();
+ break;
+ case 267: // linestart=lignedef
+ DrawPanel.record3D = DrawPanel.record3D_LINE;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new
+ // java.util.Vector<Shape3D>();
+ DrawPanel.poly = new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ DrawPanel.poly.addVertex(new Point3d(kernel.getActiveTurtle().X / 1000,
+ kernel.getActiveTurtle().Y / 1000, kernel.getActiveTurtle().Z / 1000), kernel
+ .getActiveTurtle().couleurcrayon);
+ break;
+ case 268: // lineend=lignefin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ DrawPanel.poly.addToScene();
+ break;
+ case 269: // pointstart=pointdef
+ DrawPanel.record3D = DrawPanel.record3D_POINT;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new
+ // java.util.Vector<Shape3D>();
+ DrawPanel.poly = new ElementPoint(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth());
+ break;
+ case 270: // pointend=pointfin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ DrawPanel.poly.addToScene();
+ break;
+ case 271: // textstart=textedef
+ DrawPanel.record3D = DrawPanel.record3D_TEXT;
+ cadre.initViewer3D();
+ // if (null==DrawPanel.listText) DrawPanel.listText=new
+ // java.util.Vector<TransformGroup>();
+ DrawPanel.poly = null;
+ break;
+ case 272: // textend=textefin
+ DrawPanel.record3D = DrawPanel.record3D_NONE;
+ break;
+ case 273: // operator <=
+ infequal(param);
+ break;
+ case 274: // operator >=
+ supequal(param);
+ break;
+ case 275: // primitives
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.primitive.getAllPrimitives());
+ break;
+ case 276: // listesproprietes propertylists
+ Interprete.operande = true;
+ Interprete.calcul.push(new String(getAllpropertyLists()));
+ break;
+ case 277: // contenu
+ Interprete.operande = true;
+ sb = new StringBuffer("[ ");
+ sb.append(this.getAllProcedures());
+ sb.append(this.getAllVariables());
+ sb.append(this.getAllpropertyLists());
+ sb.append("] ");
+ Interprete.calcul.push(new String(sb));
+ break;
+ case 278: // erpl=eflp effacelistepropriete
+ Interprete.operande = false;
+ this.erase(param.get(0), "propertylist");
+ break;
+ case 279: // arithmetic.exp
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().exp(param.get(0)));
+ break;
+ case 280: // arithmetic.log
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().log(param.get(0)));
+ break;
+ case 281: // controls.ifelse
+ liste = getList(param.get(1));
+ liste = new String(Utils.decoupe(liste));
+ pred = predicat(param.get(0));
+ liste2 = getList(param.get(2));
+ liste = new String(Utils.decoupe(liste));
+ kernel.primitive.si(pred, liste, liste2);
+ Interprete.renvoi_instruction = true;
+ break;
+ case 282: // workspace.ed
+
+ /*
+ * Marko Zivkovic:
+ * With the new Workspace (UserSpace + LogoFile) ed makes
+ * not so much sense, because we work with
+ * multiple files at the same time.
+ * I therefore restrict this command to allow only a single
+ * word as parameter, to open the file,
+ * where the specified procedure is defined, and scroll to
+ * the procedure's line.
+ */
+
+ mot = this.getWord(param.get(0));
+ if (mot == null)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ if (!wp.isExecutable(mot))
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.proc.does.not.exist"));
+
+ cadre.displayProcedure(mot);
+ break;
+ case 283: // workspace.edall
+ /*
+ * Marko Zivkovic:
+ * Again, it makes no sense to open all files at a time.
+ * Instead, I just open the last edited file.
+ */
+ wp.editAll();
+ break;
+ case 284: // controls.foreach pourchaque
+ // Variable name
+ var = getWord(param.get(0));
+ // If it isn't a word
+ if (null == var)
+ throw new LogoError(param.get(0).toString() + " " + Logo.messages.getString("error.word"));
+ // If it's a number
+ else
+ {
+ try
+ {
+ Double.parseDouble(var);
+ throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable"));
+ }
+ catch (NumberFormatException e1)
+ {}
+ }
+ li2 = getList(param.get(2));
+ li2 = new String(Utils.decoupe(li2));
+ li1 = getWord(param.get(1));
+ boolean isList = false;
+ if (null == li1)
+ {
+ isList = true;
+ li1 = getFinalList(param.get(1));
+ }
+ Vector<String> elements = new Vector<String>();
+ while (!li1.equals(""))
+ {
+ String character = "";
+ // If it's a list
+ if (isList)
+ {
+ character = this.item(li1, 1);
+ // If it's a number
+ try
+ {
+ // Fix Bug: foreach "i [1 2 3][pr :i]
+ // character=1 , 2 , 3 (without quote)
+ Double.parseDouble(character);
+ li1 = li1.substring(character.length() + 1);
+ }
+ catch (NumberFormatException e)
+ {
+ // Fix Bug: foreach "i [r s t][pr :i]
+ // character="r , "s or "t
+ li1 = li1.substring(character.length());
+ }
+ }
+ // If it's a word
+ else
+ {
+ character = this.itemWord(1, li1);
+ li1 = li1.substring(character.length());
+ // If it isn't a number, adding a quote
+ try
+ {
+ Double.parseDouble(character);
+ }
+ catch (NumberFormatException e)
+ {
+ character = "\"" + character;
+ }
+ }
+
+ elements.add(character);
+ }
+ if (elements.size() > 0)
+ {
+ LoopForEach lfe = new LoopForEach(BigDecimal.ZERO, new BigDecimal(elements.size() - 1),
+ BigDecimal.ONE, li2, var.toLowerCase(), elements);
+ lfe.AffecteVar(true);
+ cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " ");
+ Primitive.stackLoop.push(lfe);
+ }
+ break;
+ case 285: // controls.forever repetetoujours
+ li2 = getList(param.get(0));
+ li2 = new String(Utils.decoupe(li2));
+ LoopProperties lp = new LoopProperties(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, li2);
+ cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " ");
+ Primitive.stackLoop.push(lp);
+ break;
+ case 286: // arithmetic.setdigits
+ Interprete.operande = false;
+ kernel.initCalculator(kernel.getCalculator().getInteger(param.get(0)));
+ break;
+ case 287: // arithmetic.digits
+ Interprete.operande = true;
+ Interprete.calcul.push(String.valueOf(kernel.getCalculator().getDigits()));
+ break;
+ case 288: // workspace.text
+ var = getWord(param.get(0));
+ if (null == var)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+
+ Procedure proc = wp.getExecutable(var);
+ if (proc != null)
+ {
+ sb = new StringBuffer();
+ sb.append("[ [ ");
+ // Append variable names
+ for (int j = 0; j < proc.nbparametre; j++)
+ {
+ sb.append(proc.variable.get(j));
+ sb.append(" ");
+ }
+ for (int j = 0; j < proc.optVariables.size(); j++)
+ {
+ sb.append("[ ");
+ sb.append(proc.optVariables.get(j));
+ sb.append(" ");
+ sb.append(proc.optVariablesExp.get(j).toString());
+ sb.append(" ] ");
+ }
+ sb.append("] ");
+ // Append body procedure
+ sb.append(proc.cutInList());
+ sb.append("] ");
+ Interprete.operande = true;
+ Interprete.calcul.push(sb.toString());
+ }
+ else
+ throw new LogoError(var + " " + Logo.messages.getString("error.procedure.must.be"));
+ break;
+ case 289: // workspace.externalcommand
+ Interprete.operande = false;
+
+ {
+ list = getFinalList(param.get(0));
+ int index = numberOfElements(list);
+ String[] cmd = new String[index];
+ for (i = 0; i < index; i++)
+ {
+ String liste1 = item(list, i + 1);
+ cmd[i] = Utils.SortieTexte(getFinalList(liste1).trim());
+ }
+ try
+ {
+ /*
+ * String com="";
+ * for(int i=0;i<cmd.length;i++){
+ * com+=cmd[i]+" ";
+ * }
+ * System.out.println(com);
+ */
+ Runtime.getRuntime().exec(cmd);
+ }
+ catch (IOException e2)
+ {
+ // System.out.println("a");
+ }
+
+ }
+
+ {
+ // System.out.println("coucou");
+ }
+ break;
+ case 290: // drawing.saveimage
+ String word = getWord(param.get(0));
+ if (null == word)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ if (word.equals(""))
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("mot_vide"));
+ // xmin, ymin, width, height
+ int[] coord = new int[4];
+ list = getFinalList(param.get(1));
+ st = new StringTokenizer(list);
+ if (st.countTokens() == 4)
+ {
+ try
+ {
+ int j = 0;
+ while (st.hasMoreTokens())
+ {
+ coord[j] = Integer.parseInt(st.nextToken());
+ j++;
+ }
+ coord[0] += w / 2;
+ coord[2] += w / 2;
+ coord[1] = h / 2 - coord[1];
+ coord[3] = h / 2 - coord[3];
+ if (coord[2] < coord[0])
+ {
+ int tmp = coord[0];
+ coord[0] = coord[2];
+ coord[2] = tmp;
+ }
+ if (coord[3] < coord[1])
+ {
+ int tmp = coord[1];
+ coord[1] = coord[3];
+ coord[3] = tmp;
+ }
+ coord[2] = coord[2] - coord[0];
+ coord[3] = coord[3] - coord[1];
+ }
+ catch (NumberFormatException e)
+ {
+ coord[0] = 0;
+ coord[2] = w;
+ coord[1] = 0;
+ coord[3] = h;
+ }
+ }
+ else
+ {
+ coord[0] = 0;
+ coord[2] = w;
+ coord[1] = 0;
+ coord[3] = h;
+ }
+ if (coord[2] == 0 || coord[3] == 0)
+ {
+ coord[0] = 0;
+ coord[2] = w;
+ coord[1] = 0;
+ coord[3] = h;
+ }
+ cadre.getDrawPanel().saveImage(word, coord);
+ Interprete.operande = false;
+ break;
+ case 291: // sound.mp3play
+ Interprete.operande = false;
+ if (kernel.getMp3Player() != null)
+ kernel.getMp3Player().getPlayer().close();
+ mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(mot + " " + Logo.messages.getString("error.word"));
+ MP3Player player = new MP3Player(cadre, mot);
+ kernel.setMp3Player(player);
+ kernel.getMp3Player().start();
+ break;
+ case 292: // sound.mp3stop
+ Interprete.operande = false;
+ if (null != kernel.getMp3Player())
+ kernel.getMp3Player().getPlayer().close();
+ break;
+ case 293: // zoom
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(DrawPanel.zoom));
+ break;
+ case 294: // drawing.x
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().getX()));
+ break;
+ case 295:// drawing.y
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().getY()));
+ break;
+ case 296: // drawing.z
+ Interprete.operande = true;
+ primitive3D("drawing.z");
+ Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().Z));
+ break;
+ case 297: // drawing.fillpolygon
+ Interprete.operande = false;
+ list = getFinalList(param.get(0));
+ LoopFillPolygon lfp = new LoopFillPolygon();
+ Primitive.stackLoop.push(lfp);
+ cadre.getKernel().getInstructionBuffer().insert(Utils.decoupe(list) + Primitive.END_LOOP + " ");
+ cadre.getDrawPanel().startRecord2DPolygon();
+ break;
+ case 298: // arithmetic.alea
+ Interprete.operande = true;
+ Interprete.calcul.push(MyCalculator.teste_fin_double(Math.random()));
+ break;
+ case 299: // loop.dountil
+ li1 = getList(param.get(0));
+ li1 = new String(Utils.decoupe(li1));
+ li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ instr = "\\siwhile " + Utils.primitiveName("non") + " " + li2 + "[ " + li1 + "] ";
+ LoopWhile lw = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr);
+ Primitive.stackLoop.push(lw);
+ cadre.getKernel().getInstructionBuffer().insert(instr + Primitive.END_LOOP + " ");
+ break;
+ case 300: // loop.dowhile
+ li1 = getList(param.get(0));
+ li1 = new String(Utils.decoupe(li1));
+ li2 = getList(param.get(1));
+ li2 = new String(Utils.decoupe(li2));
+ instr = "\\siwhile " + li2 + "[ " + li1 + "] ";
+ lw = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr);
+ Primitive.stackLoop.push(lw);
+ cadre.getKernel().getInstructionBuffer().insert(li1 + instr + Primitive.END_LOOP + " ");
+ break;
+ case 301: // arithmetic.modulo
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().modulo(param.get(0), param.get(1)));
+ break;
+ case 302: // drawing.setfontjustify
+ Interprete.operande = false;
+ li1 = getFinalList(param.get(0));
+ kernel.getActiveTurtle().setFontJustify(li1);
+ break;
+ case 303: // drawing.fontjustify
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getActiveTurtle().getFontJustify());
+ break;
+ case 304: // arithmetic.inf
+ inf(param);
+ break;
+ case 305: // arithmetic.sup
+ sup(param);
+ break;
+ case 306: // arithmetic.infequal
+ infequal(param);
+ break;
+ case 307: // arithmetic.supequal
+ supequal(param);
+ break;
+
+ }
+ }
+ }
+
+ /**
+ * This implementation of the Logo command "define" is slightly changed towards the original XLogo
+ * implementation.
+ * In {@link xlogo.gui.Editor#analyseprocedure()} (which is now {@link LogoFile#setText(String)}),
+ * I added the line statement {@code wp.deleteAllProcedures()}
+ * because we don't want the procedures to
+ * be ordered by definition time in the editor.
+ * We want the programmer to have the procedures in the editor exactly in
+ * the same order as the programmer writes them.
+ *
+ * @param params
+ * @author Marko Zivkovic
+ * @throws LogoError
+ */
+ private void define(Stack<String> params) throws LogoError
+ {
+ String procedureName;
+ String procedureText = null;
+ procedureName = getWord(params.get(0));
+ if (null == procedureName)
+ throw new LogoError(params.get(0) + " " + Logo.messages.getString("error.word"));
+ if (procedureName.equals(""))
+ throw new LogoError(Logo.messages.getString("procedure_vide"));
+ String list = getFinalList(params.get(1));
+ StringBuffer sb = new StringBuffer();
+ for (int i = 1; i <= numberOfElements(list); i++)
+ {
+ String liste1 = item(list, i);
+ liste1 = getFinalList(liste1);
+
+ // First line
+ if (i == 1)
+ {
+ StringTokenizer st = new StringTokenizer(liste1);
+ sb.append(Logo.messages.getString("pour"));
+ sb.append(" ");
+ sb.append(procedureName);
+ sb.append(" ");
+
+ while (st.hasMoreTokens())
+ {
+ // Optional variables
+ String token = st.nextToken();
+ if (token.equals("["))
+ {
+ sb.append("[ :");
+ while (st.hasMoreTokens())
+ {
+ token = st.nextToken();
+ if (token.equals("]"))
+ {
+ sb.append("] ");
+ break;
+ }
+ else
+ {
+ sb.append(token);
+ sb.append(" ");
+ }
+ }
+ }
+ else
+ {
+ sb.append(":");
+ sb.append(token);
+ sb.append(" ");
+ }
+ }
+ }
+ // Body of the procedure
+ else if (i > 1)
+ {
+ sb.append("\n");
+ sb.append(liste1);
+ }
+ }
+ sb.append("\n");
+ sb.append(Logo.messages.getString("fin"));
+
+ /*
+ * Marko Zivkovic:
+ * In XLogo4Schools, procedures are not created by setting the text
+ * of the editor,
+ * we directly define them in LogoFiles.
+ * Because we have multiple files, the defined procedure will be put
+ * in a special file for programatically defined procedures.
+ * If the procedure name was already defined, then it will be
+ * redefined, as before in XLogo.
+ * If there is an ambiguity with the procedure name, an error will
+ * be displayed "I don't know which one to redefine..."
+ */
+
+ if (wp.isProcedureAmbiguous(procedureName))
+ throw new LogoError(procedureName + " " + Logo.messages.getString("ws.redefine.ambiguous.procedure"));
+ // procedureText is generated above => it is executable
+ procedureText = "\n" + new String(sb);
+ Procedure procedure = new Procedure(procedureText);
+
+ Procedure other = wp.getExecutable(procedureName);
+ String fileName = other != null ? other.getOwnerName() : Logo.messages.getString("ws.generated.procedure");
+ procedure.setOwnerName(fileName);
+
+ try
+ {
+ wp.defineProcedure(procedure);
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("ws.could.not.create.file") + " " + fileName);
+ }
+ }
+
+ /**
+ * This method tests if the primitive name exist in 2D mode
+ *
+ * @param name
+ * The primitive name
+ * @throws LogoError
+ */
+ private void primitive2D(String name) throws LogoError
+ {
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D)
+ throw new LogoError(Utils.primitiveName(name) + " " + Logo.messages.getString("error.primitive2D"));
+ }
+
+ /**
+ * This method tests if the primitive name exist in 2D mode
+ *
+ * @param name
+ * The primitive name
+ * @throws LogoError
+ */
+ private void primitive3D(String name) throws LogoError
+ {
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ throw new LogoError(Utils.primitiveName(name) + " " + Logo.messages.getString("error.primitive3D"));
+ }
+
+ /**
+ * Returns the code [r g b] for the color i
+ *
+ * @param i
+ * Integer representing the Color
+ */
+ private void colorCode(int i)
+ {
+ Interprete.operande = true;
+ Color co = DrawPanel.defaultColors[i];
+ Interprete.calcul.push("[ " + co.getRed() + " " + co.getGreen() + " " + co.getBlue() + " ] ");
+ }
+
+ /**
+ * Save all procedures whose name are contained in the Stack pile
+ *
+ * @param fichier
+ * The patch to the saved file
+ * @param pile
+ * Stack Stack containing all procedure names
+ */
+ private void saveProcedures(String fichier, Stack<String> pile)
+ {
+ try
+ {
+ String aecrire = "";
+ boolean bool = true;
+ if (!fichier.endsWith(".lgo"))
+ fichier += ".lgo";
+ String path = Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()) + File.separator + fichier;
+ try
+ {
+ // TODO CHANGED
+ for (Procedure procedure : wp.getExecutables())
+ {
+ if (null == pile)
+ bool = true;
+ else
+ bool = (pile.search(procedure.name) != -1);
+ if (bool)
+ {
+ aecrire += Logo.messages.getString("pour") + " " + procedure.name;
+ for (int j = 0; j < procedure.nbparametre; j++)
+ {
+ aecrire += " :" + procedure.variable.get(j);
+ }
+ aecrire += "\n" + procedure.instruction + Logo.messages.getString("fin") + "\n\n";
+ }
+ }
+ }
+ catch (NullPointerException ex)
+ {} // Si aucune procédure n'a été définie.
+ Utils.writeLogoFile(path, aecrire);
+ }
+ catch (IOException e2)
+ {
+ HistoryMessenger.getInstance().dispatchError(Logo.messages.getString("error.ioecriture"));
+ }
+ }
+
+ /**
+ * Returns the Image defined by the path "chemin"
+ *
+ * @param chemin
+ * The absolute path for the image
+ * @return BufferedImage defined by the path "chemin"
+ * @throws LogoError
+ * If Image format isn't valid(jpg or png)
+ */
+ private BufferedImage getImage(String path) throws LogoError
+ {
+ BufferedImage image = null;
+ String pathWord = getWord(path);
+ if (null == pathWord)
+ throw new LogoError(path + " " + Logo.messages.getString("error.word"));
+ if (!(pathWord.endsWith(".png") || pathWord.endsWith(".jpg")))
+ throw new LogoError(Logo.messages.getString("erreur_format_image"));
+ else
+ {
+ try
+ {
+ pathWord = Utils.SortieTexte(pathWord);
+ File f = new File(Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()) + File.separator
+ + pathWord);
+ image = ImageIO.read(f);
+ }
+ catch (Exception e1)
+ {
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+ }
+ }
+ return image;
+ }
+
+ /**
+ * Create a local variable called "mot" with no value.
+ *
+ * @param mot
+ * Variable name
+ */
+ private void createLocaleName(String mot)
+ {
+ mot = mot.toLowerCase();
+ if (!Interprete.locale.containsKey(mot))
+ {
+ Interprete.locale.put(mot, null);
+ }
+ }
+
+ /**
+ * Create a new local variable
+ *
+ * @param param
+ * The variable name or a list of variable names
+ * @throws LogoError
+ * If "param" isn't a list containing all variable names, or a
+ * word
+ */
+
+ private void locale(Stack<String> param) throws LogoError
+ {
+ String li = param.get(0);
+ if (LaunchPrimitive.isList(li))
+ {
+ li = getFinalList(li);
+ StringTokenizer st = new StringTokenizer(li);
+ while (st.hasMoreTokens())
+ {
+ String item = st.nextToken();
+ isVariableName(item);
+ createLocaleName(item);
+ }
+ }
+ else
+ {
+ String mot = getWord(param.get(0));
+ if (null != mot)
+ {
+ createLocaleName(mot);
+ }
+ else
+ throw new LogoError(param.get(0) + Logo.messages.getString("error.word"));
+ }
+ }
+
+ /**
+ * returns the color defined by [r g b] contained in "ob"
+ *
+ * @param obj
+ * the list [r g b]
+ * @param name
+ * The name of the calling primitive
+ * @return The Object Color
+ * @throws LogoError
+ * If the list doesn't contain 3 numbers
+ */
+
+ private Color rgb(String obj, String name) throws LogoError
+ {
+ String liste = getFinalList(obj);
+ StringTokenizer st = new StringTokenizer(liste);
+ if (st.countTokens() != 3)
+ throw new LogoError(name + " " + Logo.messages.getString("color_3_arguments"));
+ int[] entier = new int[3];
+ for (int i = 0; i < 3; i++)
+ {
+ String element = st.nextToken();
+ try
+ {
+ entier[i] = (int) (Double.parseDouble(element) + 0.5);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(element + " " + Logo.messages.getString("pas_nombre"));
+ }
+ if (entier[i] < 0)
+ entier[i] = 0;
+ if (entier[i] > 255)
+ entier[i] = 255;
+
+ }
+ return (new Color(entier[0], entier[1], entier[2]));
+ }
+
+ /**
+ * Primitive member or member?
+ *
+ * @param param
+ * Stack that contains arguments for the primitive member
+ * @param id
+ * 69 --> member? or 70--> member
+ * @throws LogoError
+ * Incorrect arguments
+ */
+ private void membre(Stack<String> param, int id) throws LogoError
+ {
+ Interprete.operande = true;
+ String mot_retourne = null;
+ boolean b = false;
+ String mot = getWord(param.get(1));
+ String liste = "[ ";
+ if (null == mot)
+ { // on travaille sur une liste
+
+ liste = getFinalList(param.get(1));
+ StringTokenizer st = new StringTokenizer(liste);
+ liste = "[ ";
+ mot = getWord(param.get(0));
+ String str;
+ if (null != mot && mot.equals(""))
+ mot = "\\v";
+ if (null == mot)
+ mot = param.get(0).trim();
+ while (st.hasMoreTokens())
+ {
+ str = st.nextToken();
+ if (str.equals("["))
+ str = extractList(st);
+ if (!liste.equals("[ "))
+ liste += str + " ";
+ if (str.equals(mot) && liste.equals("[ "))
+ {
+ if (id == 69)
+ {
+ b = true;
+ break;
+ }
+ else
+ liste += str + " ";
+ }
+ }
+ }
+ else
+ { // on travaille sur un mot
+ String mot2 = getWord(param.get(0));
+ if (null != mot2)
+ {
+ boolean backslash = false;
+ for (int i = 0; i < mot.length(); i++)
+ {
+ char c = mot.charAt(i);
+ if (!backslash && c == '\\')
+ backslash = true;
+ else
+ {
+ String tmp = Character.toString(c);
+ if (backslash)
+ tmp = "\\" + tmp;
+ if (tmp.equals(mot2))
+ {
+ if (id == 69)
+ {
+ b = true;
+ break;
+ }
+ else
+ {
+ if (!backslash)
+ mot_retourne = mot.substring(i, mot.length());
+ else
+ mot_retourne = mot.substring(i - 1, mot.length());
+ break;
+ }
+ }
+ backslash = false;
+ }
+ }
+ }
+ }
+ if (!liste.equals("[ "))
+ Interprete.calcul.push(liste + "] ");
+ else if (null != mot_retourne)
+ {
+ try
+ {
+ Double.parseDouble(mot_retourne);
+ Interprete.calcul.push(mot_retourne);
+ }
+ catch (NumberFormatException e)
+ {
+ Interprete.calcul.push(debut_chaine + mot_retourne);
+ }
+ }
+ else if (b)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+
+ /**
+ * Primitive before?
+ *
+ * @param param
+ * Stack that contains all arguments
+ * @throws LogoError
+ * Bad argument type
+ */
+
+ private void precede(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ boolean b = false;
+ String ope[] = { "", "" };
+ String mot = "";
+ for (int i = 0; i < 2; i++)
+ {
+ mot = getWord(param.get(i));
+ if (null == mot)
+ throw new LogoError(param.get(i) + " " + Logo.messages.getString("pas_mot"));
+ else
+ ope[i] = mot;
+ }
+ if (ope[1].compareTo(ope[0]) > 0)
+ b = true;
+ if (b)
+ mot = Logo.messages.getString("vrai");
+ else
+ mot = Logo.messages.getString("faux");
+ Interprete.calcul.push(mot);
+ }
+
+ private void infequal(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().infequal(param));
+ }
+
+ private void supequal(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().supequal(param));
+ }
+
+ private void inf(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().inf(param));
+ }
+
+ private void sup(Stack<String> param) throws LogoError
+ {
+ Interprete.operande = true;
+ Interprete.calcul.push(kernel.getCalculator().sup(param));
+ }
+
+ /**
+ * / Primitive equal?
+ *
+ * @param param
+ * Stack that contains all arguments
+ * @throws LogoError
+ */
+ private void equal(Stack<String> param) throws LogoError
+ {
+ try
+ {
+ Double.parseDouble(param.get(0));
+ Double.parseDouble(param.get(1));
+ Interprete.calcul.push(kernel.getCalculator().equal(param));
+ }
+ catch (NumberFormatException e)
+ {
+ if (param.get(0).toString().equals(param.get(1).toString()))
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ }
+ Interprete.operande = true;
+ }
+
+ /**
+ * this method returns the boolean corresponding to the string st
+ *
+ * @param st
+ * true or false
+ * @return The boolean corresponding to the string st
+ * @throws LogoError
+ * If st isn't equal to true or false
+ */
+
+ private boolean predicat(String st) throws LogoError
+ {
+ if (st.toLowerCase().equals(Logo.messages.getString("vrai")))
+ return true;
+ else if (st.toLowerCase().equals(Logo.messages.getString("faux")))
+ return false;
+ else
+ throw new LogoError(st + " " + Logo.messages.getString("pas_predicat"));
+
+ }
+
+ /**
+ * Returns the word contained in st. If it isn't a word, returns null
+ *
+ * @param st
+ * The Object to convert
+ * @return The word corresponding to st
+ */
+ private String getWord(Object st)
+ { // Si c'est un mot
+ String liste = st.toString();
+ if (liste.equals("\""))
+ {
+ debut_chaine = "";
+ return "";
+ }
+ if (liste.length() > 0 && liste.substring(0, 1).equals("\""))
+ {
+ debut_chaine = "\"";
+ return (liste.substring(1, liste.length()));
+ }
+ else
+ try
+ {
+ if (liste == String.valueOf(Double.parseDouble(liste)))
+ debut_chaine = "";
+ else
+ debut_chaine = "\"";
+ return Utils.SortieTexte(liste);
+ }
+ catch (NumberFormatException e)
+ {}
+ return (null);
+ }
+
+ /**
+ * Returns the list contained in the string li without any lineNumber
+ *
+ * @param li
+ * The String corresponding to the list
+ * @return A list without any line Number tag (\0, \1, \2 ...)
+ * @throws LogoError
+ * List bad format
+ */
+
+ private String getFinalList(String li) throws LogoError
+ {
+ // remove line number
+ li = li.replaceAll("\\\\l([0-9])+ ", "");
+ // return list
+ return getList(li);
+ }
+
+ /**
+ * Returns the list contained in the string li
+ *
+ * @param li
+ * The String corresponding to the list
+ * @return A list with line Number tag (\0, \1, \2 ...)
+ * @throws LogoError
+ * List bad format
+ */
+ private String getList(String li) throws LogoError
+ {
+ li = li.trim();
+ // Retourne la liste sans crochets;
+ if (li.substring(0, 1).equals("[") && li.substring(li.length() - 1, li.length()).equals("]"))
+ {
+ li = li.substring(1, li.length() - 1).trim() + " ";
+ if (!li.equals(" "))
+ return li;
+ else
+ return ("");
+ }
+ else
+ throw new LogoError(li + " " + Logo.messages.getString("pas_liste"));
+ }
+
+ /**
+ * Tests if "li" is a list
+ *
+ * @param li
+ * The String to test
+ * @return true if it is a list, else false
+ */
+ //
+ protected static boolean isList(String li)
+ {
+ li = li.trim();
+ if (li.length() > 0 && li.substring(0, 1).equals("[") && li.substring(li.length() - 1, li.length()).equals("]"))
+ return (true);
+ return false;
+ }
+
+ // Format the List (only one space between two elements)
+ private String formatList(String li)
+ {
+ String tampon = "";
+ String precedent = "";
+ StringTokenizer st = new StringTokenizer(li, " []", true);
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ while (st.hasMoreTokens() && element.equals(" "))
+ {
+ element = st.nextToken();
+ }
+ if (element.equals("]"))
+ tampon = tampon.trim() + "] ";
+ else if (element.equals("["))
+ {
+ if (precedent.equals("["))
+ tampon += "[";
+ else
+ tampon = tampon.trim() + " [";
+ }
+ else
+ tampon += element + " ";
+ precedent = element;
+ }
+ return (tampon.trim());
+ }
+
+ private String extractList(StringTokenizer st)
+ {
+ int compteur = 1;
+ String crochet = "[ ";
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ if (element.equals("["))
+ {
+ compteur++;
+ crochet += "[ ";
+ }
+ else if (!element.equals("]"))
+ crochet += element + " ";
+ else if (compteur != 1)
+ {
+ compteur--;
+ crochet += "] ";
+ }
+ else
+ {
+ crochet += element + " ";
+ break;
+ }
+ }
+ element = crochet;
+ compteur = 0;
+ return element.trim();
+ }
+
+ private int extractList(String st, int deb)
+ {
+ int compteur = 1;
+ char element;
+ boolean espace = true;
+ boolean crochet_ouvert = false;
+ boolean crochet_ferme = false;
+ for (int i = deb; i < st.length(); i++)
+ {
+ element = st.charAt(i);
+ if (element == '[')
+ {
+ if (espace)
+ crochet_ouvert = true;
+ espace = false;
+ crochet_ferme = false;
+ }
+ else if (element == ']')
+ {
+ if (espace)
+ crochet_ferme = true;
+ espace = false;
+ crochet_ouvert = false;
+ }
+ else if (element == ' ')
+ {
+ espace = true;
+ if (crochet_ouvert)
+ {
+ compteur++;
+ crochet_ouvert = false;
+ }
+ else if (crochet_ferme)
+ {
+ crochet_ferme = false;
+ if (compteur != 1)
+ compteur--;
+ else
+ {
+ compteur = i;
+ break;
+ }
+ }
+ }
+ }
+ return compteur;
+ }
+
+ // returns how many elements contains the list "liste"
+ private int numberOfElements(String liste)
+ { // calcule le nombre
+ // d'éléments dans une
+ // liste
+ StringTokenizer st = new StringTokenizer(liste);
+ int i = 0;
+ String element = "";
+ while (st.hasMoreTokens())
+ {
+ element = st.nextToken();
+ if (element.equals("["))
+ element = extractList(st);
+ i++;
+ }
+ return i;
+ }
+
+ // returns the item "i" from the list "liste"
+ private String item(String liste, int i) throws LogoError
+ { // retourne
+ // l'élément i d'une
+ // liste
+ StringTokenizer st = new StringTokenizer(liste);
+ String element = "";
+ int j = 0;
+ while (st.hasMoreTokens())
+ {
+ j++;
+ element = st.nextToken();
+ if (element.equals("["))
+ element = extractList(st);
+ if (j == i)
+ break;
+ }
+ if (j != i)
+ throw new LogoError(Logo.messages.getString("y_a_pas") + " " + i + " "
+ + Logo.messages.getString("element_dans_liste") + liste + "]");
+ else if (i == 0 && j == 0)
+ throw new LogoError(Logo.messages.getString("liste_vide"));
+ try
+ {
+ Double.parseDouble(element);
+ return element;
+ } // Si c'est un nombre, on le renvoie.
+ catch (Exception e)
+ {}
+ if (element.startsWith("["))
+ return element + " "; // C'est une liste, on la renvoie telle
+ // quelle.
+ if (element.equals("\\v"))
+ element = "";
+ return "\"" + element; // C'est forcément un mot, on le renvoie.
+ }
+
+ // Test if the name of the variable is valid
+ private void isVariableName(String st) throws LogoError
+ {
+ if (st.equals(""))
+ throw new LogoError(Logo.messages.getString("variable_vide"));
+ if (":+-*/() []=<>&|".indexOf(st) > -1)
+ throw new LogoError(st + " " + Logo.messages.getString("erreur_variable"));
+
+ try
+ {
+ Double.parseDouble(st);
+ throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable"));
+ }
+ catch (NumberFormatException e)
+ {
+
+ }
+
+ }
+
+ // primitve make
+ private void donne(Stack<String> param) throws LogoError
+ {
+ String mot = getWord(param.get(0));
+ if (null == mot)
+ throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word"));
+ mot = mot.toLowerCase();
+ isVariableName(mot);
+ if (Interprete.locale.containsKey(mot))
+ {
+ Interprete.locale.put(mot, param.get(1));
+ }
+ else
+ {
+ wp.getGlobals().define(mot, param.get(1)); // TODO changed
+ }
+ }
+
+ private void delay()
+ {
+ if (WSManager.getUserConfig().getTurtleSpeed() != 0)
+ {
+ try
+ {
+ Thread.sleep((long) (WSManager.getUserConfig().getTurtleSpeed()*3));
+ }
+ catch (InterruptedException e)
+ {}
+ }
+ }
+
+ // How many characters in the word "mot"
+ private int getWordLength(String mot)
+ {// retourne le nombre de caractères
+ // d'un mot
+ int compteur = 0;
+ boolean backslash = false;
+ for (int i = 0; i < mot.length(); i++)
+ {
+ if (!backslash && mot.charAt(i) == '\\')
+ backslash = true;
+ else
+ {
+ backslash = false;
+ compteur++;
+ }
+ }
+ return compteur;
+ }
+
+ // the character number "i" in the word "mot"
+ private String itemWord(int entier, String mot) throws LogoError
+ {
+ String reponse = "";
+ int compteur = 1;
+ boolean backslash = false;
+ if (mot.equals(""))
+ throw new LogoError(Logo.messages.getString("mot_vide"));
+ for (int i = 0; i < mot.length(); i++)
+ {
+ char c = mot.charAt(i);
+ if (!backslash && c == '\\')
+ backslash = true;
+ else
+ {
+ if (compteur == entier)
+ {
+ if (backslash)
+ reponse = "\\" + Character.toString(c);
+ else
+ reponse = Character.toString(c);
+ break;
+ }
+ else
+ {
+ compteur++;
+ backslash = false;
+ }
+ }
+ }
+ return reponse;
+ }
+
+ protected void setWorkspace(UserSpace workspace)
+ {
+ wp = workspace;
+ }
+
+ private void ou(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ boolean result = false;
+ boolean b;
+ for (int i = 0; i < size; i++)
+ {
+ b = predicat(param.get(i).toString());
+ result = result | b;
+ }
+ if (result)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+ Interprete.operande = true;
+ }
+
+ private void et(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ boolean result = true;
+ boolean b;
+ for (int i = 0; i < size; i++)
+ {
+ b = predicat(param.get(i).toString());
+ result = result & b;
+ }
+ Interprete.operande = true;
+ if (result)
+ Interprete.calcul.push(Logo.messages.getString("vrai"));
+ else
+ Interprete.calcul.push(Logo.messages.getString("faux"));
+
+ }
+
+ /**
+ * This methods returns a list that contains all procedures name
+ *
+ * @return A list with all procedure names
+ */
+ private StringBuffer getAllProcedures()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ // TODO CHANGED
+ for (Procedure proc : wp.getExecutables())
+ {
+ sb.append(proc.getName());
+ sb.append(" ");
+ }
+ sb.append("] ");
+ return sb;
+ }
+
+ /**
+ * This methods returns a list that contains all variables name
+ *
+ * @return A list with all variables names
+ */
+
+ private StringBuffer getAllVariables()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ Iterator<String> it = Interprete.locale.keySet().iterator();
+ while (it.hasNext())
+ {
+ String name = it.next();
+ sb.append(name);
+ sb.append(" ");
+ }
+ // TODO changed
+ for (String key : wp.getGlobals().getVariables())
+ {
+ if (!Interprete.locale.containsKey(key))
+ {
+ sb.append(key.toString());
+ sb.append(" ");
+ }
+ }
+ sb.append("] ");
+ return sb;
+ }
+
+ /**
+ * This methods returns a list that contains all Property Lists name
+ *
+ * @return A list with all Property Lists names
+ */
+ private StringBuffer getAllpropertyLists()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ // TODO changed
+ for (String propList : wp.getPropertyLists().getPropListKeys())
+ {
+ sb.append(propList);
+ sb.append(" ");
+ }
+ sb.append("] ");
+ return sb;
+ }
+
+ /**
+ * Delete The variable called "name" from the workspace if it exists
+ *
+ * @param name
+ * The variable name
+ */
+ private void deleteVariable(String name)
+ {
+ if (!Interprete.locale.isEmpty())
+ {
+ if (Interprete.locale.containsKey(name))
+ {
+ Interprete.locale.remove(name);
+ }
+ }
+ else
+ {
+ wp.getGlobals().deleteVariable(name.toLowerCase());
+ }
+ }
+
+ /**
+ * Delete the procedure called "name" from the workspace
+ *
+ * @param name
+ * The procedure name
+ */
+ private void deleteProcedure(String name)
+ {
+ wp.eraseProcedure(name.toLowerCase());
+ }
+
+ /**
+ * According to the type of the data, erase from workspace the resource
+ * called "name"
+ *
+ * @param name
+ * The name of the deleted resource, it couls be a list with all
+ * resource names
+ * @param type
+ * The type for the data, it could be "variable", "procedure" or
+ * "propertylist"
+ * @throws LogoError
+ */
+
+ private void erase(String name, String type) throws LogoError
+ {
+ Interprete.operande = false;
+ if (LaunchPrimitive.isList(name))
+ {
+ name = getFinalList(name);
+ StringTokenizer st = new StringTokenizer(name);
+ while (st.hasMoreTokens())
+ {
+ String item = st.nextToken();
+ this.eraseItem(item, type);
+ }
+ }
+ else
+ {
+ name = getWord(name);
+ if (null != name)
+ {
+ this.eraseItem(name, type);
+ }
+ else
+ throw new LogoError(name + Logo.messages.getString("error.word"));
+
+ }
+ }
+
+ /**
+ * According to the type of the data, erase from workspace the resource
+ * called "name"
+ *
+ * @param name
+ * The name of the deleted resource
+ * @param type
+ * The type for the data, it could be "variable", "procedure" or
+ * "propertylist"
+ */
+ private void eraseItem(String name, String type)
+ {
+ if (type.equals("procedure"))
+ {
+ this.deleteProcedure(name.toLowerCase());
+ }
+ else if (type.equals("variable"))
+ {
+ this.deleteVariable(name.toLowerCase());
+ }
+ else if (type.equals("propertylist"))
+ {
+ wp.getPropertyLists().removePropList(name.toLowerCase());
+ }
+
+ }
+}
diff --git a/logo/src/xlogo/kernel/LogoError.java b/logo/src/xlogo/kernel/LogoError.java
new file mode 100644
index 0000000..175c243
--- /dev/null
+++ b/logo/src/xlogo/kernel/LogoError.java
@@ -0,0 +1,89 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+/**
+ * In XLogo, myException was used. My exception was something rather funny.
+ * Actually it was really bad. myException was an exception that handled itself, by showing an error dialog and by aborting execution of the running Logo program.
+ * Thus throwing a myException was more like a function call with the side effect, that execution jumped to some other place, wherever it was caught.
+ * Note that to handle itself, myException had to have a reference to {@link Application}. Thus every part of the interpreter that wanted to throw Logo errors
+ * had to have such a reference, even though Application was not used otherwise.<br>
+ * By moving the exception handling at the root, where interpretation is started, in {@link Affichage}, I managed to decouple several classes from Application.
+ * (Still, many of them are unnecessarily dependent on Application, but I cannot refactor everything in the given time)
+ *
+ * Also note how ugly myException was used before. LaunchPrimitive is the best example. The general pattern was this:<br>
+ *
+ * <pre>
+ * {@code
+ * case i: // i : the id of some Logo primitive
+ * try
+ * {
+ * ...
+ * if (the next token is not as expected)
+ * throw new myException(application, errorMessage);
+ * ...
+ * }
+ * catch (myException)
+ * {}
+ * break; // => end of execute()
+ * // Because myException "handled itself", Affichage and Interprete will not continue execution
+ * }
+ * </pre>
+ *
+ * Note that almost all of the more than 300 Logo primitives contained such a statement, sometimes even more.
+ * That is more than 5x300 = 1500 unnecessary lines of code (after my style guide) that make the reading very hard,
+ * and introducing multiple levels of blocks { } to the structure.
+ *
+ * I took myself the time to remove all these unnecessary try-catches, place only one in Affichage and thus make the core of the application much more readable.
+ * <p><p>
+ * Note that I cannot test all the 300 Logo procedures for every case, but since throwing a myException caused the Interpreter to stop anyway,
+ * Redirecting the exception up to the root in Affichage will not change the flow of correct Logo Execution. Only executions which caused an exception can maybe behave slightly different now.
+ * I will for sure test all the procedures that we use in our school projects and a few more.
+ *
+ * <p><p>
+ * Another interesting thing, or again, really bad.
+ * The {@code static boolean lance} in myException was initialized with false. There are several assignments that set, again, false to it, but there is no single set to true.
+ * In most cases where lance is tested in a if-statement, it is tested in conjunction with Application.error (which can be true or false)
+ *
+ * <p> And then, there was also the nested class Affiche, which was never used.
+ *
+ *@author Marko Zivkovic
+ */
+public class LogoError extends Exception
+{
+ private static final long serialVersionUID = 9184760816698357437L;
+
+ public LogoError()
+ {
+ }
+
+ public LogoError(String st)
+ {
+ super(st);
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopFillPolygon.java b/logo/src/xlogo/kernel/LoopFillPolygon.java
new file mode 100644
index 0000000..f2cb0fd
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopFillPolygon.java
@@ -0,0 +1,48 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+
+public class LoopFillPolygon extends LoopProperties {
+ /**
+ * The super constructor for Fill Polygon Loops
+ * @param instr The instruction to execute each loop
+ */
+
+ LoopFillPolygon(){
+ super(BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,"");
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ protected boolean isFillPolygon(){
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopFor.java b/logo/src/xlogo/kernel/LoopFor.java
new file mode 100644
index 0000000..473fd28
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopFor.java
@@ -0,0 +1,94 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+import java.math.BigDecimal;
+
+public class LoopFor extends LoopProperties{
+ /**
+ * This boolean indicates
+ * @uml.property name="conserver"
+ */
+ protected boolean conserver=false;
+
+ /**
+ * The variable name
+ * @uml.property name="var"
+ */
+
+ String var="";
+ /**
+ * Constructor Loop: For
+ * @param counter The beginning integer
+ * @param end The end integer
+ * @param increment The increment between two values
+ * @param instr The instruction to execute between two values
+ * @param var The name of the variable
+ */
+
+ LoopFor(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr,String var){
+ super(counter,end,increment,instr);
+ this.var=var;
+ }
+
+ protected boolean isFor(){
+ return true;
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ /**
+ * This method affects the variable counter the correct value
+ * @param first boolean that indicates if it is the first affectation
+ */
+ protected void AffecteVar(boolean first){
+ String element=String.valueOf(super.getCounter());
+ if (element.endsWith(".0")) element=element.substring(0,element.length()-2) ;
+ if (element.startsWith(".")||element.equals("")) element="0"+element;
+
+ if (Interprete.locale.containsKey(var)){
+ if (first) conserver=true;
+ Interprete.locale.put(var, element);
+ }
+ else {
+ Interprete.locale.put(var,element);
+ }
+ }
+ /**
+ * This method deletes the variable var from the local stack variable
+ */
+ void DeleteVar(){
+ if (!conserver){
+ if (Interprete.locale.containsKey(var)){
+ Interprete.locale.remove(var);
+ }
+
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/LoopForEach.java b/logo/src/xlogo/kernel/LoopForEach.java
new file mode 100644
index 0000000..3d0c518
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopForEach.java
@@ -0,0 +1,69 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+import java.util.Vector;
+
+public class LoopForEach extends LoopFor{
+ private Vector<String> vec;
+ /**
+ * Constructor Loop: For
+ * @param counter The beginning integer
+ * @param end The end integer
+ * @param increment The increment between two values
+ * @param instr The instruction to execute between two values
+ * @param var The name of the variable
+ * @param vec The Vec with all value for the variable
+ */
+ LoopForEach(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr,String var,Vector<String> vec){
+ super(counter,end,increment,instr,var);
+ this.vec=vec;
+ }
+ protected boolean isForEach(){
+ return true;
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ /**
+ * This method affects the variable counter the correct value
+ * @param first boolean that indicates if it is the first affectation
+ */
+ protected void AffecteVar(boolean first){
+ String element=vec.get(getCounter().intValue());
+ if (Interprete.locale.containsKey(var)){
+ if (first) conserver=true;
+ Interprete.locale.put(var, element);
+ }
+ else {
+ Interprete.locale.put(var,element);
+ }
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopProperties.java b/logo/src/xlogo/kernel/LoopProperties.java
new file mode 100644
index 0000000..ac252e4
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopProperties.java
@@ -0,0 +1,149 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+import java.math.BigDecimal;
+/**
+ * This class saves all Loop Properties (repeat, while, for) such as increment, end integer ....
+ * @author loic
+ */
+public class LoopProperties {
+ /**
+ * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values
+ * @uml.property name="counter"
+ */
+ private BigDecimal counter;
+ /**
+ * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values
+ * @uml.property name="end"
+ */
+ private BigDecimal end;
+ /**
+ * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values
+ * @uml.property name="increment"
+ */
+ private BigDecimal increment;
+ /**
+ * The Instruction to execute on each iteration
+ * @uml.property name="instr"
+ */
+ String instr;
+/**
+ * The super constructor for all loops
+ * @param counter The beginning integer
+ * @param fin The end integer
+ * @param increment The increment between two values
+ * @param instr The instruction to execute each loop
+ */
+ LoopProperties(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){
+ this.counter=counter;
+ this.end=end;
+ this.increment=increment;
+ this.instr=instr;
+ }
+
+
+ /**
+ * Adds the increment to the variable counter
+ */
+
+ protected void incremente(){
+ counter=counter.add(increment);
+ counter=new BigDecimal(MyCalculator.eraseZero(counter));
+ }
+ /**
+ * This method returns the Loop Id
+ * @return the Loop Id (TYPE_FOR, TYPE_WHILE...)
+ */
+/* protected int getId(){
+ return id;
+ }*/
+ /**
+ * This method returns the Loop Id
+ * @return the Loop Id (TYPE_FOR, TYPE_WHILE...)
+ * @uml.property name="counter"
+ */
+ protected BigDecimal getCounter(){
+ return counter;
+ }
+ /**
+ * This method returns the end Value
+ * @return the end value for the loop
+ * @uml.property name="end"
+ */
+ protected BigDecimal getEnd(){
+ return end;
+ }
+ /**
+ * this method returns the increment for the loop
+ * @return The variable increment
+ * @uml.property name="increment"
+ */
+ protected BigDecimal getIncrement(){
+ return increment;
+ }
+ /**
+ * This method returns the instructions to execute each loop
+ * @return the instruction block
+ * @uml.property name="instr"
+ */
+ protected String getInstr(){
+ return instr;
+ }
+ /**
+ * This method returns a loop description
+ */
+ public String toString(){
+ return(counter+" "+end+" "+increment+"\n"+instr+"\n");
+ }
+
+ protected boolean isFor(){
+ return false;
+ }
+ protected boolean isWhile(){
+ return false;
+ }
+ protected boolean isForEach(){
+ return false;
+ }
+ protected boolean isRepeat(){
+ return false;
+ }
+ protected boolean isForEver(){
+ return true;
+ }
+ protected boolean isFillPolygon(){
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopRepeat.java b/logo/src/xlogo/kernel/LoopRepeat.java
new file mode 100644
index 0000000..5362d46
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopRepeat.java
@@ -0,0 +1,43 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+
+public class LoopRepeat extends LoopProperties{
+ LoopRepeat(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){
+ super(counter,end,increment,instr);
+ }
+ protected boolean isRepeat(){
+ return true;
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/LoopWhile.java b/logo/src/xlogo/kernel/LoopWhile.java
new file mode 100644
index 0000000..c4451c8
--- /dev/null
+++ b/logo/src/xlogo/kernel/LoopWhile.java
@@ -0,0 +1,44 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.BigDecimal;
+
+public class LoopWhile extends LoopProperties{
+
+ LoopWhile(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){
+ super(counter,end,increment,instr);
+ }
+ protected boolean isForEver(){
+ return false;
+ }
+ protected boolean isWhile(){
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/kernel/MP3Player.java b/logo/src/xlogo/kernel/MP3Player.java
new file mode 100644
index 0000000..e996bd2
--- /dev/null
+++ b/logo/src/xlogo/kernel/MP3Player.java
@@ -0,0 +1,86 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+import javazoom.jl.decoder.JavaLayerException;
+import javazoom.jl.player.advanced.AdvancedPlayer;
+public class MP3Player extends Thread{
+
+ private AdvancedPlayer player;
+
+ MP3Player(Application app,String path) throws LogoError{
+ try {
+ // Build absolutePath
+ String absolutePath= Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder())
+ + File.separator + Utils.SortieTexte(path);
+ player=new AdvancedPlayer(new FileInputStream(absolutePath));
+ }
+ catch(FileNotFoundException e){
+ // tentative fichier réseau
+ try{
+ URL url =new java.net.URL(path);
+ java.io.InputStream fr = url.openStream();
+ player=new AdvancedPlayer(fr);
+ }
+ catch( java.net.MalformedURLException e1){
+
+ throw new LogoError(Logo.messages.getString("error.iolecture"));
+
+
+ }
+ catch(IOException e2){}
+ catch(JavaLayerException e3){}
+ }
+ catch(JavaLayerException e4){}
+
+ }
+ public void run(){
+ try{
+ player.play();
+ }
+ catch(JavaLayerException e){}
+ }
+ /**
+ * @return
+ * @uml.property name="player"
+ */
+ protected AdvancedPlayer getPlayer(){
+ return player;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/MyCalculator.java b/logo/src/xlogo/kernel/MyCalculator.java
new file mode 100644
index 0000000..1fde09c
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyCalculator.java
@@ -0,0 +1,1221 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.MathContext;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Stack;
+
+import xlogo.Logo;
+import xlogo.utils.Utils;
+
+public class MyCalculator
+{
+ private final BigDecimal tenth = new BigDecimal(0.1);
+
+ private MathContext mc = null;
+
+ // If precision is lesser than 16 (operation for double)
+ /**
+ * @uml.property name="lowPrecision"
+ */
+ private boolean lowPrecision = true;
+
+ /**
+ * Indicates if the log table have been created
+ *
+ * @uml.property name="initLogTable"
+ */
+ private boolean initLogTable;
+ /**
+ * This is a table containing all BigDecimal ln(1+10^ (-k) ), k in
+ * {0,1....,digits-1} This are constants for the Cordic method to calculate
+ * ln, exp
+ *
+ * @uml.property name="logTable" multiplicity="(0 -1)" dimension="1"
+ */
+ private BigDecimal[] logTable;
+ /**
+ * Indicates if the trigonometric table have been created
+ *
+ * @uml.property name="initCosTable"
+ */
+ private boolean initCosTable;
+ /**
+ * This is a table containing all BigDecimal arctan 10^ (-k) , k in
+ * {0,1....,digits-1} This are constants for the Cordic method to calculate
+ * trigonometric functions
+ *
+ * @uml.property name="cosTable" multiplicity="(0 -1)" dimension="1"
+ */
+ private BigDecimal[] cosTable;
+
+ private static int digits;
+
+ protected MyCalculator(int digits)
+ {
+ MyCalculator.digits = digits;
+ initLogTable = false;
+ initCosTable = false;
+ if (digits < 16)
+ {
+ mc = new MathContext(16);
+ lowPrecision = true;
+ logTable = new BigDecimal[16];
+ cosTable = new BigDecimal[16];
+ }
+ else
+ {
+ mc = new MathContext(digits);
+ lowPrecision = false;
+ logTable = new BigDecimal[digits];
+ cosTable = new BigDecimal[digits];
+ }
+ }
+
+ /**
+ * Return The exponential of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return Exp(s)
+ * @throws LogoError
+ * if s isn't a number
+ */
+
+ protected String exp(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double nombre = numberDouble(s);
+ return teste_fin_double(Math.exp(nombre));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return expBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return The logarithm of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return log(s)
+ * @throws LogoError
+ * if s isn't a number or negative
+ */
+
+ protected String log(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double nombre = numberDouble(s);
+ if (nombre < 0 || nombre == 0)
+ {
+ String log = Utils.primitiveName("arithmetic.log");
+ throw new LogoError(log + " " + Logo.messages.getString("attend_positif"));
+ }
+ return teste_fin_double(Math.log(nombre));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ if (bd.signum() != 1)
+ {
+ String log = Utils.primitiveName("arithmetic.log");
+ throw new LogoError(log + " " + Logo.messages.getString("attend_positif"));
+ }
+ return logBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return The square root of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return sqrt(s)
+ * @throws LogoError
+ * if s isn't a number or negative
+ */
+
+ protected String sqrt(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double number = numberDouble(s);
+ if (number < 0)
+ {
+ String sqrt = Utils.primitiveName("arithmetic.racine");
+ throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif"));
+ }
+ return teste_fin_double(Math.sqrt(number));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ if (bd.signum() == -1)
+ {
+ String sqrt = Utils.primitiveName("arithmetic.racine");
+ throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif"));
+ }
+ return sqrtBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return the product of all elements in stack param
+ *
+ * @param param
+ * The stack of operands
+ * @return The product
+ * @throws LogoError
+ */
+ protected String multiply(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ BigDecimal product = BigDecimal.ONE;
+ BigDecimal a;
+ for (int i = 0; i < size; i++)
+ {
+ a = numberDecimal(param.get(i));
+ product = product.multiply(a, mc);
+ }
+ return product.stripTrailingZeros().toPlainString();
+ }
+
+ protected String divide(Stack<String> param) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double a = numberDouble(param.get(0));
+ double b = numberDouble(param.get(1));
+ if (b == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return (teste_fin_double(a / b));
+ }
+ else
+ {
+ BigDecimal a = new BigDecimal(param.get(0), mc);
+ BigDecimal b = new BigDecimal(param.get(1), mc);
+ if (b.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return (a.divide(b, mc).stripTrailingZeros().toPlainString());
+ }
+ }
+
+ /**
+ * Return the sum of all elements in stack param
+ *
+ * @param param
+ * The stack of operands
+ * @return The sum
+ * @throws LogoError
+ */
+ protected String add(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal a;
+ for (int i = 0; i < size; i++)
+ {
+ a = numberDecimal(param.get(i));
+ sum = sum.add(a, mc);
+ }
+
+ return sum.stripTrailingZeros().toPlainString();
+ }
+
+ protected String inf(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) < 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String sup(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) > 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String infequal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) <= 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String supequal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) >= 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String equal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) == 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String substract(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ return a.subtract(b, mc).stripTrailingZeros().toPlainString();
+ }
+
+ /**
+ * Returns the opposite of s
+ *
+ * @param s
+ * @return
+ */
+ protected String minus(String s) throws LogoError
+ {
+ BigDecimal a = numberDecimal(s);
+ return a.negate(mc).stripTrailingZeros().toPlainString();
+ }
+
+ protected String remainder(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ int aa = getInteger(a);
+ int bb = getInteger(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return teste_fin_double(aa % bb);
+ }
+ else
+ {
+ BigDecimal aa = getBigInteger(a);
+ BigDecimal bb = getBigInteger(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return aa.remainder(bb, mc).stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String modulo(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ int aa = getInteger(a);
+ int bb = getInteger(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ double rem = aa % bb;
+ if (aa * bb < 0 && rem != 0)
+ rem = rem + bb;
+ return teste_fin_double(rem);
+ }
+ else
+ {
+ BigDecimal aa = getBigInteger(a);
+ BigDecimal bb = getBigInteger(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ BigDecimal rem = aa.remainder(bb, mc);
+ if (aa.multiply(bb).compareTo(BigDecimal.ZERO) == -1 && (!rem.equals(BigDecimal.ZERO)))
+ rem = rem.add(bb);
+ return rem.stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String quotient(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double aa = numberDouble(a);
+ double bb = numberDouble(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return String.valueOf((int) (aa / bb));
+ }
+ else
+ {
+ BigDecimal aa = numberDecimal(a);
+ BigDecimal bb = numberDecimal(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return aa.divideToIntegralValue(bb, mc).stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String truncate(String a) throws LogoError
+ {
+ BigDecimal ent = numberDecimal(a);
+ return ent.toBigInteger().toString();
+ }
+
+ protected String abs(String a) throws LogoError
+ {
+ BigDecimal e = numberDecimal(a);
+ return e.abs().stripTrailingZeros().toPlainString();
+ }
+
+ protected String power(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double p = Math.pow(numberDouble(a), numberDouble(b));
+ // Bug pr power -1 0.5
+ Double p1 = new Double(p);
+ if (p1.equals(Double.NaN))
+ throw new LogoError(Utils.primitiveName("arithmetic.puissance") + " "
+ + Logo.messages.getString("attend_positif"));
+ // End Bug
+ return teste_fin_double(p);
+ }
+ else
+ {
+ // if the exposant is an integer
+ try
+ {
+ int n = Integer.parseInt(b);
+ BigDecimal aa = numberDecimal(a);
+ return aa.pow(n, mc).toPlainString();
+ }
+ catch (NumberFormatException e)
+ {
+ BigDecimal aa = numberDecimal(a);
+ BigDecimal bb = numberDecimal(b);
+ if (aa.signum() == 1)
+ {
+ return expBD(bb.multiply(logBD(aa), mc)).toPlainString();
+ }
+ else if (aa.signum() == 0)
+ return "0";
+ else
+ return String.valueOf(getInteger(b));
+ }
+ }
+ }
+
+ protected String log10(String s) throws LogoError
+ {
+ Stack<String> tmp = new Stack<String>();
+ tmp.push(log(s));
+ tmp.push(log("10"));
+ return divide(tmp);
+ }
+
+ protected String pi()
+ {
+ if (lowPrecision)
+ {
+ return String.valueOf(Math.PI);
+ }
+ else
+ {
+ return piBD().toPlainString();
+ }
+ }
+
+ protected String sin(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.sin(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return sinBD(bd).toPlainString();
+
+ }
+ }
+
+ protected String cos(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.cos(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return cosBD(bd).toPlainString();
+
+ }
+ }
+
+ protected String tan(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.tan(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return tanBD(bd).toPlainString();
+ }
+ }
+
+ protected String atan(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.atan(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(atanBD(bd)).toPlainString();
+ }
+ }
+
+ protected String acos(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.acos(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(acosBD(bd)).toPlainString();
+ }
+ }
+
+ protected String asin(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.asin(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(asinBD(bd)).toPlainString();
+ }
+ }
+
+ /**
+ * This method returns the exp of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal expBD(BigDecimal bd)
+ {
+ if (!initLogTable)
+ {
+ initLogTable();
+ }
+ int signum = bd.signum();
+ if (signum == -1)
+ {
+ BigDecimal exp = expCordic(bd.negate(mc));
+ exp = BigDecimal.ONE.divide(exp, mc);
+ return exp;
+ }
+ else if (signum == 0)
+ return BigDecimal.ONE;
+ else
+ {
+ return expCordic(bd);
+ }
+ }
+
+ private BigDecimal expCordic(BigDecimal bd)
+ {
+ int i = 0;
+ BigDecimal y = BigDecimal.ONE;
+ while (i < mc.getPrecision())
+ {
+ while (logTable[i].subtract(bd).signum() == -1)
+ {
+ bd = bd.subtract(logTable[i], mc);
+ y = y.add(y.multiply(tenth.pow(i, mc), mc), mc);
+ }
+ i++;
+ }
+ y = y.multiply(bd.add(BigDecimal.ONE, mc), mc);
+ return y;
+
+ }
+
+ /**
+ * This method returns the log of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal logBD(BigDecimal bd)
+ {
+ if (!initLogTable)
+ {
+ initLogTable();
+ }
+ // If bd > 1
+ int signum = bd.subtract(BigDecimal.ONE, mc).signum();
+ if (signum == 1)
+ {
+ bd = bd.subtract(BigDecimal.ONE, mc);
+ return logCordic(bd);
+ }
+ else if (signum == 0)
+ return BigDecimal.ZERO;
+ else
+ {
+ bd = BigDecimal.ONE.divide(bd, mc).subtract(BigDecimal.ONE, mc);
+ return logCordic(bd).negate(mc);
+ }
+ }
+
+ private BigDecimal logCordic(BigDecimal bd)
+ {
+ int i = 0;
+ BigDecimal y = BigDecimal.ZERO;
+ while (i < mc.getPrecision())
+ {
+ BigDecimal tenthi = tenth.pow(i, mc);
+ while (bd.subtract(tenthi, mc).signum() > 0)
+ {
+ bd = bd.subtract(tenthi, mc).divide(BigDecimal.ONE.add(tenthi, mc), mc);
+ y = y.add(logTable[i], mc);
+ }
+ i++;
+ }
+ y = y.add(bd, mc).subtract(bd.pow(2, mc).multiply(new BigDecimal(0.5), mc), mc);
+ return y;
+
+ }
+
+ /**
+ * This method returns the sqrt of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sqrtBD(BigDecimal bd)
+ {
+ if (bd.signum() == 0)
+ return BigDecimal.ZERO;
+ BigDecimal three = new BigDecimal(3);
+ BigDecimal half = new BigDecimal(0.5);
+ BigDecimal x = BigDecimal.ZERO;
+ BigDecimal y = BigDecimal.ONE.min(BigDecimal.ONE.divide(bd, mc));
+ while (x.compareTo(y) == -1)
+ {
+ x = y;
+ // y=(3x-bd*x^3)/2
+ y = x.multiply(three, mc).subtract(bd.multiply(x.pow(3, mc), mc), mc).multiply(half, mc);
+ }
+ return BigDecimal.ONE.divide(y, mc);
+ }
+
+ /**
+ * This method returns the cos of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal cosBD(BigDecimal bd)
+ {
+ // bd is in degree
+ BigDecimal period = new BigDecimal(360);
+ BigDecimal a90 = new BigDecimal(90);
+ BigDecimal a135 = new BigDecimal(135);
+ BigDecimal a180 = new BigDecimal(180);
+ BigDecimal a225 = new BigDecimal(225);
+ BigDecimal a270 = new BigDecimal(270);
+ BigDecimal a315 = new BigDecimal(315);
+
+ bd = bd.remainder(period, mc);
+ if (bd.signum() == -1)
+ bd = bd.add(period, mc);
+ BigDecimal quarterpi = new BigDecimal(45);
+ // Now bd between 0 and 360.
+ if (bd.compareTo(quarterpi) == -1)
+ {
+ // Between 0 and 45
+ return cosCordic(toRadian(bd));
+ }
+ else if (bd.compareTo(a90) == -1)
+ {
+ // Between 45 and 90
+ return sinCordic(toRadian(a90.subtract(bd, mc)));
+ }
+ else if (bd.compareTo(a135) == -1)
+ {
+ // Between 90 and 135
+ return sinCordic(toRadian(bd.subtract(a90, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a180) == -1)
+ {
+ // Between 135 and 180
+ return cosCordic(toRadian(a180.subtract(bd, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a225) == -1)
+ {
+ // Between 180 and 225
+ return cosCordic(toRadian(bd.subtract(a180, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a270) == -1)
+ {
+ // Between 225 and 270
+ return sinCordic(toRadian(a270.subtract(bd, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a315) == -1)
+ {
+ // Between 270 and 315
+ return sinCordic(toRadian(bd.subtract(a270, mc)));
+ }
+ else
+ {
+ return cosCordic(toRadian(new BigDecimal(360).subtract(bd, mc)));
+ }
+ }
+
+ /**
+ * This method returns the cos of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal cosCordic(BigDecimal bd)
+ {
+ return BigDecimal.ONE.divide(sqrtBD(tanCordic(bd).pow(2, mc).add(BigDecimal.ONE, mc)), mc);
+ }
+
+ /**
+ * This method returns the cos of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sinBD(BigDecimal bd)
+ {
+ BigDecimal a90 = new BigDecimal(90);
+ return cosBD(a90.subtract(bd, mc));
+
+ }
+
+ /**
+ * This method returns the sin of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sinCordic(BigDecimal bd)
+ {
+ BigDecimal tan = tanCordic(bd);
+ return tan.divide(sqrtBD(tan.pow(2, mc).add(BigDecimal.ONE, mc)), mc);
+ }
+
+ /**
+ * This method returns the tan of bd (in degree)
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal tanBD(BigDecimal bd)
+ {
+ // bd is in degree
+ BigDecimal pi = new BigDecimal(180);
+ BigDecimal halfpi = new BigDecimal(90);
+ BigDecimal quarterpi = new BigDecimal(45);
+ bd = bd.remainder(pi, mc);
+ if (bd.compareTo(halfpi.negate(mc)) == -1)
+ bd = bd.add(pi, mc);
+ if (bd.compareTo(halfpi) == 1)
+ bd = bd.subtract(pi, mc);
+ // Now bd is in -90;+90 degrees
+
+ if (bd.compareTo(quarterpi) == 1)
+ {
+ BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc));
+ return new BigDecimal(2).multiply(tanCordic(x), mc).divide(
+ BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc);
+ }
+ else if (bd.signum() == 1)
+ {
+ return tanCordic(toRadian(bd));
+ }
+ else if (bd.compareTo(quarterpi.negate(mc)) == 1)
+ {
+ return tanCordic(toRadian(bd.negate(mc))).negate(mc);
+ }
+ else
+ {
+ BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc)).negate(mc);
+ return new BigDecimal(2).multiply(tanCordic(x), mc)
+ .divide(BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc).negate(mc);
+ }
+ }
+
+ /**
+ * This method returns the tan of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal tanCordic(BigDecimal bd)
+ {
+ if (!initCosTable)
+ initCosTable();
+ BigDecimal three = new BigDecimal(3);
+ int k = 1;
+ BigDecimal x = BigDecimal.ONE;
+ BigDecimal y = BigDecimal.ZERO;
+ BigDecimal tenthk = tenth;
+ while (k < mc.getPrecision())
+ {
+ while (cosTable[k].compareTo(bd) == -1)
+ {
+ bd = bd.subtract(cosTable[k], mc);
+ BigDecimal tmp = x;
+ x = x.subtract(tenthk.multiply(y, mc), mc);
+ y = y.add(tenthk.multiply(tmp, mc), mc);
+ }
+ tenthk = tenthk.multiply(tenth, mc);
+ k++;
+ }
+ BigDecimal tmp = bd.pow(3, mc).add(three.multiply(bd, mc), mc);
+ // return (3*y+(3t+t^3)*x)/(3x-(3t+t^3)*y
+ return three.multiply(y, mc).add(x.multiply(tmp, mc), mc)
+ .divide(three.multiply(x, mc).subtract(y.multiply(tmp, mc), mc), mc);
+ }
+
+ private BigDecimal piBD()
+ {
+ if (!initCosTable)
+ {
+ initCosTable();
+ }
+ return cosTable[0].multiply(new BigDecimal(4), mc);
+ }
+
+ /**
+ * This method creates the log Table using
+ * log h=(h-1)-(h-1)^2/2+(h-1)^3/3-.....
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private void initLogTable()
+ {
+ initLogTable = true;
+ // calculate ln 2
+ // Using ln 2=2*(x+x^3/3+x^5/5+....) with x=1/3
+
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal previous = BigDecimal.ONE;
+ BigDecimal i = BigDecimal.ONE;
+ BigDecimal nine = new BigDecimal(9);
+ BigDecimal two = new BigDecimal(2);
+ BigDecimal power = new BigDecimal(3);
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(BigDecimal.ONE.divide(i.multiply(power, mc), mc), mc);
+ i = i.add(two, mc);
+ power = power.multiply(nine, mc);
+ }
+ logTable[0] = sum.multiply(two, mc);
+
+ // Calculate ln (1+10^-j) j in 1 ... digits-1
+
+ for (int j = 1; j < mc.getPrecision(); j++)
+ {
+ // count=0;
+ sum = BigDecimal.ZERO;
+ previous = BigDecimal.ONE;
+ i = BigDecimal.ONE;
+ // 10^(-j)
+ BigDecimal bd = tenth.pow(j, mc);
+ power = bd;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(power.divide(i, mc), mc);
+ if (i.signum() == 1)
+ i = i.add(BigDecimal.ONE, mc).negate(mc);
+ else
+ i = i.subtract(BigDecimal.ONE, mc).negate(mc);
+ power = power.multiply(bd, mc);
+ // count++;
+ }
+ logTable[j] = sum;
+ }
+ }
+
+ /**
+ * This method creates the cos Table using
+ * arctan h=x-x^3/3+x^5/5-x^7/7...
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private void initCosTable()
+ {
+ initCosTable = true;
+ // calculate pi/4
+
+ cosTable[0] = calcPI().multiply(new BigDecimal(0.25), mc);
+
+ // Calculate arctan (10^-j) j in 1 ... digits-1
+
+ for (int j = 1; j < mc.getPrecision(); j++)
+ {
+ // 10^(-j)
+ BigDecimal bd = tenth.pow(j, mc);
+ cosTable[j] = arctanSE(bd);
+ // System.out.println(cosTable[j].toPlainString());
+ }
+ }
+
+ // Using PI = 16arctg(1/5) - 4arctg(1/239)
+ private BigDecimal calcPI()
+ {
+ return new BigDecimal(16).multiply(arctanSE(new BigDecimal("0.2")), mc).subtract(
+ new BigDecimal(4).multiply(arctanSE(BigDecimal.ONE.divide(new BigDecimal(239), mc)), mc), mc);
+ }
+
+ private BigDecimal arctanSE2(BigDecimal bd)
+ {
+ BigDecimal i = BigDecimal.ONE;
+ // BigDecimal j=new BigDecimal(3);
+ BigDecimal two = new BigDecimal(2);
+ BigDecimal square = bd.multiply(bd, mc);
+ BigDecimal power = bd.divide(square.add(BigDecimal.ONE, mc), mc);
+ BigDecimal cst = new BigDecimal(4).multiply(square, mc).divide(square.add(BigDecimal.ONE, mc), mc);
+ BigDecimal previous = BigDecimal.ZERO;
+ BigDecimal sum = power;
+ int count = 0;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ power = power.multiply(cst, mc);
+ power = power.multiply(i.pow(2, mc), mc);
+ BigDecimal doublei = two.multiply(i, mc);
+ doublei = doublei.multiply(doublei.add(BigDecimal.ONE, mc), mc);
+ power = power.divide(doublei, mc);
+ sum = sum.add(power, mc);
+ i = i.add(BigDecimal.ONE);
+ count++;
+ }
+ System.out.println("Itérations " + count);
+ return sum;
+
+ }
+
+ private BigDecimal atanBD(BigDecimal bd)
+ {
+ if (bd.signum() == -1)
+ return atanBD(bd.negate(mc)).negate(mc);
+ if (bd.compareTo(BigDecimal.ONE) == 1)
+ // pi/2 -arctan (1/x)
+ return piBD().multiply(new BigDecimal(0.5), mc).subtract(arctanSE(BigDecimal.ONE.divide(bd, mc)), mc);
+ else if (bd.compareTo(BigDecimal.ONE) == 0)
+ return piBD().multiply(new BigDecimal("0.25"), mc);
+ else
+ return arctanSE(bd);
+ }
+
+ private BigDecimal acosBD(BigDecimal bd)
+ {
+ if (bd.compareTo(new BigDecimal("-1")) == 0)
+ {
+ return piBD();
+ }
+ // acos x= 2 atan (sqrt(1-x^2)/1+x
+ else
+ {
+ return new BigDecimal("2").multiply(
+ atanBD(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)).divide(BigDecimal.ONE.add(bd, mc), mc)),
+ mc);
+
+ }
+ }
+
+ private BigDecimal asinBD(BigDecimal bd)
+ {
+ // acos x= 2 atan (x/(1+sqrt(1-x^2))
+ return new BigDecimal("2").multiply(
+ atanBD(bd.divide(BigDecimal.ONE.add(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)), mc), mc)), mc);
+ }
+
+ // arctan h=x-x^3/3+x^5/5-x^7/7...
+ private BigDecimal arctanSE(BigDecimal bd)
+ {
+ BigDecimal i = BigDecimal.ONE;
+ BigDecimal two = new BigDecimal(2, mc);
+ BigDecimal square = bd.multiply(bd, mc);
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal previous = BigDecimal.ONE;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(bd.divide(i, mc), mc);
+ if (i.signum() == 1)
+ i = i.add(two, mc).negate(mc);
+ else
+ i = i.subtract(two, mc).negate(mc);
+ bd = bd.multiply(square, mc);
+ }
+ return sum;
+ }
+
+ private BigDecimal toRadian(BigDecimal n)
+ {
+ return n.multiply(piBD(), mc).divide(new BigDecimal(180), mc);
+ }
+
+ private BigDecimal toDegree(BigDecimal n)
+ {
+ return n.multiply(new BigDecimal(180), mc).divide(piBD(), mc);
+ }
+
+ /**
+ * This method converts st to double
+ *
+ * @param st
+ * The String
+ * @return The double corresponding to st
+ * @throws LogoError
+ * If st can't be convert
+ */
+
+ protected double numberDouble(String st) throws LogoError
+ { // Si un nombre est
+ // un double
+ try
+ {
+ return (Double.parseDouble(st));
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_nombre"));
+ }
+ }
+
+ /**
+ * If a double ends with the suffix ".0", remove it
+ *
+ * @param d
+ * @return
+ */
+
+ static protected String teste_fin_double(double d)
+ {
+ String st = String.valueOf(d);
+ if (st.endsWith(".0"))
+ st = st.substring(0, st.length() - 2);
+ return st;
+ }
+
+ /**
+ * Converts st to BigDecimal number
+ *
+ * @param st
+ * The String to convert
+ * @return The BigDecimal Number
+ * @throws LogoError
+ * if st isn't a number
+ */
+
+ protected BigDecimal numberDecimal(String st) throws LogoError
+ {
+ /*
+ * if (null==mc){
+ * try {
+ * BigDecimal bd = new BigDecimal(st).setScale(16,
+ * BigDecimal.ROUND_HALF_EVEN);
+ * return (new BigDecimal(eraseZero(bd)));
+ * } catch (NumberFormatException e) {
+ * throw new myException( st + " "
+ * + Logo.messages.getString("pas_nombre"));
+ * }
+ * }
+ * else {
+ */
+ try
+ {
+ return new BigDecimal(st, mc);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_nombre"));
+ }
+ // }
+
+ }
+
+ /**
+ * Erase unused Zeros in decimal Format
+ *
+ * @param bd
+ * The decimal number
+ * @return The formatted number
+ */
+ static protected String eraseZero(BigDecimal bd)
+ {
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols();
+ dfs.setDecimalSeparator('.');
+ DecimalFormat df = new DecimalFormat("#####.################", dfs);
+ String st = df.format(bd);
+ return st;
+
+ }
+
+ /**
+ * Test if the number contained in st is an integer
+ *
+ * @param st
+ * The Object to convert
+ * @return The integer corresponding to st
+ * @throws LogoError
+ * If it isn't an integer
+ */
+
+ protected int getInteger(String st) throws LogoError
+ { // Si c'est un
+ // entier
+ try
+ {
+ return Integer.parseInt(st);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_entier"));
+ }
+ }
+
+ /**
+ * Test if the number contained in st is an integer
+ *
+ * @param st
+ * The Object to convert
+ * @return The integer corresponding to st
+ * @throws LogoError
+ * If it isn't an integer
+ */
+
+ protected BigDecimal getBigInteger(String st) throws LogoError
+ { // Si c'est un
+ // entier
+ try
+ {
+ return new BigDecimal(new BigInteger(st));
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_entier"));
+ }
+ }
+
+ protected int getDigits()
+ {
+ if (digits < 0)
+ return -1;
+ else
+ return mc.getPrecision();
+ }
+
+ public static String getOutputNumber(String s)
+ {
+ try
+ {
+ if (digits >= 0 && digits < 16)
+ {
+ BigDecimal bd = new BigDecimal(s);
+ s = bd.toPlainString();
+ // is it a decimal number?
+ int index = s.indexOf(".");
+ if (index != -1)
+ {
+ if (digits == 0)
+ return s.substring(0, index);
+ else if (s.length() > index + digits)
+ {
+ s = s.substring(0, index + digits + 1);
+ int a = Integer.parseInt(String.valueOf(s.charAt(s.length() - 1)));
+ if (a > 4)
+ {
+ a++;
+ s = s.substring(0, s.length() - 1) + a;
+ }
+ return s;
+ }
+ }
+ else
+ return s;
+ }
+ }
+ catch (NumberFormatException e)
+ {}
+ return s;
+ }
+}
diff --git a/logo/src/xlogo/kernel/MyFlow.java b/logo/src/xlogo/kernel/MyFlow.java
new file mode 100644
index 0000000..2c7b5e3
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyFlow.java
@@ -0,0 +1,110 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+public class MyFlow
+{
+
+ private String path;
+ private int id;
+ private boolean finished;
+
+ MyFlow(int id, String path, boolean finished)
+ {
+ this.id = id;
+ this.path = path;
+ this.finished = finished;
+ }
+
+ MyFlow(MyFlow flow)
+ {
+ this.id = flow.getId();
+ this.path = flow.getPath();
+ this.finished = flow.isFinished();
+ }
+
+ /**
+ * @return
+ */
+ String getPath()
+ {
+ return path;
+ }
+
+ /**
+ * @param p
+ */
+ void setPath(String p)
+ {
+ path = p;
+ }
+
+ /**
+ * @return
+ */
+ int getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param i
+ */
+ void setId(int i)
+ {
+ id = i;
+ }
+
+ /**
+ * @return
+ */
+ boolean isFinished()
+ {
+ return finished;
+ }
+
+ /**
+ * @param b
+ */
+ void setFinished(boolean b)
+ {
+ finished = b;
+ }
+
+ boolean isReader()
+ {
+ return false;
+ }
+
+ boolean isWriter()
+ {
+ return false;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/MyFlowReader.java b/logo/src/xlogo/kernel/MyFlowReader.java
new file mode 100644
index 0000000..2b3409b
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyFlowReader.java
@@ -0,0 +1,81 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class MyFlowReader extends MyFlow
+{
+ BufferedReader bfr;
+
+ boolean isReader()
+ {
+ return true;
+ }
+
+ MyFlowReader(MyFlow flow)
+ {
+ super(flow);
+ }
+
+ String readLine() throws FileNotFoundException, IOException
+ {
+ if (null == bfr)
+ bfr = new BufferedReader(new FileReader(getPath()));
+ String line = bfr.readLine();
+ return line;
+ }
+
+ int readChar() throws FileNotFoundException, IOException
+ {
+ if (null == bfr)
+ bfr = new BufferedReader(new FileReader(getPath()));
+ int character = bfr.read();
+ return character;
+ }
+
+ int isReadable() throws FileNotFoundException, IOException
+ {
+ if (null == bfr)
+ bfr = new BufferedReader(new FileReader(getPath()));
+ bfr.mark(2);
+ int id = bfr.read();
+ bfr.reset();
+ return id;
+ }
+
+ void close() throws IOException
+ {
+ if (null != bfr)
+ bfr.close();
+ }
+}
diff --git a/logo/src/xlogo/kernel/MyFlowWriter.java b/logo/src/xlogo/kernel/MyFlowWriter.java
new file mode 100644
index 0000000..7651aee
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyFlowWriter.java
@@ -0,0 +1,74 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import xlogo.utils.Utils;
+
+public class MyFlowWriter extends MyFlow
+{
+ BufferedWriter bfw;
+
+ MyFlowWriter(MyFlow flow)
+ {
+ super(flow);
+ }
+
+ void append(String line) throws FileNotFoundException, IOException
+ {
+ if (null == bfw)
+ bfw = new BufferedWriter(new FileWriter(getPath(), true));
+ PrintWriter pw = new PrintWriter(bfw);
+ pw.println(Utils.SortieTexte(line));
+ }
+
+ void write(String line) throws FileNotFoundException, IOException
+ {
+ if (null == bfw)
+ bfw = new BufferedWriter(new FileWriter(getPath()));
+ PrintWriter pw = new PrintWriter(bfw);
+ pw.println(Utils.SortieTexte(line));
+ }
+
+ boolean isWriter()
+ {
+ return true;
+ }
+
+ void close() throws IOException
+ {
+ if (null != bfw)
+ bfw.close();
+ }
+}
diff --git a/logo/src/xlogo/kernel/Primitive.java b/logo/src/xlogo/kernel/Primitive.java
new file mode 100644
index 0000000..0d0d8d1
--- /dev/null
+++ b/logo/src/xlogo/kernel/Primitive.java
@@ -0,0 +1,573 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo Description : XLogo is an interpreter for the Logo programming
+ * language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel;
+
+import java.util.Vector;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.math.BigDecimal;
+
+import xlogo.kernel.LoopProperties;
+import xlogo.messages.async.history.HistoryMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.Language;
+import xlogo.utils.Utils;
+import xlogo.Application;
+
+import java.io.*;
+
+import xlogo.Logo;
+
+import java.util.Enumeration;
+
+public class Primitive
+{
+ /**
+ * This character indicates the end of a procedure in instructionBuffer
+ */
+ protected static final String END_PROCEDURE = "\n";
+ /**
+ * This character indicates the end of a loop in instructionBuffer
+ */
+ protected static final String END_LOOP = "\\";
+
+ // float taille_crayon=(float)0;
+ private Application app;
+ protected static final int PRIMITIVE_NUMBER = 310;
+ protected static int[] parametres = new int[PRIMITIVE_NUMBER];
+ protected static boolean[] generalForm = new boolean[PRIMITIVE_NUMBER];
+
+ // Treemap for primitives (better efficiency in searching)
+ public static TreeMap<String, String> primitives = new TreeMap<String, String>();
+
+ public static Stack<LoopProperties> stackLoop = new Stack<LoopProperties>();
+
+ public Primitive()
+ {
+ }
+
+ public Primitive(Application app)
+ {
+ this.app = app;
+ // build treemap for primitives
+ buildPrimitiveTreemap(WSManager.getInstance().getWorkspaceConfigInstance().getLanguage());
+ }
+
+ /**
+ * This methods returns a list which contains all the primitive for the
+ * current language
+ *
+ * @return The primitives list
+ */
+ protected String getAllPrimitives()
+ {
+ Vector<String> list = new Vector<String>();
+ Locale locale = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage().getLocale();
+ ResourceBundle prim = ResourceBundle.getBundle("primitives", locale);
+ try
+ {
+ BufferedReader bfr = new BufferedReader(new InputStreamReader(
+ Primitive.class.getResourceAsStream("genericPrimitive")));
+ while (bfr.ready())
+ {
+ String line = bfr.readLine();
+ // read the generic keyword for the primitive
+ StringTokenizer cut = new StringTokenizer(line);
+ // read the standard number of arguments for the primitive
+ String cle = cut.nextToken();
+ // Exclude internal primitive \n \x and siwhile
+ if (!cle.equals("\\n") && !cle.equals("\\siwhile") && !cle.equals("\\x"))
+ {
+ // Exclude all arithmetic symbols + - / * & |
+ if (cle.length() != 1)
+ {
+ if (!cle.equals(">=") && !cle.equals("<="))
+ {
+ cle = prim.getString(cle).trim();
+ list.add(cle);
+ }
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ System.out.println("Impossible de lire le fichier d'initialisation des primitives");
+ }
+ Collections.sort(list);
+ StringBuffer sb = new StringBuffer("[ ");
+ for (int i = 0; i < list.size(); i++)
+ {
+ sb.append("[ ");
+ sb.append(list.get(i));
+ sb.append("] ");
+ }
+ sb.append("] ");
+ return (sb.toString());
+ }
+
+ // Exécution des primitives
+ public static void buildPrimitiveTreemap(Language lang)
+ {
+ // this.exportPrimCSV();
+ primitives = new TreeMap<String, String>();
+ Locale locale = lang.getLocale();
+ ResourceBundle prim = ResourceBundle.getBundle("primitives", locale);
+ try
+ {
+ BufferedReader bfr = new BufferedReader(new InputStreamReader(
+ Primitive.class.getResourceAsStream("genericPrimitive")));
+ int i = 0;
+ while (bfr.ready())
+ {
+ String line = bfr.readLine();
+ // read the generic keyword for the primitive
+ StringTokenizer cut = new StringTokenizer(line);
+ // read the standard number of arguments for the primitive
+ String cle = cut.nextToken();
+ parametres[i] = Integer.parseInt(cut.nextToken());
+ // Read if the primitive has a general form
+ // eg (sum 2 3 4 5) --> 14
+ // eg (list 3 4 5) ---> [3 4 5]
+ if (cut.hasMoreTokens())
+ {
+ generalForm[i] = true;
+ }
+ else
+ generalForm[i] = false;
+
+ if (i == 39)
+ primitives.put("\n", "39");
+ // Internal Primitive siwhile
+ else if (i == 95)
+ {
+ primitives.put("\\siwhile", "95");
+ }
+ // Internal Primitive \x
+ else if (i == 212)
+ primitives.put("\\x", "212");
+ else
+ {
+ if (cle.length() != 1)
+ {
+ if (!cle.equals(">=") && !cle.equals("<="))
+ cle = prim.getString(cle);
+ }
+ StringTokenizer st = new StringTokenizer(cle.toLowerCase());
+ int compteur = 0;
+ while (st.hasMoreTokens())
+ {
+ primitives.put(st.nextToken(), String.valueOf(i + Primitive.PRIMITIVE_NUMBER * compteur));
+ compteur++;
+ }
+ }
+ i++;
+ }
+ /*
+ * System.out.println(i+ " primitives");
+ * java.util.Iterator it=primitives.keySet().iterator();
+ * while(it.hasNext()){
+ * String next=it.next().toString();
+ * System.out.println(next+" "+primitives.get(next));
+ * }
+ */
+ }
+ catch (IOException e)
+ {
+ System.out.println("Impossible de lire le fichier d'initialisation des primitives");
+ }
+ }
+
+ /**
+ * This method creates the loop "repeat"
+ *
+ * @param i
+ * The number of iteration
+ * @param st
+ * The instruction to execute
+ * @throws LogoError
+ */
+ protected void repete(int i, String st) throws LogoError
+ {
+ if (i > 0)
+ {
+ st = new String(Utils.decoupe(st));
+ LoopProperties bp = new LoopRepeat(BigDecimal.ONE, new BigDecimal(i), BigDecimal.ONE, st);
+ stackLoop.push(bp);
+ app.getKernel().getInstructionBuffer().insert(st + "\\ ");
+ }
+ else if (i != 0) { throw new LogoError(Utils.primitiveName("controls.repete") + " "
+ + Logo.messages.getString("attend_positif")); }
+ }
+
+ /**
+ * This method is an internal primitive for the primitive "while"
+ *
+ * @param b
+ * Do we still execute the loop body?
+ * @param li
+ * The loop body instructions
+ * @throws LogoError
+ */
+ protected void whilesi(boolean b, String li) throws LogoError
+ {
+ if (b)
+ {
+ app.getKernel().getInstructionBuffer().insert(li + Primitive.stackLoop.peek().getInstr());
+ }
+ else
+ {
+ eraseLevelStop(app);
+ }
+ }
+
+ // primitive if
+ protected void si(boolean b, String li, String li2)
+ {
+ if (b)
+ {
+ app.getKernel().getInstructionBuffer().insert(li);
+ }
+ else if (null != li2)
+ {
+ app.getKernel().getInstructionBuffer().insert(li2);
+ }
+ }
+
+ // primitive stop
+ protected void stop() throws LogoError
+ {
+ Interprete.operande = false;
+ String car = "";
+ car = eraseLevelStop(app);
+
+ // A procedure has been stopped
+ if (car.equals("\n"))
+ {
+ String en_cours = Interprete.en_cours.pop();
+ Interprete.locale = Interprete.stockvariable.pop();
+ // Example: to bug
+ // fd stop
+ // end
+ // --------
+ // bug
+ // stop doesn't output to fd
+ if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n"))
+ {
+ // System.out.println(Interprete.nom);
+ throw new LogoError(Utils.primitiveName("controls.stop") + " "
+ + Logo.messages.getString("ne_renvoie_pas") + " " + Interprete.nom.peek());
+ }
+ else if (!Interprete.nom.isEmpty())
+ {
+ // Removing the character "\n"
+ Interprete.nom.pop();
+ // Example: to bug | to bug2
+ // fd bug2 | stop
+ // end | end
+ // ------------------------
+ // bug
+ // bug2 doesn't output to fd
+ if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n"))
+ {
+ // System.out.println(Interprete.nom);
+ throw new LogoError(en_cours + " " + Logo.messages.getString("ne_renvoie_pas") + " "
+ + Interprete.nom.peek());
+ }
+ }
+ }
+ }
+
+ // primitive output
+ protected void retourne(String val) throws LogoError
+ {
+ Interprete.calcul.push(val);
+ Interprete.operande = true;
+ if (Kernel.mode_trace)
+ {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < Interprete.en_cours.size() - 1; i++)
+ buffer.append(" ");
+ buffer.append(Interprete.en_cours.peek());
+ buffer.append(" " + Utils.primitiveName("ret") + " " + val);
+ HistoryMessenger.getInstance().dispatchMessage(Utils.SortieTexte(buffer.toString()) + "\n");
+ }
+ Interprete.en_cours.pop();
+ Interprete.locale = Interprete.stockvariable.pop();
+ if ((!Interprete.nom.isEmpty()) && Interprete.nom.peek().equals("\n"))
+ {
+ eraseLevelReturn(app);
+ Interprete.nom.pop();
+ }
+ else if (!Interprete.nom.isEmpty())
+ throw new LogoError(Utils.primitiveName("ret") + " " + Logo.messages.getString("ne_renvoie_pas") + " "
+ + Interprete.nom.peek());
+ else
+ throw new LogoError(Logo.messages.getString("erreur_retourne"));
+ }
+
+ /**
+ * This method deletes all instruction since it encounters the end of a loop
+ * or the end of a procedure
+ *
+ * @param app
+ * The runnning frame Application
+ * @return The specific character \n or \ if found
+ * @throws LogoError
+ */
+ private String eraseLevelStop(Application app) throws LogoError
+ {
+ boolean error = true;
+ String caractere = "";
+ int marqueur = 0;
+ InstructionBuffer instruction = app.getKernel().getInstructionBuffer();
+ for (int i = 0; i < instruction.getLength(); i++)
+ {
+ caractere = String.valueOf(instruction.charAt(i));
+ if (caractere.equals(Primitive.END_LOOP) | caractere.equals(Primitive.END_PROCEDURE))
+ {
+ marqueur = i;
+ if (caractere.equals(Primitive.END_LOOP) && i != instruction.getLength() - 1)
+ {
+ /*
+ * On test si le caractère "\" est bien un caractère de
+ * fin
+ * de boucle et non du style "\e" ou "\#"
+ */
+ if (instruction.charAt(i + 1) == ' ')
+ {
+ error = false;
+ break;
+ }
+ }
+ else
+ {
+ error = false;
+ break;
+ }
+ }
+ }
+
+ if (error) { throw new LogoError(Logo.messages.getString("erreur_stop")); }
+ if (marqueur + 2 > instruction.getLength())
+ instruction = new InstructionBuffer(" ");
+ else
+ instruction.delete(0, marqueur + 2);
+ if (!caractere.equals("\n"))
+ {
+ Primitive.stackLoop.pop();
+ }
+ return (caractere);
+ }
+
+ /**
+ * This method deletes all instruction since it encounters the end of a
+ * procedure
+ *
+ * @param app
+ * The running frame Application
+ * @return an integer that indicates the number of loop to delete from
+ * Primitive.stackLoop
+ * @throws LogoError
+ */
+ private void eraseLevelReturn(Application app) throws LogoError
+ {
+ boolean error = true;
+ String caractere = "";
+ int loopLevel = 0;
+ int marqueur = 0;
+ InstructionBuffer instruction = app.getKernel().getInstructionBuffer();
+ for (int i = 0; i < instruction.getLength(); i++)
+ {
+ caractere = String.valueOf(instruction.charAt(i));
+ if (caractere.equals(Primitive.END_PROCEDURE))
+ {
+ marqueur = i;
+ error = false;
+ break;
+ }
+ else if (caractere.equals(Primitive.END_LOOP))
+ {
+ /*
+ * On test si le caractère "\" est bien un caractère de fin
+ * de boucle et non du style "\e" ou "\#"
+ */
+ if (instruction.charAt(i + 1) == ' ')
+ {
+ loopLevel++;
+ }
+ }
+ }
+ if (error) { throw new LogoError(Logo.messages.getString("erreur_retourne")); }
+ if (marqueur + 2 > instruction.getLength())
+ instruction = new InstructionBuffer(" ");
+ else
+ instruction.delete(0, marqueur + 2);
+ for (int i = 0; i < loopLevel; i++)
+ {
+ Primitive.stackLoop.pop();
+ }
+ }
+
+ private void exportPrimCSV()
+ {
+ StringBuffer sb = new StringBuffer();
+ Locale locale = new Locale("fr", "FR");
+ ResourceBundle prim_fr = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("en", "US");
+ ResourceBundle prim_en = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("ar", "MA");
+ ResourceBundle prim_ar = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("es", "ES");
+ ResourceBundle prim_es = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("pt", "BR");
+ ResourceBundle prim_pt = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("eo", "EO");
+ ResourceBundle prim_eo = ResourceBundle.getBundle("primitives", locale);
+ locale = new Locale("de", "DE");
+ ResourceBundle prim_de = ResourceBundle.getBundle("primitives", locale);
+ // locale=new Locale("nl","NL");
+ ResourceBundle[] prim = { prim_fr, prim_en, prim_es, prim_pt, prim_ar, prim_eo, prim_de };
+ try
+ {
+ BufferedReader bfr = new BufferedReader(new InputStreamReader(
+ Primitive.class.getResourceAsStream("genericPrimitive")));
+ int i = 0;
+ while (bfr.ready())
+ {
+ String line = bfr.readLine();
+ StringTokenizer cut = new StringTokenizer(line);
+ String cle = cut.nextToken();
+ parametres[i] = Integer.parseInt(cut.nextToken());
+ if (i != 39 && i != 95 && i != 212 && cle.length() != 1)
+ {
+ sb.append("$");
+ sb.append(cle);
+ sb.append("$");
+ sb.append(";");
+ for (int j = 0; j < prim.length; j++)
+ {
+ String txt = cle;
+ txt = prim[j].getString(cle);
+ sb.append("$");
+ sb.append(txt);
+ sb.append("$");
+ sb.append(";");
+ }
+ sb.append("\n");
+ }
+ i++;
+ }
+ Utils.writeLogoFile("/home/loic/primTable.csv", new String(sb));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void exportMessageCSV()
+ {
+ StringBuffer sb = new StringBuffer();
+ Locale locale = new Locale("fr", "FR");
+ ResourceBundle lang_fr = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("en", "US");
+ ResourceBundle lang_en = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("ar", "MA");
+ ResourceBundle lang_ar = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("es", "ES");
+ ResourceBundle lang_es = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("pt", "BR");
+ ResourceBundle lang_pt = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("eo", "EO");
+ ResourceBundle lang_eo = ResourceBundle.getBundle("langage", locale);
+ locale = new Locale("de", "DE");
+ ResourceBundle lang_de = ResourceBundle.getBundle("langage", locale);
+ // locale=new Locale("nl","NL");
+ ResourceBundle[] lang = { lang_fr, lang_en, lang_es, lang_pt, lang_ar, lang_eo, lang_de };
+ try
+ {
+ Enumeration<String> en = lang_fr.getKeys();
+ while (en.hasMoreElements())
+ {
+ String cle = en.nextElement();
+ sb.append("$");
+ sb.append(cle);
+ sb.append("$");
+ sb.append(";");
+ for (int j = 0; j < lang.length; j++)
+ {
+ String txt = lang[j].getString(cle);
+ sb.append("$");
+ sb.append(txt);
+ sb.append("$");
+ if (j != lang.length - 1)
+ sb.append(";");
+ }
+
+ sb.append("\n");
+ }
+ Utils.writeLogoFile("/home/loic/messageTable.csv", new String(sb));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public static int isPrimitive(String s)
+ {
+ String index = Primitive.primitives.get(s.toLowerCase());
+ if (null == index)
+ return -1;
+ try
+ {
+ int id = Integer.parseInt(index);
+ return id % Primitive.PRIMITIVE_NUMBER;
+ }
+ catch (NumberFormatException e)
+ {
+ System.err.println("!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
+ + "The file xlogo/kernel/genericPrimitive has been corrupted...\n"
+ + " Please, check the integrity of xlogo.jar \n!!!!!!!!!!!!!!!!!!!!!!!!!");
+ }
+ return -1;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/Turtle.java b/logo/src/xlogo/kernel/Turtle.java
new file mode 100644
index 0000000..82f3a76
--- /dev/null
+++ b/logo/src/xlogo/kernel/Turtle.java
@@ -0,0 +1,434 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+package xlogo.kernel;
+
+import java.awt.Toolkit;
+import java.awt.MediaTracker;
+import java.awt.geom.GeneralPath;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.BasicStroke;
+import java.awt.Stroke;
+import java.util.StringTokenizer;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.PenShape;
+import xlogo.storage.user.UserConfig;
+import xlogo.utils.Utils;
+import xlogo.Application;
+
+public class Turtle
+{
+
+ private Application app;
+
+ public Color couleurcrayon = Color.black;
+ public Stroke stroke = new BasicStroke(1);
+
+ Color couleurmodedessin = Color.black;
+
+ public int id = -1;
+
+ BasicStroke crayon = null;
+
+ int police = 12;
+
+ private int labelHorizontalAlignment = 0;
+ protected static final int LABEL_HORIZONTAL_ALIGNMENT_LEFT = 0;
+ protected static final int LABEL_HORIZONTAL_ALIGNMENT_CENTER = 1;
+ protected static final int LABEL_HORIZONTAL_ALIGNMENT_RIGHT = 2;
+
+ private int labelVerticalAlignment = 0;
+ protected static final int LABEL_VERTICAL_ALIGNMENT_BOTTOM = 0;
+ protected static final int LABEL_VERTICAL_ALIGNMENT_CENTER = 1;
+ protected static final int LABEL_VERTICAL_ALIGNMENT_TOP = 2;
+
+ // Image for the turtle
+ // If null then draw the triangle
+
+ Image tort = null;
+
+ GeneralPath triangle;
+ /**
+ * The turtle heading (degree)
+ */
+ public double heading;
+ /**
+ * The turtle roll (degree)
+ */
+ public double roll;
+ /**
+ * The turtle pitch (degree)
+ */
+ public double pitch;
+
+ /**
+ * The X coordinates on the screen
+ */
+ public double corX;
+ /**
+ * The Y coordinates on the screen
+ */
+ public double corY;
+
+ public double angle;
+ /**
+ * The X coordinates in real World (3D or 2D)
+ */
+ public double X = 0;
+ /**
+ * The Y coordinates in real World (3D or 2D)
+ */
+ public double Y = 0;
+ /**
+ * The Z coordinates in real World (3D or 2D)
+ */
+ public double Z = 0;
+ /**
+ * Identity Matrix
+ */
+ private final double[][] identity = new double[3][3];
+ {
+ identity[0][0] = identity[1][1] = identity[2][2] = 1;
+ identity[0][1] = identity[0][2] = identity[1][2] = 0;
+ identity[1][0] = identity[2][1] = identity[2][0] = 0;
+ }
+ /**
+ * This is the rotation Matrix (3x3) in 3D world
+ */
+ private double[][] rotationMatrix = identity;
+
+ int largeur = 0;
+
+ int hauteur = 0;
+
+ int gabarit = 0;
+
+ private boolean pendown = true;
+
+ private boolean penReverse = false;
+
+ private boolean visible = true;
+
+ private int shape = WSManager.getUserConfig().getActiveTurtle();
+
+ private float penWidth = 0; // half
+ // of
+ // the
+ // pen
+ // width
+
+ public Turtle(Application app)
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ this.app = app;
+ fixe_taille_crayon(1);
+ String chemin = "tortue" + uc.getActiveTurtle() + ".png";
+ couleurcrayon = uc.getPencolor();
+ couleurmodedessin = uc.getPencolor();
+ if (uc.getActiveTurtle() == 0)
+ {
+ tort = null;
+ largeur = 26;
+ hauteur = 26;
+ }
+ else
+ {
+ // ON teste tout d'abord si le chemin est valide
+ if (null == Utils.class.getResource(chemin))
+ chemin = "tortue1.png";
+ tort = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(chemin));
+ MediaTracker tracker = new MediaTracker(app.getFrame());
+ tracker.addImage(tort, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException e1)
+ {}
+ largeur = tort.getWidth(app.getFrame());
+ hauteur = tort.getHeight(app.getFrame());
+ double largeur_ecran = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+ // On fait attention à la résolution de l'utilisateur
+ double facteur = largeur_ecran / 1024.0;
+ if ((int) (facteur + 0.001) != 1)
+ {
+ tort = tort.getScaledInstance((int) (facteur * largeur), (int) (facteur * hauteur), Image.SCALE_SMOOTH);
+ tracker = new MediaTracker(app.getFrame());
+ tracker.addImage(tort, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException e1)
+ {}
+ }
+ largeur = tort.getWidth(app.getFrame());
+ hauteur = tort.getHeight(app.getFrame());
+ }
+ gabarit = Math.max(hauteur, largeur);
+ corX = uc.getImageWidth() / 2;
+ corY = uc.getImageHeight() / 2;
+ angle = Math.PI / 2;
+ heading = 0.0;
+ pitch = 0;
+ roll = 0;
+ X = 0;
+ Y = 0;
+ Z = 0;
+ }
+
+ protected void init()
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ corX = uc.getImageWidth() / 2;
+ corY = uc.getImageHeight() / 2;
+ X = 0;
+ Y = 0;
+ Z = 0;
+ heading = 0;
+ pitch = 0;
+ roll = 0;
+ rotationMatrix = identity;
+ angle = Math.PI / 2;
+ pendown = true;
+ stroke = new BasicStroke(1);
+ fixe_taille_crayon(1);
+ couleurcrayon = uc.getPencolor();
+ couleurmodedessin = uc.getPencolor();
+ penReverse = false;
+ }
+
+ void drawTriangle()
+ {
+ if (null == tort)
+ {
+ if (null == triangle)
+ {
+ triangle = new GeneralPath();
+ }
+ else
+ triangle.reset();
+ if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D)
+ {
+ triangle.moveTo((float) (corX - 10.0 * Math.sin(angle)), (float) (corY - 10.0 * Math.cos(angle)));
+ triangle.lineTo((float) (corX + 24.0 * Math.cos(angle)), (float) (corY - 24.0 * Math.sin(angle)));
+ triangle.lineTo((float) (corX + 10.0 * Math.sin(angle)), (float) (corY + 10.0 * Math.cos(angle)));
+ triangle.lineTo((float) (corX - 10.0 * Math.sin(angle)), (float) (corY - 10.0 * Math.cos(angle)));
+ }
+ else
+ {
+ double[] screenCoord = new double[2];
+ // The triangle has coordinates: (-10,0,0);(0,24,0);(10,0,0)
+ double[] x1 = new double[3];
+ x1[0] = X - 20 * rotationMatrix[0][0];
+ x1[1] = Y - 20 * rotationMatrix[1][0];
+ x1[2] = Z - 20 * rotationMatrix[2][0];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.moveTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X + 48 * rotationMatrix[0][1];
+ x1[1] = Y + 48 * rotationMatrix[1][1];
+ x1[2] = Z + 48 * rotationMatrix[2][1];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X + 20 * rotationMatrix[0][0];
+ x1[1] = Y + 20 * rotationMatrix[1][0];
+ x1[2] = Z + 20 * rotationMatrix[2][0];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ triangle.closePath();
+
+ // the "aileron" has coordinates: (0,10,0);(0,0,10);(0,0,0)
+ x1[0] = X + 15 * rotationMatrix[0][1];
+ x1[1] = Y + 15 * rotationMatrix[1][1];
+ x1[2] = Z + 15 * rotationMatrix[2][1];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.moveTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X + 15 * rotationMatrix[0][2];
+ x1[1] = Y + 15 * rotationMatrix[1][2];
+ x1[2] = Z + 15 * rotationMatrix[2][2];
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ x1[0] = X;
+ x1[1] = Y;
+ x1[2] = Z;
+ screenCoord = app.getDrawPanel().toScreenCoord(x1, false);
+ triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]);
+ }
+ }
+ }
+
+ public void fixe_taille_crayon(float nb)
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ if (nb < 0)
+ nb = 1;
+ else if (uc.getMaxPenWidth() != -1 && nb > uc.getMaxPenWidth())
+ nb = 1;
+ if (uc.getPenShape() == PenShape.SQUARE)
+ crayon = new BasicStroke(nb, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER);
+ else
+ crayon = new BasicStroke(nb, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
+ penWidth = nb / 2;
+ }
+
+ public float getPenWidth()
+ {
+ return penWidth;
+ }
+
+ protected int getShape()
+ {
+ return shape;
+ }
+
+ public void setShape(int id)
+ {
+ shape = id;
+ }
+
+ protected boolean isVisible()
+ {
+ return visible;
+ }
+
+ protected void setVisible(boolean b)
+ {
+ visible = b;
+ }
+
+ protected boolean isPenDown()
+ {
+ return pendown;
+ }
+
+ protected void setPenDown(boolean b)
+ {
+ pendown = b;
+ }
+
+ protected boolean isPenReverse()
+ {
+ return penReverse;
+ }
+
+ protected void setPenReverse(boolean b)
+ {
+ penReverse = b;
+ }
+
+ protected void setRotationMatrix(double[][] m)
+ {
+ rotationMatrix = m;
+ }
+
+ protected double[][] getRotationMatrix()
+ {
+ return rotationMatrix;
+ }
+
+ protected double getX()
+ {
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D)
+ return X;
+ return corX - WSManager.getUserConfig().getImageWidth() / 2;
+
+ }
+
+ protected double getY()
+ {
+ if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D)
+ return Y;
+ return WSManager.getUserConfig().getImageHeight() / 2 - corY;
+
+ }
+
+ protected int getLabelHorizontalAlignment()
+ {
+ return labelHorizontalAlignment;
+ }
+
+ protected int getLabelVerticalAlignment()
+ {
+ return labelVerticalAlignment;
+ }
+
+ protected void setFontJustify(String list) throws LogoError
+ {
+ StringTokenizer st = new StringTokenizer(list);
+ int i = 0;
+ while (st.hasMoreTokens())
+ {
+ String s = st.nextToken();
+ try
+ {
+ int j = Integer.parseInt(s);
+ if (j < 0 || j > 2)
+ throw new LogoError(list + " " + Logo.messages.getString("pas_argument"));
+ else
+ {
+ if (i == 0)
+ labelHorizontalAlignment = j;
+ else if (i == 1)
+ labelVerticalAlignment = j;
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(list + " " + Logo.messages.getString("pas_argument"));
+ }
+
+ i++;
+ }
+ if (i != 2)
+ throw new LogoError(list + " " + Logo.messages.getString("pas_argument"));
+ }
+
+ public String getFontJustify()
+ {
+ StringBuffer sb = new StringBuffer("[ ");
+ sb.append(labelHorizontalAlignment);
+ sb.append(" ");
+ sb.append(labelVerticalAlignment);
+ sb.append(" ] ");
+ return new String(sb);
+ }
+}
diff --git a/logo/src/xlogo/kernel/genericPrimitive b/logo/src/xlogo/kernel/genericPrimitive
new file mode 100644
index 0000000..bec63b7
--- /dev/null
+++ b/logo/src/xlogo/kernel/genericPrimitive
@@ -0,0 +1,308 @@
+drawing.av 1
+drawing.re 1
+drawing.td 1
+drawing.tg 1
+arithmetic.puissance 2
+controls.repete 2
+ve 0
+ct 0
+mt 0
+ec 1 +
+si 2
+controls.stop 0
+drawing.origine 0
+drawing.fpos 1
+drawing.fixex 1
+drawing.fixey 1
+drawing.fixexy 2
+drawing.fixecap 1
+lc 0
+bc 0
+go 0
+ic 0
+de 0
+arithmetic.somme 2 +
+arithmetic.difference 2
+arithmetic.moins 1
+arithmetic.produit 2 +
+arithmetic.div 2
+arithmetic.reste 2
+ret 1
+* 1
+/ 1
++ 1
+- 1
+= 1
+< 1
+> 1
+| 1
+& 1
+\n 0
+\ 0
+pos 0
+cap 0
+arithmetic.arrondi 1
+arithmetic.log10 1
+arithmetic.sin 1
+arithmetic.cos 1
+ou 2 +
+et 2 +
+non 1
+liste 2 +
+ph 2 +
+mp 2
+md 2
+inverse 1
+choix 1
+enleve 2
+item 2
+sd 1
+sp 1
+dernier 1
+premier 1
+compte 1
+mot? 1
+nombre? 1
+liste? 1
+vide? 1
+egal? 2
+precede? 2
+membre? 2
+arithmetic.racine 1
+membre 2
+workspace.donne 2
+workspace.locale 1
+workspace.donnelocale 2
+fcc 1
+fcfg 1
+arithmetic.hasard 1
+attends 1
+workspace.imts 0
+workspace.efn 1
+workspace.efv 1
+workspace.efns 0
+mot 2 +
+drawing.etiquette 1
+tc 1
+fen 0
+enr 0
+clos 0
+vt 0
+ci 1
+ftc 1
+controls.tantque 2
+lis 2
+touche? 0
+\siwhile 2
+liscar 0
+remplis 0
+drawing.point 1
+vers 1
+distance 1
+cc 0
+cf 0
+bc? 0
+visible? 0
+prim? 1
+proc? 1
+workspace.exec 1
+cat 0
+frep 1
+rep 0
+sauve 2
+sauved 1
+ramene 1
+arithmetic.pi 0
+arithmetic.tan 1
+arithmetic.acos 1
+arithmetic.asin 1
+arithmetic.atan 1
+vrai 0
+faux 0
+turtle.forme 0
+turtle.fforme 1
+workspace.def 2
+tortue 0
+tortues 0
+ftortue 1
+police 0
+fpolice 1
+tuetortue 1
+seq 1
+instr 0
+finstr 1
+joue 0
+efseq 0
+indseq 0
+findseq 1
+fpt 1
+ptexte 0
+fct 1
+couleurtexte 0
+lissouris 0
+possouris 0
+message 1
+date 0
+heure 0
+temps 0
+debuttemps 1
+fintemps? 0
+fixenompolice 1
+nompolice 0
+fixenompolicetexte 1
+nompolicetexte 0
+listeflux 0
+lisligneflux 1
+liscarflux 1
+ecrisligneflux 2
+finflux? 1
+ouvreflux 2
+fermeflux 1
+ajouteligneflux 2
+souris? 0
+workspace.listevariables 0
+workspace.chose 1
+nettoie 0
+tape 1
+drawing.cercle 1
+drawing.arc 3
+rempliszone 0
+animation 0
+rafraichis 0
+tailleimage 0
+arithmetic.quotient 2
+entier? 1
+fixeseparation 1
+separation 0
+arithmetic.tronque 1
+workspace.trace 0
+changedossier 1
+unicode 1
+caractere 1
+controls.stoptout 0
+controls.compteur 0
+controls.repetepour 2
+arithmetic.absolue 1
+remplace 3
+ajoute 3
+color.gris 0
+color.grisclair 0
+color.rougefonce 0
+color.vertfonce 0
+color.bleufonce 0
+color.orange 0
+color.rose 0
+color.violet 0
+color.marron 0
+color.noir 0
+color.rouge 0
+color.vert 0
+color.jaune 0
+color.bleu 0
+color.magenta 0
+color.cyan 0
+color.blanc 0
+) 0
+fixestyle 1
+style 0
+taillefenetre 0
+drawing.longueuretiquette 1
+envoietcp 2
+ecoutetcp 0
+executetcp 2
+\x 0
+chattcp 2
+resetall 0
+penwidth 0
+setpenshape 1
+penshape 0
+setdrawingquality 1
+drawingquality 0
+setturtlesnumber 1
+turtlesnumber 0
+setscreensize 1
+guibutton 2
+guiaction 2
+guiremove 1
+guiposition 2
+guidraw 1
+setzoom 1
+grille 2
+stopgrille 0
+stopanimation 0
+workspace.stoptrace 0
+guimenu 2
+axis 1
+xaxis 1
+yaxis 1
+stopaxis 0
+bye 0
+var? 1
+axiscolor 0
+gridcolor 0
+grid? 0
+xaxis? 0
+yaxis? 0
+setgridcolor 1
+setaxiscolor 1
+3d.perspective 0
+3d.rightroll 1
+3d.uppitch 1
+3d.leftroll 1
+3d.downpitch 1
+3d.roll 0
+3d.pitch 0
+3d.setroll 1
+3d.setpitch 1
+3d.setorientation 1
+3d.orientation 0
+3d.setxyz 3
+3d.setz 1
+workspace.pprop 3
+workspace.gprop 2
+workspace.remprop 2
+workspace.plist 1
+3d.polystart 0
+3d.polyend 0
+3d.polyview 0
+3d.linestart 0
+3d.lineend 0
+3d.pointstart 0
+3d.pointend 0
+3d.textstart 0
+3d.textend 0
+<= 1
+>= 1
+workspace.primitives 0
+workspace.propertylists 0
+workspace.content 0
+workspace.erpl 1
+arithmetic.exp 1
+arithmetic.log 1
+controls.ifelse 3
+workspace.ed 1
+workspace.edall 0
+controls.foreach 3
+controls.forever 1
+arithmetic.setdigits 1
+arithmetic.digits 0
+workspace.text 1
+workspace.externalcommand 1
+drawing.saveimage 2
+sound.mp3play 1
+sound.mp3stop 0
+zoom 0
+drawing.x 0
+drawing.y 0
+drawing.z 0
+drawing.fillpolygon 1
+arithmetic.alea 0
+loop.dountil 2
+loop.dowhile 2
+aritmetic.modulo 2
+drawing.setfontjustify 1
+drawing.fontjustify 0
+arithmetic.inf 2
+arithmetic.sup 2
+arithmetic.infequal 2
+arithmetic.supequal 2 \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/grammar/LogoException.java b/logo/src/xlogo/kernel/grammar/LogoException.java
new file mode 100644
index 0000000..225a677
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoException.java
@@ -0,0 +1,69 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoException extends LogoType
+{
+
+ private String message;
+
+ public LogoException(String message)
+ {
+ this.message = message;
+ }
+
+ public boolean isException()
+ {
+ return true;
+ }
+
+ public String toString()
+ {
+ return message;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(EXCEPTION) " + message;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoList.java b/logo/src/xlogo/kernel/grammar/LogoList.java
new file mode 100644
index 0000000..1cd6ea5
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoList.java
@@ -0,0 +1,93 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+import java.util.Vector;
+
+public class LogoList extends LogoType
+{
+ private Vector<LogoType> vector;
+
+ LogoList(Vector<LogoType> vector)
+ {
+ this.vector = vector;
+ }
+
+ LogoList()
+ {
+ vector = new Vector<LogoType>();
+ }
+
+ public boolean isList()
+ {
+ return true;
+ }
+
+ public void add(LogoType type)
+ {
+ vector.add(type);
+ }
+
+ public Vector<LogoType> getVector()
+ {
+ return vector;
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ for (int i = 0; i < vector.size(); i++)
+ {
+ sb.append(vector.get(i).toString());
+ sb.append(" ");
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public String toDebug()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("(LIST) ");
+ sb.append(toString());
+ return sb.toString();
+
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoNumber.java b/logo/src/xlogo/kernel/grammar/LogoNumber.java
new file mode 100644
index 0000000..fd367bb
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoNumber.java
@@ -0,0 +1,76 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoNumber extends LogoType
+{
+ private double value;
+
+ LogoNumber(double value)
+ {
+ this.value = value;
+ }
+ public double getValue()
+ {
+ return value;
+ }
+
+ public boolean isWord()
+ {
+ return true;
+ }
+
+ public boolean isNumber()
+ {
+ return true;
+ }
+
+ public String toString()
+ {
+ return String.valueOf(value);
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(NUMBER) " + String.valueOf(value);
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoParser.java b/logo/src/xlogo/kernel/grammar/LogoParser.java
new file mode 100644
index 0000000..ccf3436
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoParser.java
@@ -0,0 +1,289 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+package xlogo.kernel.grammar;
+
+import xlogo.Logo;
+import xlogo.kernel.DrawPanel;
+import xlogo.kernel.Primitive;
+import xlogo.storage.global.GlobalConfig;
+
+public class LogoParser
+{
+ private char c;
+ private int cursor;
+ private String text;
+
+ /**
+ *
+ * @param sr
+ * The String input reader
+ */
+ public LogoParser(String text)
+ {
+ this.text = text;
+ cursor = 0;
+ while (cursor < text.length())
+ {
+ LogoType lt = getToken();
+ if (GlobalConfig.DEBUG)
+ System.out.println("[DEBUG] Token " + lt.toDebug() + " cursor " + cursor);
+ }
+ }
+
+ private LogoType getToken()
+ {
+ boolean isQuotedWord = false;
+ boolean isVariable = false;
+
+ StringBuffer sb = new StringBuffer();
+ boolean start = false;
+ boolean backslash = false;
+ do
+ {
+ c = text.charAt(cursor);
+ // Skip White Spaces
+ if (c == ' ' || c == '\t')
+ {
+ if (start)
+ break;
+ else
+ cursor++;
+ }
+ else
+ {
+ if (backslash)
+ {
+ if (c == ' ')
+ sb.append(" ");
+ else if (c == '#')
+ sb.append("#");
+ else if (c == '\\')
+ sb.append("\\");
+ else if (c == '(')
+ sb.append("(");
+ else if (c == ')')
+ sb.append(")");
+ else if (c == '[')
+ sb.append("[");
+ else if (c == ']')
+ sb.append("]");
+ else if (c == 'n')
+ sb.append("\n");
+ else
+ sb.append(c);
+ cursor++;
+ }
+ else
+ {
+ // If it's the first character, check for type
+ if (!start)
+ {
+ if (c == ':')
+ isVariable = true;
+ else if (c == '\"')
+ isQuotedWord = true;
+ }
+ if (c == '\\')
+ backslash = true;
+
+ else if (c == '[')
+ {
+ if (start)
+ break;
+ else
+ {
+ cursor++;
+ return extractList();
+ }
+ }
+ else if (c == '(')
+ {
+ if (start)
+ break;
+ }
+ else if (c == '*' || c == '/' || c == '+' || c == '-' || c == '|' || c == '&' || c == '=')
+ {
+ if (!isQuotedWord)
+ {
+ if (!start)
+ {
+ sb.append(c);
+ cursor++;
+ }
+ break;
+
+ }
+ else
+ cursor++;
+ }
+ else if (c == ')')
+ return new LogoException(Logo.messages.getString("parenthese_ouvrante"));
+ else if (c == ']')
+ return new LogoException(Logo.messages.getString("error.whattodo") + " ]");
+ else
+ {
+ sb.append(c);
+ cursor++;
+ }
+ }
+ start = true;
+ }
+ } while (cursor < text.length());
+ if (sb.length() == 0)
+ return DrawPanel.nullType;
+ else if (isQuotedWord)
+ return new LogoWord(sb.substring(1));
+ else if (isVariable)
+ return new LogoVariable(sb.substring(1));
+ try
+ {
+ double d = Double.parseDouble(sb.toString());
+ return new LogoNumber(d);
+ }
+ catch (NumberFormatException e)
+ {
+ int id = Primitive.isPrimitive(sb.toString());
+ if (id != -1) { return new LogoPrimitive(id, sb.toString()); }
+ return new LogoException(Logo.messages.getString("je_ne_sais_pas") + " " + sb.toString());
+ }
+
+ }
+
+ /**
+ * This method extracts a list.
+ *
+ * @return a LogoList if operation succeed,
+ * a LogoException otherwise
+ */
+
+ private LogoType extractList()
+ {
+ LogoList list = new LogoList();
+ while (cursor < text.length())
+ {
+ LogoType lt = getListToken();
+ if (lt.isNull())
+ {
+ return new LogoException(Logo.messages.getString("erreur_crochet"));
+ }
+ else if (lt.isRightDelimiter())
+ return list;
+ else
+ {
+ list.add(lt);
+ }
+ }
+ return new LogoException(Logo.messages.getString("erreur_crochet"));
+ }
+
+ private LogoType getListToken()
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean start = false;
+ boolean backslash = false;
+ for (int i = cursor; i < text.length(); i++)
+ {
+ cursor = i;
+ c = text.charAt(i);
+ // Skip White Spaces
+ if (c == ' ' || c == '\t')
+ {
+ if (start)
+ break;
+ }
+ else
+ {
+ if (backslash)
+ {
+ if (c == ' ')
+ sb.append(" ");
+ else if (c == '#')
+ sb.append("#");
+ else if (c == '\\')
+ sb.append("\\");
+ else if (c == '(')
+ sb.append("(");
+ else if (c == ')')
+ sb.append(")");
+ else if (c == '[')
+ sb.append("[");
+ else if (c == ']')
+ sb.append("]");
+ else if (c == 'n')
+ sb.append("\n");
+ else
+ sb.append(c);
+ }
+ else
+ {
+ if (c == '\\')
+ {
+ backslash = true;
+ }
+ else if (c == '[')
+ {
+ if (start)
+ break;
+ else
+ {
+ cursor++;
+ return extractList();
+ }
+ }
+ else if (c == ']')
+ {
+ if (start)
+ break;
+ else
+ {
+ System.out.println("coucou");
+ cursor++;
+ return new LogoRightDelimiter();
+ }
+ }
+ else
+ sb.append(c);
+ }
+ start = true;
+ }
+ }
+ if (sb.length() == 0)
+ return DrawPanel.nullType;
+ return new LogoWord(sb.toString());
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoPrimitive.java b/logo/src/xlogo/kernel/grammar/LogoPrimitive.java
new file mode 100644
index 0000000..52044a3
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoPrimitive.java
@@ -0,0 +1,74 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+public class LogoPrimitive extends LogoType
+{
+ private int id;
+ private String name;
+
+ LogoPrimitive(int id, String name)
+ {
+ this.id = id;
+ this.name = name;
+ }
+
+ public boolean isPrimitive()
+ {
+ return true;
+ }
+
+ /**
+ * @return
+ * @uml.property name="id"
+ */
+ public int getId()
+ {
+ return id;
+ }
+
+ public String toString()
+ {
+ return name;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(PRIMITIVE id=" + id + ") " + name;
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java b/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java
new file mode 100644
index 0000000..fb46026
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java
@@ -0,0 +1,56 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoRightDelimiter extends LogoType
+{
+ public boolean isRightDelimiter()
+ {
+ return true;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return null;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoTree.java b/logo/src/xlogo/kernel/grammar/LogoTree.java
new file mode 100644
index 0000000..4e6b66e
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoTree.java
@@ -0,0 +1,96 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.grammar;
+
+import java.util.Vector;
+
+public class LogoTree
+{
+ private Vector<LogoTree> children;
+ private LogoTree parent;
+ private LogoType value;
+ private boolean isRoot = false;
+ private boolean isProcedure = false;
+ private boolean isPrimitive = false;
+ private boolean isLeaf = false;
+
+ LogoTree()
+ {
+ children = new Vector<LogoTree>();
+ }
+
+ protected void setParent(LogoTree lt)
+ {
+ this.parent = lt;
+ }
+
+ protected LogoTree getParent()
+ {
+ return parent;
+ }
+
+ protected boolean isRoot()
+ {
+ return isRoot;
+ }
+
+ protected void addChild(LogoTree child)
+ {
+ children.add(child);
+ }
+
+ protected void setValue(LogoType value)
+ {
+ this.value = value;
+ }
+
+ protected LogoType getValue()
+ {
+ return value;
+ }
+
+ protected boolean isLeaf()
+ {
+ return isLeaf;
+ }
+
+ LogoType evaluate()
+ {
+ Vector<LogoType> args = new Vector<LogoType>();
+ for (int i = 0; i < children.size(); i++)
+ {
+ LogoTree child = children.get(i);
+ if (child.isLeaf())
+ args.add(child.getValue());
+ else
+ args.add(child.evaluate());
+ }
+ return null;
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoType.java b/logo/src/xlogo/kernel/grammar/LogoType.java
new file mode 100644
index 0000000..d407431
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoType.java
@@ -0,0 +1,129 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+
+package xlogo.kernel.grammar;
+
+public abstract class LogoType
+{
+
+ /**
+ * If this token is a word ?
+ *
+ * @return true for a word, false otherwise
+ */
+ public boolean isWord()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a list?
+ *
+ * @return true for a list, false otherwise
+ */
+ public boolean isList()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a number?
+ *
+ * @return true for a number, false otherwise
+ */
+ public boolean isNumber()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a variable?
+ *
+ * @return true for a variable, false otherwise
+ */
+ public boolean isVariable()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a primitive?
+ *
+ * @return true for a primitive, false otherwise
+ */
+ public boolean isPrimitive()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is a procedure?
+ *
+ * @return true for a procedure, false otherwise
+ */
+ public boolean isProcedure()
+ {
+ return false;
+ }
+
+ /**
+ * If this token is an exception?
+ *
+ * @return true for an exception, false otherwise
+ */
+ public boolean isException()
+ {
+ return false;
+ }
+
+ public boolean isNull()
+ {
+ return false;
+ }
+
+ public boolean isRightDelimiter()
+ {
+ return false;
+ }
+
+ /**
+ * Util for debugging
+ *
+ * @return the type and value for LogoType
+ */
+ abstract public String toDebug();
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoTypeNull.java b/logo/src/xlogo/kernel/grammar/LogoTypeNull.java
new file mode 100644
index 0000000..b764063
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoTypeNull.java
@@ -0,0 +1,56 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+/**
+ * @author loic
+ *
+ */
+public class LogoTypeNull extends LogoType
+{
+ public boolean isNull()
+ {
+ return true;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(LogoTypeNull)";
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoVariable.java b/logo/src/xlogo/kernel/grammar/LogoVariable.java
new file mode 100644
index 0000000..d2a7e91
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoVariable.java
@@ -0,0 +1,68 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.grammar;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+public class LogoVariable extends LogoType
+{
+ String name;
+
+ LogoVariable(String name)
+ {
+ this.name = name;
+ }
+
+ public boolean isVariable()
+ {
+ return true;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String toString()
+ {
+ return ":" + name;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(VARIABLE) :" + name;
+ }
+}
diff --git a/logo/src/xlogo/kernel/grammar/LogoWord.java b/logo/src/xlogo/kernel/grammar/LogoWord.java
new file mode 100644
index 0000000..e155180
--- /dev/null
+++ b/logo/src/xlogo/kernel/grammar/LogoWord.java
@@ -0,0 +1,68 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * Licence : GPL
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.grammar;
+
+public class LogoWord extends LogoType
+{
+ private String value;
+
+ LogoWord(String value)
+ {
+ this.value = value;
+ }
+
+ public String getQuotedValue()
+ {
+ return "\"" + value;
+ }
+
+ public String toString()
+ {
+ return value;
+ }
+
+ public boolean isWord()
+ {
+ return true;
+ }
+
+ @Override
+ public String toDebug()
+ {
+ return "(WORD) " + value;
+ }
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiButton.java b/logo/src/xlogo/kernel/gui/GuiButton.java
new file mode 100644
index 0000000..1d440a5
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiButton.java
@@ -0,0 +1,92 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import javax.swing.JButton;
+
+import xlogo.kernel.Interprete;
+
+import java.awt.Font;
+import java.awt.event.*;
+
+import xlogo.Application;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+
+public class GuiButton extends GuiComponent
+{
+
+ private StringBuffer action;
+
+ private Application app;
+
+ public GuiButton(String id, String text, Application app)
+ {
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ super.setId(id);
+ guiObject = new JButton(Utils.SortieTexte(text));
+ this.app = app;
+ java.awt.FontMetrics fm = app.getFrame().getGraphics().getFontMetrics(font);
+ originalWidth = fm.stringWidth(((JButton) (getGuiObject())).getText()) + 50;
+ originalHeight = font.getSize() + 10;
+ setSize(originalWidth, originalHeight);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ if (!app.commande_isEditable())
+ {
+ Interprete.actionInstruction.append(action);
+ }
+ else
+ {
+ app.startInterpretation(action);
+ }
+ }
+
+ public boolean isButton()
+ {
+ return true;
+ }
+
+ public boolean isMenu()
+ {
+ return false;
+ }
+
+ /**
+ * @param action
+ * @uml.property name="action"
+ */
+ public void setAction(StringBuffer action)
+ {
+ this.action = action;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiComponent.java b/logo/src/xlogo/kernel/gui/GuiComponent.java
new file mode 100644
index 0000000..c144432
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiComponent.java
@@ -0,0 +1,95 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.Point;
+import java.awt.event.ActionListener;
+import javax.swing.JComponent;
+
+public abstract class GuiComponent implements ActionListener
+{
+ public boolean hasAction = false;
+ protected int originalWidth;
+ protected int originalHeight;
+ private String id;
+ protected JComponent guiObject;
+ protected boolean actionFinished;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ protected void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public boolean hasAction()
+ {
+ return hasAction;
+ }
+
+ public JComponent getGuiObject()
+ {
+ return guiObject;
+ }
+
+ public void setLocation(int x, int y)
+ {
+ guiObject.setLocation(x, y);
+ }
+
+ public Point getLocation()
+ {
+ return guiObject.getLocation();
+ }
+
+ public void setSize(int width, int height)
+ {
+ guiObject.setSize(width, height);
+ }
+
+ public int getOriginalWidth()
+ {
+ return originalWidth;
+ }
+
+ public int getOriginalHeight()
+ {
+ return originalHeight;
+ }
+
+ public abstract void actionPerformed(ActionEvent e);
+
+ public abstract boolean isButton();
+
+ public abstract boolean isMenu();
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiMap.java b/logo/src/xlogo/kernel/gui/GuiMap.java
new file mode 100644
index 0000000..d2c071e
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiMap.java
@@ -0,0 +1,72 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import xlogo.kernel.LogoError;
+
+import java.util.HashMap;
+
+import xlogo.Logo;
+
+public class GuiMap extends HashMap<String, GuiComponent>
+{
+ private static final long serialVersionUID = 1L;
+
+ public GuiMap()
+ {
+ }
+
+ public void put(GuiComponent gc) throws LogoError
+ {
+ if (this.containsKey(gc.getId()))
+ {
+ throw new LogoError(Logo.messages.getString("gui_exists") + " " + gc.getId());
+ }
+ else
+ this.put(gc.getId(), gc);
+ }
+
+ public GuiComponent get(Object key)
+ {
+ String k = key.toString().toLowerCase();
+ return super.get(k);
+ }
+
+ /*
+ * public void remove(GuiComponent gc) throws myException{
+ * if (this.containsKey(gc.getId())){
+ * this.remove(gc.getId());
+ * }
+ * else{
+ * throw new
+ * myException(app,Logo.messages.getString("no_gui")+" "+gc.getId());
+ * }
+ * }
+ */
+}
diff --git a/logo/src/xlogo/kernel/gui/GuiMenu.java b/logo/src/xlogo/kernel/gui/GuiMenu.java
new file mode 100644
index 0000000..2a424f3
--- /dev/null
+++ b/logo/src/xlogo/kernel/gui/GuiMenu.java
@@ -0,0 +1,101 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.gui;
+
+import javax.swing.JComboBox;
+
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+
+import java.util.StringTokenizer;
+
+import xlogo.kernel.Interprete;
+
+import java.awt.event.*;
+
+import xlogo.Application;
+
+public class GuiMenu extends GuiComponent
+{
+ private Application app;
+ private String[] item;
+ private StringBuffer[] action;
+
+ public GuiMenu(String id, String text, Application app)
+ {
+ this.app = app;
+ setId(id);
+ StringTokenizer st = new StringTokenizer(text);
+ item = new String[st.countTokens()];
+ action = new StringBuffer[st.countTokens()];
+ int i = 0;
+ originalWidth = 0;
+ while (st.hasMoreTokens())
+ {
+ item[i] = Utils.SortieTexte(st.nextToken());
+ java.awt.FontMetrics fm = app.getFrame().getGraphics()
+ .getFontMetrics(WSManager.getWorkspaceConfig().getFont());
+ originalWidth = Math.max(originalWidth, fm.stringWidth(item[i]));
+ i++;
+ }
+ originalWidth += 50;
+ guiObject = new JComboBox(item);
+ originalHeight = WSManager.getWorkspaceConfig().getFont().getSize() + 10;
+ setSize(originalWidth, originalHeight);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ // System.out.println("coucou");
+ int select = ((JComboBox) guiObject).getSelectedIndex();
+ if (!app.commande_isEditable())
+ {
+ Interprete.actionInstruction.append(action[select]);
+ }
+ else
+ {
+ app.startInterpretation(action[select]);
+ }
+ }
+
+ public boolean isButton()
+ {
+ return false;
+ }
+
+ public boolean isMenu()
+ {
+ return true;
+ }
+
+ public void setAction(StringBuffer action, int id)
+ {
+ this.action[id] = action;
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/ChatFrame.java b/logo/src/xlogo/kernel/network/ChatFrame.java
new file mode 100644
index 0000000..227f4ca
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/ChatFrame.java
@@ -0,0 +1,160 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Toolkit;
+
+import javax.swing.BorderFactory;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.JTextField;
+
+import java.awt.event.*;
+import java.awt.Dimension;
+import java.io.PrintWriter;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+import java.awt.BorderLayout;
+
+import xlogo.Application;
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+import xlogo.Logo;
+
+public class ChatFrame extends JFrame implements ActionListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private MutableAttributeSet local;
+
+ private MutableAttributeSet distant;
+
+ private JTextPane textPane;
+
+ private DefaultStyledDocument dsd;
+
+ private JScrollPane scroll;
+
+ private JTextField textField;
+
+ private Application app;
+
+ private PrintWriter out;
+
+ protected ChatFrame(PrintWriter out, Application app)
+ {
+ this.app = app;
+ this.out = out;
+ initStyle();
+ initGui();
+ textField.addActionListener(this);
+ }
+
+ private void initStyle()
+ {
+ Font font = WSManager.getWorkspaceConfig().getFont();
+ local = new SimpleAttributeSet();
+ StyleConstants.setFontFamily(local, font.getFamily());
+ StyleConstants.setForeground(local, Color.black);
+ StyleConstants.setFontSize(local, font.getSize());
+
+ distant = new SimpleAttributeSet();
+ StyleConstants.setFontFamily(distant, font.getFamily());
+ StyleConstants.setForeground(distant, Color.RED);
+ StyleConstants.setFontSize(distant, font.getSize());
+ }
+
+ private void initGui()
+ {
+ setTitle(Logo.messages.getString("chat"));
+ setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png")));
+ dsd = new DefaultStyledDocument();
+ textPane = new JTextPane();
+ textPane.setDocument(dsd);
+ textPane.setEditable(false);
+ textPane.setBackground(new Color(255, 255, 220));
+ this.getContentPane().setLayout(new BorderLayout());
+ textField = new JTextField();
+ java.awt.FontMetrics fm = app.getFrame().getGraphics().getFontMetrics(WSManager.getWorkspaceConfig().getFont());
+ int width = fm.stringWidth(Logo.messages.getString("stop_chat")) + 30;
+ if (width < 200)
+ width = 200;
+
+ textPane.setPreferredSize(new Dimension(width, 300));
+ textField.setPreferredSize(new Dimension(width, WSManager.getWorkspaceConfig().getFont().getSize() + 10));
+ scroll = new JScrollPane(textPane);
+ getContentPane().add(scroll, BorderLayout.CENTER);
+ getContentPane().add(textField, BorderLayout.SOUTH);
+ textPane.setBorder(BorderFactory.createLineBorder(Color.BLUE, 3));
+ textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ pack();
+ setVisible(true);
+ }
+
+ protected void append(String sty, String st)
+ {
+ st += "\n";
+ try
+ {
+ if (sty.equals("local"))
+ dsd.insertString(dsd.getLength(), st, local);
+ if (sty.equals("distant"))
+ dsd.insertString(dsd.getLength(), ">" + st, distant);
+ textPane.setCaretPosition(dsd.getLength());
+
+ }
+ catch (BadLocationException e)
+ {}
+ }
+
+ protected void processWindowEvent(WindowEvent e)
+ {
+ if (e.getID() == WindowEvent.WINDOW_CLOSING)
+ {
+ out.println(NetworkServer.END_OF_FILE);
+ dispose();
+ }
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String txt = textField.getText();
+ append("local", txt);
+ textField.setText("");
+ out.println(txt);
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkClientChat.java b/logo/src/xlogo/kernel/network/NetworkClientChat.java
new file mode 100644
index 0000000..cf9ce0b
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkClientChat.java
@@ -0,0 +1,115 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+import xlogo.storage.WSManager;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+public class NetworkClientChat
+{
+ private InetAddress ip;
+ private String st;
+ private Application app;
+ private PrintWriter out;
+ private BufferedReader in;
+ private Socket socket;
+ ChatFrame cf;
+
+ public NetworkClientChat(Application app, String ip, String st) throws LogoError
+ {
+ this.app = app;
+ try
+ {
+ this.ip = InetAddress.getByName(ip);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + " " + ip);
+ }
+ this.st = st;
+ init();
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ socket = new Socket(ip, WSManager.getUserConfig().getTcpPort());
+ java.io.OutputStream os = socket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8"));
+ String cmd = NetworkServer.CHATTCP + "\n";
+ cmd += st + "\n";
+ cmd += NetworkServer.END_OF_FILE;
+ cf = new ChatFrame(out, app);
+ cf.append("local", st);
+ out.println(cmd);
+ while ((cmd = in.readLine()) != null)
+ {
+ if (cmd.equals(NetworkServer.END_OF_FILE))
+ {
+ cf.append("local", Logo.messages.getString("stop_chat"));
+ break;
+ }
+ cf.append("distant", cmd);
+ }
+ out.close();
+ in.close();
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress());
+ }
+
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkClientExecute.java b/logo/src/xlogo/kernel/network/NetworkClientExecute.java
new file mode 100644
index 0000000..22c3753
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkClientExecute.java
@@ -0,0 +1,114 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+
+import java.net.Socket;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.UnknownHostException;
+
+import xlogo.storage.WSManager;
+
+import java.net.InetAddress;
+
+import xlogo.Logo;
+import xlogo.kernel.Kernel;
+import xlogo.kernel.LogoError;
+
+public class NetworkClientExecute
+{
+ private InetAddress ip;
+ private String cmd;
+ private PrintWriter out;
+ private BufferedReader in;
+ private Socket socket;
+ private Kernel kernel;
+
+ public NetworkClientExecute(Kernel kernel, String ip, String cmd) throws LogoError
+ {
+ this.kernel = kernel;
+ try
+ {
+ this.ip = InetAddress.getByName(ip);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + " " + ip);
+ }
+ this.cmd = cmd;
+ init();
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ socket = new Socket(ip, WSManager.getUserConfig().getTcpPort());
+ java.io.OutputStream os = socket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ String wp = NetworkServer.EXECUTETCP + "\n";
+ wp += kernel.getWorkspace().getSerializedContext(); // Marko : changed
+ wp += NetworkServer.END_OF_FILE;
+ out.println(wp);
+ String input;
+ while ((input = in.readLine()) != null)
+ {
+ // System.out.println("je vais envoyer: OK");
+ if (input.equals("OK"))
+ {
+ // System.out.println("chargement reussi");
+ break;
+ }
+ }
+ out.println(cmd);
+ out.close();
+ in.close();
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress());
+ }
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkClientSend.java b/logo/src/xlogo/kernel/network/NetworkClientSend.java
new file mode 100644
index 0000000..2954436
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkClientSend.java
@@ -0,0 +1,100 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+import xlogo.storage.WSManager;
+
+public class NetworkClientSend
+{
+
+ private InetAddress ip;
+ private String data;
+ private BufferedReader in;
+ private PrintWriter out;
+ private Socket socket;
+ private String answer;
+
+ public NetworkClientSend(String ip, String data) throws LogoError
+ {
+ try
+ {
+ this.ip = InetAddress.getByName(ip);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + " " + ip);
+ }
+ this.data = data;
+ init();
+ }
+
+ /**
+ * @return
+ * @uml.property name="answer"
+ */
+ public String getAnswer()
+ {
+ return answer;
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ socket = new Socket(ip, WSManager.getUserConfig().getTcpPort());
+ in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ java.io.OutputStream os = socket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ out.println(data);
+ answer = "";
+ answer = in.readLine();
+ out.close();
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress());
+ }
+ }
+}
diff --git a/logo/src/xlogo/kernel/network/NetworkServer.java b/logo/src/xlogo/kernel/network/NetworkServer.java
new file mode 100644
index 0000000..f12b383
--- /dev/null
+++ b/logo/src/xlogo/kernel/network/NetworkServer.java
@@ -0,0 +1,194 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.network;
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import xlogo.storage.WSManager;
+import xlogo.Application;
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+import xlogo.kernel.Kernel;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.history.HistoryMessenger;
+
+public class NetworkServer
+{
+ public static boolean isActive;
+ private ServerSocket serverSocket;
+ private Application app;
+ private PrintWriter out;
+ private BufferedReader in;
+ private ChatFrame cf;
+
+ protected static final String EXECUTETCP = "executetcp" + "\u001B";
+ protected static final String CHATTCP = "chattcp" + "\u001B";
+ protected static final String END_OF_FILE = "*/EOF\\*" + "\u001B";
+ private Kernel kernel;
+
+ public NetworkServer(Application app) throws LogoError
+ {
+ isActive = true;
+ this.app = app;
+ this.kernel = app.getKernel();
+ init();
+
+ }
+
+ private void init() throws LogoError
+ {
+ try
+ {
+ serverSocket = new ServerSocket(WSManager.getUserConfig().getTcpPort());
+ }
+ catch (IOException e)
+ {
+ throw (new LogoError(Logo.messages.getString("pb_port") + WSManager.getUserConfig().getTcpPort()));
+ }
+ Socket clientSocket = null;
+ try
+ {
+ clientSocket = serverSocket.accept();
+ java.io.OutputStream os = clientSocket.getOutputStream();
+ BufferedOutputStream b = new BufferedOutputStream(os);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ out = new PrintWriter(osw, true);
+ in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF8"));
+ String inputLine = "";
+ inputLine = in.readLine();
+ // ******************* Executetcp **************************
+ if (inputLine.equals(NetworkServer.EXECUTETCP))
+ {
+ StringBuffer remoteNetworkContext = new StringBuffer();
+ while ((inputLine = in.readLine()) != null)
+ {
+ if (inputLine.equals(NetworkServer.END_OF_FILE))
+ break;
+ remoteNetworkContext.append(inputLine);
+ remoteNetworkContext.append("\n");
+ }
+
+ /*
+ * Marko Zivkovic : New implementation of this context switch
+ */
+
+ try
+ {
+ kernel.getWorkspace().pushNetworkMode(remoteNetworkContext.toString());
+ }
+ catch(Exception e)
+ {
+ throw new LogoError(Logo.messages.getString(MessageKeys.ERROR_NETWORK_CONTEXT));
+ }
+
+ // We say the workspace is fully created
+ out.println("OK");
+ // What is the command to execute?
+ inputLine = in.readLine();
+ // System.out.println("a exécuter: "+inputLine);
+ out.close();
+ in.close();
+ clientSocket.close();
+ serverSocket.close();
+
+ kernel.execute(new StringBuffer(inputLine + "\\x "));
+
+ }
+ // ******************* Chattcp **************************
+ else if (inputLine.equals(NetworkServer.CHATTCP))
+ {
+ String sentence = "";
+ while ((inputLine = in.readLine()) != null)
+ {
+ if (inputLine.equals(NetworkServer.END_OF_FILE))
+ break;
+ sentence = inputLine;
+ }
+ cf = new ChatFrame(out, app);
+ cf.append("distant", sentence);
+ while ((sentence = in.readLine()) != null)
+ {
+ if (sentence.equals(NetworkServer.END_OF_FILE))
+ {
+ cf.append("local", Logo.messages.getString("stop_chat"));
+ break;
+ }
+ cf.append("distant", sentence);
+ }
+ out.close();
+ in.close();
+ clientSocket.close();
+ serverSocket.close();
+ }
+ // ******************* Envoietcp **************************
+ else
+ {
+ HistoryMessenger.getInstance().dispatchMessage(inputLine);
+ out.println(Logo.messages.getString("pref.ok"));
+ out.close();
+ in.close();
+ clientSocket.close();
+ serverSocket.close();
+
+ }
+ }
+ catch (IOException e)
+ {
+ throw new LogoError(Logo.messages.getString("erreur_tcp"));
+ }
+ isActive = false;
+ }
+
+ public static void stopServer()
+ {
+ String st = NetworkServer.EXECUTETCP + "\n" + NetworkServer.END_OF_FILE + "\n\n";
+ try
+ {
+ Socket socket = new Socket("127.0.0.1", WSManager.getUserConfig().getTcpPort());
+ PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
+ out.println(st);
+ }
+ catch (IOException e)
+ {}
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/Conic.java b/logo/src/xlogo/kernel/perspective/Conic.java
new file mode 100644
index 0000000..6963b57
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/Conic.java
@@ -0,0 +1,214 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.math.BigDecimal;
+
+import xlogo.kernel.DrawPanel;
+
+/**
+ * @author Marko : This is now decoupled from Application
+ *
+ */
+public class Conic
+{
+ private DrawPanel dp;
+ BigDecimal a;
+ BigDecimal b;
+ BigDecimal c;
+ BigDecimal d;
+ BigDecimal e;
+ BigDecimal f;
+ BigDecimal deux = new BigDecimal(2);
+ private final BigDecimal un = BigDecimal.ONE;
+ private final BigDecimal zero = BigDecimal.ZERO;
+ BigDecimal[][] A = new BigDecimal[2][2];
+ private BigDecimal[] eigenValue = new BigDecimal[2];
+ private BigDecimal[][] eigenVect = new BigDecimal[2][2];
+ private BigDecimal[] center = new BigDecimal[2];
+ private double halfXAxis;
+ private double halfYAxis;
+
+ public Conic(DrawPanel dp, BigDecimal[] v)
+ {
+ this.dp = dp;
+ this.a = v[0];
+ this.b = v[1];
+ this.c = v[2];
+ this.d = v[3];
+ this.e = v[4];
+ this.f = v[5];
+ A[0][0] = a;
+ A[1][1] = c;
+ // b/2
+ A[0][1] = b.divide(deux);
+ A[1][0] = b.divide(deux);
+ drawConic();
+ }
+
+ private void drawConic()
+ {
+ calculateEigenVect();
+ // ellipse
+ BigDecimal det = det(A);
+ if (det.compareTo(zero) == 1)
+ {
+ // Looking for center
+ calculateCenter();
+ // The main equation becomes with the center as Origin:
+ // aX^2+bXY+cY^2+omega=0
+
+ // double omega=a*Math.pow(center[0],2)+c*Math.pow(center[1],2)
+ // +b*center[0]*center[1]+d*center[0]+e*center[1]+f;
+
+ BigDecimal omega = a.multiply(center[0].pow(2)).add(c.multiply(center[1].pow(2)))
+ .add(center[0].multiply(center[1]).multiply(b)).add(d.multiply(center[0]))
+ .add(e.multiply(center[1])).add(f);
+
+ // We apply the rotation with the Eigen Vect Matrix
+ // Now the equation becomes: lambda*X^2+mu*Y^2+omega=0
+ // with lambda and mu the eigen Values
+ // 1/sqrt(eigenValue[0]/-omega)
+ halfXAxis = 1 / Math.sqrt(eigenValue[0].divide(omega.negate(), 20, BigDecimal.ROUND_HALF_EVEN)
+ .doubleValue());
+ // 1/sqrt(eigenValue[1]/-omega)
+ halfYAxis = 1 / Math.sqrt(eigenValue[1].divide(omega.negate(), 20, BigDecimal.ROUND_HALF_EVEN)
+ .doubleValue());
+ double angle = Math.atan(eigenVect[1][0].divide(eigenVect[0][0], 20, BigDecimal.ROUND_HALF_EVEN)
+ .doubleValue());
+ // System.out.println(toString());
+ dp.drawEllipseArc(halfXAxis, halfYAxis, angle, center[0].doubleValue(),
+ center[1].doubleValue(), 0, 360);
+
+ }
+ // hyperbola
+ else if (det.compareTo(zero) == -1)
+ {
+ calculateCenter();
+
+ }
+ // parabola
+ else
+ {}
+ }
+
+ private void calculateEigenValue()
+ {
+ // Polynom: acX^2-(a+c)X+A[0][1]^2
+ // tmp=a+c
+ BigDecimal tmp = A[0][0].add(A[1][1]);
+ // Discriminant delta=(a-c)^2+4*A[0][1]^2
+ BigDecimal delta = A[0][0].subtract(A[1][1]).pow(2).add(new BigDecimal(4).multiply(A[0][1].pow(2)));
+ // calculate the eigen Values
+ eigenValue[0] = tmp.subtract(sqrt(delta)).divide(deux);
+ eigenValue[1] = tmp.add(sqrt(delta)).divide(deux);
+ }
+
+ private void calculateEigenVect()
+ {
+ if (A[0][1].compareTo(zero) == 0)
+ {
+ eigenValue[0] = A[0][0];
+ eigenValue[1] = A[1][1];
+ eigenVect[0][0] = un;
+ eigenVect[1][0] = zero;
+ eigenVect[0][1] = zero;
+ eigenVect[1][1] = un;
+ }
+ else
+ {
+ calculateEigenValue();
+ BigDecimal tmp = A[0][0].subtract(eigenValue[0]);
+ eigenVect[0][0] = un;
+ // System.out.println("pb: "+tmp+"\n"+A[0][1]);
+ eigenVect[1][0] = tmp.negate().divide(A[0][1], 20, BigDecimal.ROUND_HALF_EVEN);
+ // vector length and then normalize
+ tmp = sqrt(eigenVect[0][0].pow(2).add(eigenVect[1][0].pow(2)));
+ eigenVect[0][0] = eigenVect[0][0].divide(tmp, 20, BigDecimal.ROUND_HALF_EVEN);
+ eigenVect[1][0] = eigenVect[1][0].divide(tmp, 20, BigDecimal.ROUND_HALF_EVEN);
+ eigenVect[0][1] = eigenVect[1][0].negate();
+ eigenVect[1][1] = un;
+
+ }
+ }
+
+ private void calculateCenter()
+ {
+ // System determinant
+ // df/dx=0 and df/dy=0
+
+ // double det=4*a*c-Math.pow(b,2);
+ BigDecimal det = new BigDecimal(4).multiply(a).multiply(c).subtract(b.pow(2));
+ // xCenter=(-2*c*d+b*e)/det;
+ center[0] = (deux.negate().multiply(c).multiply(d).add(b.multiply(e))).divide(det, 20,
+ BigDecimal.ROUND_HALF_EVEN);
+ // yCenter=(-2*a*e+b*d)/det;
+ center[1] = (deux.negate().multiply(a).multiply(e).add(b.multiply(d))).divide(det, 20,
+ BigDecimal.ROUND_HALF_EVEN);
+ }
+
+ private BigDecimal det(BigDecimal[][] M)
+ {
+ return M[0][0].multiply(M[1][1]).subtract(M[0][1].multiply(M[1][0]));
+ }
+
+ public String toString()
+ {
+ String st = "centre: " + center[0] + " " + center[1] + "\n" + "Valeurs propres: " + eigenValue[0] + " "
+ + eigenValue[1] + "\n" + "Vecteurs propres:\n" + eigenVect[0][0] + " " + eigenVect[0][1] + "\n"
+ + eigenVect[1][0] + " " + eigenVect[1][1] + "\n" + "Demi Axe: X " + halfXAxis + " Y " + halfYAxis;
+ return st;
+ }
+
+ private BigDecimal sqrt(BigDecimal b)
+ {
+ BigDecimal precision = new BigDecimal(0.00000000001);
+ int scale = precision.scale();
+ if (b.scale() < scale)
+ {
+ b = b.setScale(scale);
+ }
+ // Initial guess
+ // G = A/2
+ BigDecimal g = new BigDecimal(Math.sqrt(b.doubleValue()));
+
+ // Iterate until we're done
+ while (true)
+ {
+ // G* = ((A/G)+G)/2
+ BigDecimal gStar = b.divide(g, BigDecimal.ROUND_HALF_EVEN).add(g).divide(deux, BigDecimal.ROUND_HALF_EVEN);
+ BigDecimal delta = gStar.subtract(g);
+ delta = delta.abs();
+ if (delta.compareTo(precision) < 0)
+ break;
+ g = gStar;
+ }
+ return g;
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/Element3D.java b/logo/src/xlogo/kernel/perspective/Element3D.java
new file mode 100644
index 0000000..d3473cf
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/Element3D.java
@@ -0,0 +1,115 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Color;
+import java.util.Vector;
+
+import javax.vecmath.Point3d;
+
+import xlogo.kernel.LogoError;
+
+/**
+ * @author Marko - I decoupled this and its subclasses from Application
+ *
+ */
+abstract public class Element3D
+{
+ protected Viewer3D v3d;
+
+ /**
+ * Color for each vertex
+ *
+ * @uml.property name="color"
+ * @uml.associationEnd multiplicity="(0 -1)" elementType="java.awt.Color"
+ */
+ protected Vector<Color> color;
+
+ /**
+ * Vertex coordinates in virtual world
+ *
+ * @uml.property name="vertex"
+ * @uml.associationEnd multiplicity="(0 -1)"
+ * elementType="javax.vecmath.Point3d"
+ */
+ protected Vector<Point3d> vertex;
+
+ public Element3D(Viewer3D v3d)
+ {
+ this.v3d = v3d;
+ vertex = new Vector<Point3d>();
+ color = new Vector<Color>();
+
+ }
+
+ public void addVertex(Point3d p, Color c)
+ {
+ vertex.add(p);
+ color.add(c);
+ }
+
+ /**
+ * This method calculates all attributes for polygon and add it in the 3D
+ * Viewer
+ */
+ abstract public void addToScene() throws LogoError;
+
+ /**
+ * This method indicates if the Element3D is a Point.
+ *
+ * @return true if this Element3D is a Point, false otherwise
+ */
+ abstract public boolean isPoint();
+
+ /**
+ * This method indicates if the Element3D is a Polygon.
+ *
+ * @return true if this Element3D is a Polygon, false otherwise
+ */
+ abstract public boolean isPolygon();
+
+ /**
+ * This method indicates if the Element3D is a line.
+ *
+ * @return true if this Element3D is a line, false otherwise
+ */
+ abstract public boolean isLine();
+
+ /**
+ * This method indicates if the Element3D is a Text2D.
+ *
+ * @return true if this Element3D is a Text2D, false otherwise
+ */
+ abstract public boolean isText();
+
+ public int getVertexCount()
+ {
+ return vertex.size();
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/ElementLine.java b/logo/src/xlogo/kernel/perspective/ElementLine.java
new file mode 100644
index 0000000..5aa0a8a
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/ElementLine.java
@@ -0,0 +1,374 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.LineStripArray;
+import javax.media.j3d.Material;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.LineAttributes;
+import javax.media.j3d.TransformGroup;
+
+import com.sun.j3d.utils.geometry.Sphere;
+
+import javax.media.j3d.Transform3D;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.AxisAngle4d;
+
+import com.sun.j3d.utils.geometry.Cylinder;
+
+import javax.vecmath.Vector3d;
+
+import xlogo.kernel.LogoError;
+
+/**
+ *
+ * @author Marko Zivkovic - I decoupled this from Application
+ *
+ */
+public class ElementLine extends Element3D
+{
+ /**
+ * This float stores the line Width for each 3D line
+ *
+ * @uml.property name="lineWidth"
+ */
+ private float lineWidth;
+
+ public ElementLine(Viewer3D v3d, float lineWidth)
+ {
+ super(v3d);
+ this.lineWidth = lineWidth; //app.getKernel().getActiveTurtle().getPenWidth();
+ }
+
+ public void addToScene() throws LogoError
+ {
+ int size = vertex.size();
+ if (size > 1)
+ {
+ if (lineWidth == 0.5)
+ createSimpleLine(size);
+ else
+ createComplexLine(size);
+ }
+ }
+
+ /**
+ * This method draws a line with width 1
+ *
+ * @param size
+ */
+ private void createSimpleLine(int size)
+ {
+ int[] tab = { size };
+ LineStripArray line = new LineStripArray(size, LineStripArray.COORDINATES | LineStripArray.COLOR_3, tab);
+ for (int i = 0; i < size; i++)
+ {
+ line.setCoordinate(i, vertex.get(i));
+ // System.out.println("sommet "+(2*i-1)+" "+vertex.get(i).x+" "+vertex.get(i).y+" "+vertex.get(i).z+" ");
+ line.setColor(i, new Color3f(color.get(i)));
+ }
+ Shape3D s = new Shape3D(line);
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0f, 0f), new Color3f(1f, 1.0f,
+ 1.0f), new Color3f(1f, 1f, 1f), 64);
+ appear.setMaterial(mat);
+ appear.setLineAttributes(new LineAttributes(2 * lineWidth, LineAttributes.PATTERN_SOLID, false));
+ s.setAppearance(appear);
+ // DrawPanel.listPoly.add(s);
+ v3d.add3DObject(s);
+ /*
+ * for (int i=0;i<line.getVertexCount();i++){
+ * double[] d=new double[3];
+ * line.getCoordinate(i, d);
+ * for(int j=0;j<d.length;j++){
+ * System.out.println(i+" "+d[j]);
+ * }
+ * }
+ */
+ }
+
+ /**
+ * This method draws a sequence of lines with a width different from 1
+ * Draws a cylinder around the line
+ *
+ * @param size
+ */
+
+ private void createComplexLine(int size)
+ {
+ for (int i = 0; i < size - 1; i++)
+ {
+ createLine(vertex.get(i), vertex.get(i + 1), new Color3f(color.get(i + 1)));
+ }
+
+ }
+
+ /**
+ * This method creates an elementary line with a width different from 1
+ * It draws a cylinder around the line and two spheres on each extremities
+ *
+ * @param p1
+ * the first point
+ * @param p2
+ * the second point
+ * @param color
+ * the sphere color
+ */
+ private void createLine(Point3d p1, Point3d p2, Color3f color)
+ {
+ // create a new CylinderTransformer between the two atoms
+ CylinderTransformer cT = new CylinderTransformer(p1, p2);
+
+ // get the length
+ double length = cT.getLength();
+ float radius = lineWidth / 1000;
+ // holds the translation
+ Transform3D transform1 = new Transform3D();
+ // ...move the coordinates there
+ transform1.setTranslation(cT.getTranslation());
+
+ // get the axis and angle for rotation
+ AxisAngle4d rotation = cT.getAxisAngle();
+ Transform3D transform2 = new Transform3D();
+ transform2.setRotation(rotation);
+
+ // combine the translation and rotation into transform1
+ transform1.mul(transform2);
+
+ // create a new Cylinder
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), color, new Color3f(1f, 1.0f, 1.0f), new Color3f(1f,
+ 1f, 1f), 64);
+ appear.setMaterial(mat);
+ PolygonAttributes pa = new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+ Cylinder cyl = new Cylinder(radius, (float) length, appear);
+
+ // and add it to the TransformGroup
+ TransformGroup tg = new TransformGroup();
+ tg.setTransform(transform1);
+ tg.addChild(cyl);
+ v3d.add2DText(tg);
+
+ // Add Sphere to the line extremities.
+ Sphere sphere1 = new Sphere(radius, appear);
+ TransformGroup tg2 = new TransformGroup();
+ Transform3D transform3 = new Transform3D();
+ transform3.setTranslation(new Vector3d(p1));
+ tg2.setTransform(transform3);
+ tg2.addChild(sphere1);
+ v3d.add2DText(tg2);
+
+ Sphere sphere2 = new Sphere(radius, appear);
+ TransformGroup tg3 = new TransformGroup();
+ Transform3D transform4 = new Transform3D();
+ transform4.setTranslation(new Vector3d(p2));
+ tg3.setTransform(transform4);
+ tg3.addChild(sphere2);
+ v3d.add2DText(tg3);
+
+ }
+
+ public boolean isLine()
+ {
+ return true;
+ }
+
+ public boolean isPoint()
+ {
+ return false;
+ }
+
+ public boolean isPolygon()
+ {
+ return false;
+ }
+
+ public boolean isText()
+ {
+ return false;
+ }
+
+ /**
+ * A class to calculate the length and transformations necessary to produce
+ * a cylinder to connect two points. Useful for Java3D and VRML where a
+ * cylinder object is created aligned along the y-axis.
+ *
+ * @author Alastair Hill
+ */
+ class CylinderTransformer
+ {
+
+ /** point A */
+ private final Point3d pointA;
+ /** point B */
+ private final Point3d pointB;
+ /**
+ * the angle through which to rotate the cylinder
+ */
+ private double angle;
+ /**
+ * the axis around which to rotate the cylinder
+ *
+ */
+ private Vector3d axis;
+ /**
+ * The translation required to translate the cylinder to the midpoint of
+ * the two points
+ */
+ private Vector3d translation;
+ /**
+ * The length of the cylinder required to join the two points
+ *
+ */
+ private double length;
+
+ /**
+ * Creates a new instance of CylinderTransformer
+ *
+ * @param a
+ * point a
+ * @param b
+ * point b
+ */
+ CylinderTransformer(Point3d a, Point3d b)
+ {
+ pointA = a;
+ pointB = b;
+
+ // carry out the calculations
+ doCalculations();
+ }
+
+ /**
+ * Carries out the necessary calculations so that values may be returned
+ */
+ private void doCalculations()
+ {
+ length = pointA.distance(pointB);
+
+ double[] arrayA = new double[3];
+ pointA.get(arrayA);
+ double[] arrayB = new double[3];
+ pointB.get(arrayB);
+ double[] arrayMid = new double[3];
+
+ for (int i = 0; i < arrayA.length; i++)
+ {
+ arrayMid[i] = (arrayA[i] + arrayB[i]) / 2f;
+ }
+
+ // the translation needed is
+ translation = new Vector3d(arrayMid);
+
+ // the initial orientation of the bond is in the y axis
+ Vector3d init = new Vector3d(0.0f, 1.0f, 0.0f);
+
+ // the vector needed is the same as that from a to b
+ Vector3d needed = new Vector3d(pointB.x - pointA.x, pointB.y - pointA.y, pointB.z - pointA.z);
+
+ // so the angle to rotate the bond by is:
+ angle = needed.angle(init);
+
+ // and the axis to rotate by is the cross product of the initial and
+ // needed vectors - ie the vector orthogonal to them both
+ axis = new Vector3d();
+ axis.cross(init, needed);
+ }
+
+ /**
+ * Returns the angle (in radians) through which to rotate the cylinder
+ *
+ * @return the angle.
+ */
+ double getAngle()
+ {
+ return angle;
+ }
+
+ /**
+ * The axis around which the cylinder must be rotated
+ *
+ * @return the axis
+ */
+ Vector3d getAxis()
+ {
+ return axis;
+ }
+
+ /**
+ * The length required for the cylinder to join the two points
+ *
+ * @return the length
+ */
+ double getLength()
+ {
+ return length;
+ }
+
+ /**
+ * The translation required to move the cylinder to the midpoint of the
+ * two points
+ *
+ * @return the translation
+ */
+ Vector3d getTranslation()
+ {
+ return translation;
+ }
+
+ /**
+ * Generates a (pretty) string representation of the the
+ * CylinderTransformer
+ *
+ * @return the string representation
+ */
+ public String toString()
+ {
+ return "tr: " + translation + ", ax: " + axis + ", an: " + angle + ", le: " + length;
+ }
+
+ /**
+ * Generates the required axis and angle combined into an AxisAngle4f
+ * object
+ *
+ * @return the axis and angle
+ */
+ public AxisAngle4d getAxisAngle()
+ {
+ return new AxisAngle4d(axis.x, axis.y, axis.z, angle);
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/ElementPoint.java b/logo/src/xlogo/kernel/perspective/ElementPoint.java
new file mode 100644
index 0000000..c77011a
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/ElementPoint.java
@@ -0,0 +1,129 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Material;
+import javax.media.j3d.PointArray;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3d;
+import javax.vecmath.Color3f;
+import javax.vecmath.Vector3d;
+
+import xlogo.kernel.LogoError;
+
+import javax.media.j3d.Shape3D;
+
+import com.sun.j3d.utils.geometry.Sphere;
+
+/**
+ * @author Marko - I decoupled this from Application
+ */
+public class ElementPoint extends Element3D
+{
+ float pointWidth;
+
+ public ElementPoint(Viewer3D v3d, float pointWidth)
+ {
+ super(v3d);
+ this.pointWidth = pointWidth; //app.getKernel().getActiveTurtle().getPenWidth();
+ }
+
+ public void addToScene() throws LogoError
+ {
+ if (vertex.size() == 0)
+ return;
+ if (pointWidth == 0.5)
+ {
+ int[] tab = new int[1];
+ tab[0] = vertex.size();
+ PointArray point = new PointArray(vertex.size(), PointArray.COORDINATES | PointArray.COLOR_3);
+ for (int i = 0; i < vertex.size(); i++)
+ {
+ point.setCoordinate(i, vertex.get(i));
+ point.setColor(i, new Color3f(color.get(i)));
+ }
+ v3d.add3DObject(new Shape3D(point));
+ }
+ else
+ {
+ for (int i = 0; i < vertex.size(); i++)
+ {
+ createBigPoint(vertex.get(i), new Color3f(color.get(i)));
+ }
+
+ }
+ }
+
+ private void createBigPoint(Point3d p, Color3f color)
+ {
+ // Add a Sphere to main 3D scene.
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), color,// new
+ // Color3f(0.0f,0f,0f),
+ new Color3f(1f, 1.0f, 1.0f), new Color3f(1f, 1f, 1f), 64);
+ appear.setMaterial(mat);
+ PolygonAttributes pa = new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+
+ Sphere sphere = new Sphere(pointWidth / 1000, appear);
+
+ TransformGroup tg = new TransformGroup();
+ Transform3D transform = new Transform3D();
+ transform.setTranslation(new Vector3d(p));
+ tg.setTransform(transform);
+ tg.addChild(sphere);
+ v3d.add2DText(tg);
+ }
+
+ public boolean isLine()
+ {
+ return false;
+ }
+
+ public boolean isPoint()
+ {
+ return true;
+ }
+
+ public boolean isPolygon()
+ {
+ return false;
+ }
+
+ public boolean isText()
+ {
+ return false;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/ElementPolygon.java b/logo/src/xlogo/kernel/perspective/ElementPolygon.java
new file mode 100644
index 0000000..d0aa85b
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/ElementPolygon.java
@@ -0,0 +1,179 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Material;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.TriangleFanArray;
+import javax.vecmath.Point3d;
+import javax.media.j3d.Shape3D;
+import javax.vecmath.Vector3f;
+import javax.vecmath.Color3f;
+
+import xlogo.Logo;
+import xlogo.kernel.LogoError;
+
+/**
+ * This class represent A polygon surface in 3D mode
+ *
+ * @author loic
+ *
+ * @author Marko Zivkovic - I decoupled this from Application
+ */
+public class ElementPolygon extends Element3D
+{
+ public ElementPolygon(Viewer3D v3d)
+ {
+ super(v3d);
+ }
+
+ /**
+ * This method calculates all attributes for polygon and add it to the
+ * Polygon's list
+ */
+ public void addToScene() throws LogoError
+ {
+
+ if (vertex.size() < 3)
+ throw new LogoError(Logo.messages.getString("error.3d.3vertex"));
+
+ // Create normal vector
+
+ Point3d origine = vertex.get(0);
+ // System.out.println(" origine "+origine.x+" "+origine.y+" "+origine.z);
+
+ Point3d point1;
+ Vector3f vec1 = null;
+ Vector3f vec2 = null;
+ for (int i = 1; i < vertex.size(); i++)
+ {
+ point1 = vertex.get(i);
+ if (!point1.equals(origine))
+ {
+ // System.out.println(" point1 "+point1.x+" "+point1.y+" "+point1.z);
+ vec1 = new Vector3f((float) (point1.x - origine.x), (float) (point1.y - origine.y),
+ (float) (point1.z - origine.z));
+ break;
+ }
+ }
+ if (null == vec1)
+ throw new LogoError(Logo.messages.getString("error.3d.emptypolygon"));
+ for (int i = 2; i < vertex.size(); i++)
+ {
+ point1 = vertex.get(i);
+ // System.out.println(" point1 "+point1.x+" "+point1.y+" "+point1.z);
+ vec2 = new Vector3f((float) (point1.x - origine.x), (float) (point1.y - origine.y),
+ (float) (point1.z - origine.z));
+ if (vec1.dot(vec2) == 0)
+ vec2 = null;
+ else
+ {
+ // System.out.println(" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z);
+ // System.out.println(" vec2 "+vec2.x+" "+vec2.y+" "+vec2.z);
+ vec2.cross(vec1, vec2);
+ vec2.normalize();
+ vec1 = new Vector3f(vec2);
+ vec1.negate();
+ // System.out.println("Après"+" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z);
+ // System.out.println("Après"+" vec2 "+vec2.x+" "+vec2.y+" "+vec2.z);
+ // if (vec1.x!=0&& vec1.y!=0&& vec1.z!=0)
+ // System.out.println("coucou"+" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z);
+ break;
+ }
+ if (null == vec2)
+ throw new LogoError(Logo.messages.getString("error.3d.emptypolygon"));
+ }
+
+ // Create Geometry
+
+ int[] tab = new int[1];
+ tab[0] = vertex.size();
+ TriangleFanArray tfa = new TriangleFanArray(vertex.size(), TriangleFanArray.COORDINATES
+ | TriangleFanArray.COLOR_3 | TriangleFanArray.NORMALS, tab);
+ // TriangleFanArray tfa2=new
+ // TriangleFanArray(vertex.size(),TriangleFanArray.COORDINATES|TriangleFanArray.COLOR_3|TriangleFanArray.NORMALS,tab);
+ for (int i = 0; i < vertex.size(); i++)
+ {
+
+ tfa.setCoordinate(i, vertex.get(i));
+ // tfa2.setCoordinate(i, vertex.get(vertex.size()-1-i));
+
+ tfa.setColor(i, new Color3f(color.get(i)));
+ // tfa2.setColor(i, new Color3f(color.get(color.size()-i-1)));
+
+ tfa.setNormal(i, vec2);
+ // tfa2.setNormal(i, vec1);
+
+ }
+
+ Shape3D s = new Shape3D(tfa);
+ Appearance appear = new Appearance();
+ Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0f, 0f), new Color3f(1f, 1.0f,
+ 1.0f), new Color3f(1f, 1f, 1f), 64);
+ appear.setMaterial(mat);
+ PolygonAttributes pa = new PolygonAttributes();
+ pa.setCullFace(PolygonAttributes.CULL_NONE);
+ pa.setBackFaceNormalFlip(true);
+ appear.setPolygonAttributes(pa);
+
+ s.setAppearance(appear);
+ v3d.add3DObject(s);
+ // DrawPanel.listPoly.add(s);
+ // DrawPanel.listPoly.add(new Shape3D(tfa2));
+ // System.out.println(DrawPanel.listPoly.size()+" "+vertex.get(i).x+" "+vertex.get(i).y+" "+vertex.get(i).z);
+ }
+
+ public boolean isPoint()
+ {
+ return false;
+ }
+
+ public boolean isPolygon()
+ {
+ return true;
+ }
+
+ public boolean isLine()
+ {
+ return false;
+ }
+
+ public boolean isText()
+ {
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/perspective/FogDialog.java b/logo/src/xlogo/kernel/perspective/FogDialog.java
new file mode 100644
index 0000000..24b8bd1
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/FogDialog.java
@@ -0,0 +1,256 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+
+public class FogDialog extends JDialog implements ActionListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private String[] type = { Logo.messages.getString("3d.fog.none"),
+ Logo.messages.getString("3d.fog.linear"), Logo.messages.getString("3d.fog.exponential") };
+
+ private JComboBox comboType;
+
+ private PanelValue panelDensity;
+
+ private PanelValue panelFront;
+
+ private PanelValue panelBack;
+
+ private JLabel labelType;
+
+ private JButton ok;
+
+ private JButton refresh;
+
+ private Viewer3D viewer3d;
+
+ private MyFog fog;
+
+ FogDialog(Viewer3D viewer3d, MyFog fog, String title)
+ {
+ super(viewer3d, title, true);
+ this.viewer3d = viewer3d;
+ this.fog = fog;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ getContentPane().setLayout(new GridBagLayout());
+ setSize(450, 200);
+ labelType = new JLabel(Logo.messages.getString("3d.fog.type"));
+ comboType = new JComboBox(type);
+ comboType.setSelectedIndex(fog.getType());
+
+ panelDensity = new PanelValue(fog.getDensity(), Logo.messages.getString("3d.fog.density"));
+ panelFront = new PanelValue((int) (fog.getFront() * 1000), Logo.messages.getString("3d.fog.frontdistance"));
+ panelBack = new PanelValue((int) (fog.getBack() * 1000), Logo.messages.getString("3d.fog.backdistance"));
+
+ Font font = WSManager.getWorkspaceConfig().getFont();
+
+ ok = new JButton(Logo.messages.getString("pref.ok"));
+ refresh = new JButton(Logo.messages.getString("3d.light.apply"));
+ labelType.setFont(font);
+ comboType.setFont(font);
+ ok.setFont(font);
+ refresh.setFont(font);
+
+ comboType.addActionListener(this);
+ comboType.setActionCommand("combo");
+ ok.addActionListener(this);
+ ok.setActionCommand("ok");
+ refresh.addActionListener(this);
+ refresh.setActionCommand("refresh");
+
+ getContentPane().add(
+ labelType,
+ new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ comboType,
+ new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelFront,
+ new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelBack,
+ new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelDensity,
+ new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ refresh,
+ new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ ok,
+ new GridBagConstraints(1, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ selectComponents();
+ setVisible(true);
+
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String cmd = e.getActionCommand();
+ // The selected item in the combo Box has changed
+ if (cmd.equals("combo"))
+ selectComponents();
+ // Button Ok pressed
+ else if (cmd.equals("ok"))
+ {
+ updateFog();
+ dispose();
+ }
+ // Button Apply pressed
+ else if (cmd.equals("refresh"))
+ {
+ updateFog();
+ }
+ }
+
+ private void updateFog()
+ {
+ int t = comboType.getSelectedIndex();
+ float d = panelDensity.getTextValue();
+ float back = panelBack.getTextValue();
+ float front = panelFront.getTextValue();
+ fog.setType(t);
+ fog.setDensity(d);
+ fog.setBack(back / 1000);
+ fog.setFront(front / 1000);
+ fog.detach();
+ fog.removeAllChildren();
+ fog.createFog();
+ viewer3d.addNode(fog);
+ }
+
+ private void selectComponents()
+ {
+ int id = comboType.getSelectedIndex();
+ // None
+ if (id == MyFog.FOG_OFF)
+ {
+ panelDensity.setEnabled(false);
+ panelBack.setEnabled(false);
+ panelFront.setEnabled(false);
+ }
+ // Linear Fog
+ else if (id == MyFog.FOG_LINEAR)
+ {
+ panelDensity.setEnabled(false);
+ panelBack.setEnabled(true);
+ panelFront.setEnabled(true);
+ }
+ // Exponential Fog
+ else if (id == MyFog.FOG_EXPONENTIAL)
+ {
+ panelBack.setEnabled(false);
+ panelFront.setEnabled(false);
+ panelDensity.setEnabled(true);
+ }
+ }
+
+ class PanelValue extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private JLabel label;
+ private JTextField text;
+ private String title;
+ private float textValue;
+
+ PanelValue(float textValue, String title)
+ {
+ this.textValue = textValue;
+ this.title = title;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ label = new JLabel(title);
+ label.setFont(WSManager.getWorkspaceConfig().getFont());
+ String st;
+ int i = (int) textValue;
+ if (i == textValue)
+ {
+ st = String.valueOf(i);
+
+ }
+ else
+ st = String.valueOf(textValue);
+ text = new JTextField(st, 4);
+ add(label);
+ add(text);
+ }
+
+ public void setEnabled(boolean b)
+ {
+ super.setEnabled(b);
+ label.setEnabled(b);
+ text.setEnabled(b);
+ }
+
+ float getTextValue()
+ {
+ try
+ {
+ float f = Float.parseFloat(text.getText());
+ return f;
+ }
+ catch (NumberFormatException e)
+ {}
+ return 0;
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/LightDialog.java b/logo/src/xlogo/kernel/perspective/LightDialog.java
new file mode 100644
index 0000000..13aa671
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/LightDialog.java
@@ -0,0 +1,378 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Color;
+
+import xlogo.Logo;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Tuple3f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import javax.swing.BorderFactory;
+import javax.swing.JDialog;
+import javax.swing.JComboBox;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.JLabel;
+import javax.swing.JButton;
+import javax.swing.border.TitledBorder;
+
+import java.awt.event.*;
+
+import xlogo.gui.preferences.PanelColor;
+import xlogo.storage.WSManager;
+
+public class LightDialog extends JDialog implements ActionListener
+{
+ private static final long serialVersionUID = 1L;
+ private String[] type = { Logo.messages.getString("3d.light.none"),
+ Logo.messages.getString("3d.light.ambient"), Logo.messages.getString("3d.light.directional"),
+ Logo.messages.getString("3d.light.point"), Logo.messages.getString("3d.light.spot") };
+
+ private JComboBox comboType;
+
+ PanelColor panelColor;
+
+ private PanelPosition panelPosition;
+
+ private PanelPosition panelDirection;
+
+ private PanelAngle panelAngle;
+
+ private JLabel labelType;
+
+ private JButton ok;
+
+ private JButton refresh;
+
+ private Viewer3D viewer3d;
+
+ private MyLight light;
+
+ LightDialog(Viewer3D viewer3d, MyLight light, String title)
+ {
+ super(viewer3d, title, true);
+ this.viewer3d = viewer3d;
+ this.light = light;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ getContentPane().setLayout(new GridBagLayout());
+ setSize(500, 200);
+ labelType = new JLabel(Logo.messages.getString("3d.light.type"));
+ comboType = new JComboBox(type);
+ comboType.setSelectedIndex(light.getType());
+
+ Color3f col = light.getColor();
+ if (null != col)
+ panelColor = new PanelColor(col.get());
+ else
+ panelColor = new PanelColor(Color.white);
+ panelColor.setBackground(comboType.getBackground());
+
+ panelPosition = new PanelPosition(Logo.messages.getString("3d.light.position"), light.getPosition());
+ panelDirection = new PanelPosition(Logo.messages.getString("3d.light.direction"), light.getDirection());
+ panelAngle = new PanelAngle(light.getAngle());
+ ok = new JButton(Logo.messages.getString("pref.ok"));
+ refresh = new JButton(Logo.messages.getString("3d.light.apply"));
+
+ Font font = WSManager.getWorkspaceConfig().getFont();
+
+ labelType.setFont(font);
+ comboType.setFont(font);
+ ok.setFont(font);
+ refresh.setFont(font);
+
+ comboType.addActionListener(this);
+ comboType.setActionCommand("combo");
+ ok.addActionListener(this);
+ ok.setActionCommand("ok");
+ refresh.addActionListener(this);
+ refresh.setActionCommand("refresh");
+
+ getContentPane().add(
+ labelType,
+ new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ comboType,
+ new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelColor,
+ new GridBagConstraints(0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelPosition,
+ new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelDirection,
+ new GridBagConstraints(1, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ panelAngle,
+ new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ refresh,
+ new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ ok,
+ new GridBagConstraints(1, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ selectComponents();
+ setVisible(true);
+
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String cmd = e.getActionCommand();
+ // The selected item in the combo Box has changed
+ if (cmd.equals("combo"))
+ selectComponents();
+ // Button Ok pressed
+ else if (cmd.equals("ok"))
+ {
+ updateLight();
+ dispose();
+ }
+ // Button Apply pressed
+ else if (cmd.equals("refresh"))
+ {
+ updateLight();
+ }
+ }
+
+ private void updateLight()
+ {
+ int t = comboType.getSelectedIndex();
+ Color3f c = new Color3f(panelColor.getValue());
+ Point3f p = panelPosition.getPosition();
+ Vector3f d = panelDirection.getDirection();
+ float a = panelAngle.getAngleValue();
+ light.setType(t);
+ light.setColor(c);
+ light.setPosition(p);
+ light.setDirection(d);
+ light.setAngle(a);
+ light.detach();
+ light.removeAllChildren();
+ light.createLight();
+ // System.out.println(c+" "+" "+p+" "+d);
+ viewer3d.addNode(light);
+
+ }
+
+ private void selectComponents()
+ {
+ int id = comboType.getSelectedIndex();
+ // None
+ if (id == MyLight.LIGHT_OFF)
+ {
+ panelColor.setEnabled(false);
+ panelPosition.setEnabled(false);
+ panelDirection.setEnabled(false);
+ panelAngle.setEnabled(false);
+ }
+ // Ambient
+ else if (id == MyLight.LIGHT_AMBIENT)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(false);
+ panelDirection.setEnabled(false);
+ panelAngle.setEnabled(false);
+ }
+ // Directional
+ else if (id == MyLight.LIGHT_DIRECTIONAL)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(false);
+ panelDirection.setEnabled(true);
+ panelAngle.setEnabled(false);
+ }
+ // PointLight
+ else if (id == MyLight.LIGHT_POINT)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(true);
+ panelDirection.setEnabled(false);
+ panelAngle.setEnabled(false);
+ }
+ // Spot
+ else if (id == MyLight.LIGHT_SPOT)
+ {
+ panelColor.setEnabled(true);
+ panelPosition.setEnabled(true);
+ panelDirection.setEnabled(true);
+ panelAngle.setEnabled(true);
+ }
+ }
+
+ class PanelAngle extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private JLabel label;
+ private JTextField angle;
+ private float angleValue;
+
+ PanelAngle(float angleValue)
+ {
+ this.angleValue = angleValue;
+
+ initGui();
+ }
+
+ private void initGui()
+ {
+ label = new JLabel(Logo.messages.getString("3d.light.angle"));
+ label.setFont(WSManager.getWorkspaceConfig().getFont());
+ angle = new JTextField(String.valueOf(angleValue));
+ angle.setSize(30, WSManager.getWorkspaceConfig().getFont().getSize() + 10);
+ add(label);
+ add(angle);
+ }
+
+ public void setEnabled(boolean b)
+ {
+ super.setEnabled(b);
+ label.setEnabled(b);
+ angle.setEnabled(b);
+ }
+
+ /**
+ * @return
+ * @uml.property name="angleValue"
+ */
+ float getAngleValue()
+ {
+ try
+ {
+ float f = Float.parseFloat(angle.getText());
+ return f;
+ }
+ catch (NumberFormatException e)
+ {}
+ return MyLight.DEFAULT_ANGLE;
+ }
+ }
+
+ class PanelPosition extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private String title;
+ private JTextField Xpos;
+ private JTextField Ypos;
+ private JTextField Zpos;
+ private JLabel sep1;
+ private JLabel sep2;
+ Tuple3f tuple;
+
+ PanelPosition(String title, Tuple3f tuple)
+ {
+ this.title = title;
+ this.tuple = tuple;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ TitledBorder tb = BorderFactory.createTitledBorder(title);
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ setBorder(tb);
+ sep1 = new JLabel("x");
+ sep2 = new JLabel("x");
+ Xpos = new JTextField(4);
+ Ypos = new JTextField(4);
+ Zpos = new JTextField(4);
+ if (null != tuple)
+ {
+ Xpos.setText(String.valueOf((int) (tuple.x * 1000)));
+ Ypos.setText(String.valueOf((int) (tuple.y * 1000)));
+ Zpos.setText(String.valueOf((int) (tuple.z * 1000)));
+ }
+ add(Xpos);
+ add(sep1);
+ add(Ypos);
+ add(sep2);
+ add(Zpos);
+ }
+
+ Point3f getPosition()
+ {
+ try
+ {
+ float x = Float.parseFloat(Xpos.getText());
+ float y = Float.parseFloat(Ypos.getText());
+ float z = Float.parseFloat(Zpos.getText());
+ return new Point3f(x / 1000, y / 1000, z / 1000);
+ }
+ catch (NumberFormatException e)
+ {}
+ return (new Point3f(0, 0, 0));
+ }
+
+ Vector3f getDirection()
+ {
+ try
+ {
+ float x = Float.parseFloat(Xpos.getText());
+ float y = Float.parseFloat(Ypos.getText());
+ float z = Float.parseFloat(Zpos.getText());
+ return new Vector3f(x / 1000, y / 1000, z / 1000);
+ }
+ catch (NumberFormatException e)
+ {}
+ return (new Vector3f(0, 0, 0));
+
+ }
+
+ public void setEnabled(boolean b)
+ {
+ super.setEnabled(b);
+ sep1.setEnabled(b);
+ sep2.setEnabled(b);
+ Xpos.setEnabled(b);
+ Ypos.setEnabled(b);
+ Zpos.setEnabled(b);
+ }
+
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/MyFog.java b/logo/src/xlogo/kernel/perspective/MyFog.java
new file mode 100644
index 0000000..1961fcc
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/MyFog.java
@@ -0,0 +1,134 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.LinearFog;
+import javax.media.j3d.ExponentialFog;
+import javax.media.j3d.Fog;
+import javax.media.j3d.BoundingSphere;
+
+import javax.vecmath.Color3f;
+
+public class MyFog extends BranchGroup
+{
+ protected final static int FOG_OFF = 0;
+ protected final static int FOG_LINEAR = 1;
+ protected final static int FOG_EXPONENTIAL = 2;
+ private int type = FOG_OFF;
+ private float density = 1;
+ private float backDistance = 3.5f;
+ private float frontDistance = 0.5f;
+ private Fog fog;
+ Color3f color;
+
+ MyFog(int type, Color3f color)
+ {
+ super();
+ setCapability(BranchGroup.ALLOW_DETACH);
+ setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+ this.type = type;
+ this.color = color;
+ }
+
+ /**
+ * This method creates a light according to each parameter:<br>
+ * type, color, position, direction and angle
+ */
+ void createFog()
+ {
+ this.setCapability(BranchGroup.ALLOW_DETACH);
+ this.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+ switch (type)
+ {
+ case FOG_OFF:
+ fog = null;
+ break;
+ case FOG_LINEAR:
+ fog = new LinearFog(color);
+ ((LinearFog) fog).setBackDistance(backDistance);
+ ((LinearFog) fog).setFrontDistance(frontDistance);
+ fog.setInfluencingBounds(new BoundingSphere());
+ break;
+ case FOG_EXPONENTIAL:
+ fog = new ExponentialFog(color, density);
+ fog.setInfluencingBounds(new BoundingSphere());
+ break;
+ }
+ if (null != fog)
+ addChild(fog);
+ }
+
+ /**
+ * This method returns the light type
+ *
+ * @return an integer which represents the light type
+ * @uml.property name="type"
+ */
+ int getType()
+ {
+ return type;
+ }
+
+ void setType(int t)
+ {
+ type = t;
+ }
+
+ float getDensity()
+ {
+ return density;
+ }
+
+ void setDensity(float f)
+ {
+ density = f;
+ }
+
+ float getBack()
+ {
+ return backDistance;
+ }
+
+ void setBack(float f)
+ {
+ backDistance = f;
+ }
+
+ float getFront()
+ {
+ return frontDistance;
+ }
+
+ void setFront(float f)
+ {
+ frontDistance = f;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/MyLight.java b/logo/src/xlogo/kernel/perspective/MyLight.java
new file mode 100644
index 0000000..9c5c054
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/MyLight.java
@@ -0,0 +1,190 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import javax.media.j3d.AmbientLight;
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.DirectionalLight;
+import javax.media.j3d.Light;
+import javax.media.j3d.PointLight;
+import javax.media.j3d.SpotLight;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import javax.media.j3d.BranchGroup;
+
+public class MyLight extends BranchGroup
+{
+ protected final static int LIGHT_OFF = 0;
+ protected final static int LIGHT_AMBIENT = 1;
+ protected final static int LIGHT_DIRECTIONAL = 2;
+ protected final static int LIGHT_POINT = 3;
+ protected final static int LIGHT_SPOT = 4;
+ protected final static float DEFAULT_ANGLE = 15;
+ private int type = LIGHT_OFF;
+ private Color3f color;
+ private Point3f position;
+ private Vector3f direction;
+ private float angle = DEFAULT_ANGLE;
+ private Light light;
+
+ MyLight(int type)
+ {
+ super();
+ this.type = type;
+ }
+
+ MyLight(int type, Color3f color, Point3f position)
+ {
+ super();
+ this.type = type;
+ this.color = color;
+ this.position = position;
+ }
+
+ /**
+ * This method creates a light according to each parameter:<br>
+ * type, color, position, direction and angle
+ */
+ void createLight()
+ {
+ this.setCapability(BranchGroup.ALLOW_DETACH);
+ this.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+ switch (type)
+ {
+ case LIGHT_OFF:
+ light = null;
+ break;
+ case LIGHT_AMBIENT:
+ light = new AmbientLight(color);
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ case LIGHT_DIRECTIONAL:
+ light = new DirectionalLight(color, direction);
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ case LIGHT_POINT:
+ light = new PointLight(color, position, new Point3f(1, 0, 0));
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ case LIGHT_SPOT:
+ light = new SpotLight(color, position, new Point3f(1, 0, 0), direction, (float) Math.toRadians(angle),
+ 64);
+ light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE));
+ break;
+ }
+ if (null != light)
+ addChild(light);
+ }
+
+ /**
+ * This method returns the light type
+ *
+ * @return an integer which represents the light type
+ */
+ int getType()
+ {
+ return type;
+ }
+
+ /**
+ * @param t
+ */
+ void setType(int t)
+ {
+ type = t;
+ }
+
+ /**
+ * @return
+ */
+ Color3f getColor()
+ {
+ return color;
+ }
+
+ /**
+ * @param c
+ */
+ void setColor(Color3f c)
+ {
+ color = c;
+ }
+
+ /**
+ * @return
+ */
+ Point3f getPosition()
+ {
+ return position;
+ }
+
+ /**
+ * @param p
+ */
+ void setPosition(Point3f p)
+ {
+
+ position = p;
+ }
+
+ /**
+ * @return
+ */
+ Vector3f getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * @param v
+ */
+ void setDirection(Vector3f v)
+ {
+ direction = v;
+ }
+
+ /**
+ * @return
+ */
+ float getAngle()
+ {
+ return angle;
+ }
+
+ /**
+ * @param f
+ */
+ void setAngle(float f)
+ {
+ angle = f;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/Viewer3D.java b/logo/src/xlogo/kernel/perspective/Viewer3D.java
new file mode 100644
index 0000000..e7bce56
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/Viewer3D.java
@@ -0,0 +1,493 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+import java.awt.Toolkit;
+
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.Canvas3D;
+
+import java.awt.image.BufferedImage;
+
+import javax.media.j3d.ImageComponent2D;
+import javax.media.j3d.ImageComponent;
+import javax.media.j3d.GraphicsContext3D;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.Raster;
+import javax.vecmath.Point3f;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Node;
+import javax.media.j3d.Background;
+
+import java.awt.GridBagLayout;
+import java.awt.Color;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.border.TitledBorder;
+
+import java.awt.GridLayout;
+
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector3d;
+
+import xlogo.storage.WSManager;
+import xlogo.utils.Utils;
+import xlogo.utils.WriteImage;
+import xlogo.Logo;
+
+import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
+import com.sun.j3d.utils.universe.SimpleUniverse;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+/**
+ * This Frame displays the 3D main Scene
+ *
+ * BG scene
+ *
+ * BG backBranchGroup | BG Mylight (4) | BG
+ * Background Light 1,2,3,4 Other objects
+ */
+
+public class Viewer3D extends JFrame implements ActionListener
+{
+ private final static String ACTION_SCREENSHOT = "screenshot";
+ private final static String ACTION_LIGHT0 = "light0";
+ private final static String ACTION_LIGHT1 = "light1";
+ private final static String ACTION_LIGHT2 = "light2";
+ private final static String ACTION_LIGHT3 = "light3";
+ private final static String ACTION_FOG = "fog";
+
+ // To store the Light attributes
+ private MyLight[] myLights;
+ // To store the Fog attributes
+ private MyFog myFog;
+ // Gui Components
+ private ImageIcon iscreenshot = Utils.dimensionne_image("screenshot.png", this);
+ private JButton screenshot;
+ private static final long serialVersionUID = 1L;
+ private World3D w3d;
+ private Canvas3D canvas3D;
+ /**
+ * Main scene's Branchgroup
+ */
+ private BranchGroup scene;
+ private BranchManager branchManager;
+ private SimpleUniverse universe;
+ private Color3f backgroundColor;
+ private BranchGroup backBranchgroup;
+ private Background back;
+ private PanelLight panelLight;
+ private PanelFog panelFog;
+
+ public Viewer3D(World3D w3d, Color c)
+ {
+ this.w3d = w3d;
+ this.backgroundColor = new Color3f(c);
+ initGui();
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ String cmd = e.getActionCommand();
+ // Take a screenshot
+ if (cmd.equals(Viewer3D.ACTION_SCREENSHOT))
+ {
+ WriteImage wi = new WriteImage(this, null);
+ int id = wi.chooseFile();
+ if (id == JFileChooser.APPROVE_OPTION)
+ {
+ GraphicsContext3D ctx = canvas3D.getGraphicsContext3D();
+ java.awt.Dimension scrDim = canvas3D.getSize();
+
+ // setting raster component
+ Raster ras = new Raster(new Point3f(-1.0f, -1.0f, -1.0f), Raster.RASTER_COLOR, 0, 0, scrDim.width,
+ scrDim.height, new ImageComponent2D(ImageComponent.FORMAT_RGB, new BufferedImage(scrDim.width,
+ scrDim.height, java.awt.image.BufferedImage.TYPE_INT_RGB)), null);
+ ctx.readRaster(ras);
+ BufferedImage img = ras.getImage().getImage();
+ wi.setImage(img);
+ wi.start();
+ }
+ }
+ // Click on Button Light1
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT0))
+ {
+ new LightDialog(this, myLights[0], Logo.messages.getString("3d.light") + " 1");
+ }
+ // Click on Button Light2
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT1))
+ {
+ new LightDialog(this, myLights[1], Logo.messages.getString("3d.light") + " 2");
+ }
+ // Click on Button Light3
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT2))
+ {
+ new LightDialog(this, myLights[2], Logo.messages.getString("3d.light") + " 3");
+ }
+ // Click on Button Light4
+ else if (cmd.equals(Viewer3D.ACTION_LIGHT3))
+ {
+ new LightDialog(this, myLights[3], Logo.messages.getString("3d.light") + " 4");
+ }
+ // Click on the Fog Button
+ else if (cmd.equals(Viewer3D.ACTION_FOG))
+ {
+ new FogDialog(this, myFog, Logo.messages.getString("3d.fog"));
+
+ }
+ }
+
+ public void setText()
+ {
+ setTitle(Logo.messages.getString("3d.viewer"));
+ panelFog.setText();
+ panelLight.setText();
+
+ }
+
+ private void initGui()
+ {
+ this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png")));
+
+ // Creation d'un composant de classe Canvas3D permettant de visualiser
+ // une scène 3D
+ canvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
+ // Création d'un univers 3D rattaché au composant 3D
+ universe = new SimpleUniverse(canvas3D);
+
+ // Install the camera at the valid position with correct orientation
+ TransformGroup tg = universe.getViewingPlatform().getViewPlatformTransform();
+ Transform3D trans = new Transform3D();
+ if (null == w3d)
+ {
+ w3d = new World3D();
+ }
+ trans.setTranslation(new Vector3d(-w3d.xCamera / 1000, -w3d.yCamera / 1000, -w3d.zCamera / 1000));
+ Transform3D rot = new Transform3D();
+ rot.lookAt(new Point3d(-w3d.xCamera / 1000, -w3d.yCamera / 1000, -w3d.zCamera / 1000), new Point3d(0, 0, 0),
+ new Vector3d(0, 1, 0));
+ rot.invert();
+ trans.mul(rot);
+ tg.setTransform(trans);
+ OrbitBehavior ob = new OrbitBehavior(canvas3D, OrbitBehavior.REVERSE_ALL);
+ ob.setRotationCenter(new Point3d(0, 0, 0));
+ ob.setSchedulingBounds(new BoundingSphere(new Point3d(0, 0, 0), 2 * w3d.r / 1000));
+ universe.getViewingPlatform().setViewPlatformBehavior(ob);
+
+ // Create the root of the branch graph
+ scene = new BranchGroup();
+ branchManager = new BranchManager();
+ // scene.setName("Main Branch");
+ // We can add New BranchGroup dynamically in the main scene
+ scene.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
+ // We can remove BranchGroup dynamically in the main scene
+ scene.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+
+ // Configure and create background
+ createBackground();
+
+ // Configure Lights
+ initLights();
+
+ // Configure Fog
+ myFog = new MyFog(MyFog.FOG_OFF, backgroundColor);
+ scene.addChild(myFog);
+
+ // Rattachement de la scène 3D à l'univers
+ universe.addBranchGraph(scene);
+
+ Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
+ int width = d.width * 9 / 10;
+ int height = d.height * 9 / 10;
+ setSize(width, height);
+ getContentPane().setLayout(new GridBagLayout());
+ screenshot = new JButton(iscreenshot);
+ screenshot.addActionListener(this);
+ screenshot.setMaximumSize(new Dimension(100, 100));
+ screenshot.setActionCommand(Viewer3D.ACTION_SCREENSHOT);
+
+ panelLight = new PanelLight(this);
+ panelFog = new PanelFog(this);
+
+ getContentPane().add(
+ canvas3D,
+ new GridBagConstraints(0, 0, 1, 3, 2.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+ new Insets(0, 0, 0, 0), 0, 0));
+ getContentPane().add(
+ screenshot,
+ new GridBagConstraints(1, 0, 1, 1, 0.1, 0.8, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(10, 10, 10, 10), 0, 0));
+ getContentPane().add(
+ panelLight,
+ new GridBagConstraints(1, 1, 1, 1, 0.1, 0.1, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(10, 10, 10, 10), 0, 0));
+ getContentPane().add(
+ panelFog,
+ new GridBagConstraints(1, 2, 1, 1, 0.1, 0.1, GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(10, 10, 10, 10), 0, 0));
+ setText();
+ getContentPane().validate();
+ }
+
+ /**
+ * This method adds a shape3D to the main scene
+ *
+ * @param s
+ * The Shape to add
+ */
+ public void add3DObject(Shape3D s)
+ {
+ branchManager.add3DObject(s);
+ }
+
+ public void add2DText(TransformGroup tg)
+ {
+ branchManager.add2DText(tg);
+ }
+
+ public void insertBranch()
+ {
+ branchManager.insertBranch();
+ branchManager = new BranchManager();
+ }
+
+ /**
+ * This methods adds two default PointLight int the 3d Scene
+ */
+ private void initLights()
+ {
+ myLights = new MyLight[4];
+ // First Default Point Light
+ Color3f color = new Color3f(1f, 1f, 1f);
+ Point3f pos = new Point3f((float) w3d.xCamera / 1000, (float) w3d.yCamera / 1000, (float) w3d.zCamera / 1000);
+ myLights[0] = new MyLight(MyLight.LIGHT_POINT, color, pos);
+
+ // Second default Point Light
+ pos = new Point3f(-(float) w3d.xCamera / 1000, -(float) w3d.yCamera / 1000, -(float) w3d.zCamera / 1000);
+ myLights[1] = new MyLight(MyLight.LIGHT_POINT, color, pos);
+
+ myLights[2] = new MyLight(MyLight.LIGHT_OFF);
+ myLights[3] = new MyLight(MyLight.LIGHT_OFF);
+ for (int i = 0; i < 4; i++)
+ {
+ myLights[i].createLight();
+ }
+ addAllLights(scene);
+ }
+
+ /**
+ * Add all lights in the main 3D scene
+ */
+ private void addAllLights(BranchGroup bg)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ bg.addChild(myLights[i]);
+ }
+ }
+
+ /**
+ * add a light in the main scene
+ */
+ protected void addNode(Node l)
+ {
+ scene.addChild(l);
+ }
+
+ /**
+ * This methods erase all drawings on the 3D Viewer Drawing Area
+ */
+ public void clearScreen()
+ {
+ scene.removeAllChildren();
+ createBackground();
+ initLights();
+ /*
+ * Enumeration<Locale> locales=universe.getAllLocales();
+ * while(locales.hasMoreElements()){
+ * Locale lo=locales.nextElement();
+ * Enumeration<BranchGroup> en=lo.getAllBranchGraphs();
+ * while(en.hasMoreElements()){
+ * BranchGroup bg=en.nextElement();
+ * // System.out.println(bg.getName());
+ * if (null!=bg.getName()&&bg.getName().equals("Main Branch")){
+ * // Detach scene
+ * bg.detach();
+ * // Delete all nodes
+ * bg.removeAllChildren();
+ * // create background
+ * createBackground(bg);
+ * // Add lights
+ * addAllLights(bg);
+ * // Attach scene
+ * lo.addBranchGraph(bg);
+ * }
+ * }
+ * }
+ */
+
+ }
+
+ public void updateBackGround(Color c)
+ {
+ backgroundColor = new Color3f(c);
+ backBranchgroup.detach();
+ createBackground();
+ }
+
+ /**
+ * This method creates the Background with the defined color
+ */
+ public void createBackground()
+ {
+ backBranchgroup = new BranchGroup();
+ backBranchgroup.setCapability(BranchGroup.ALLOW_DETACH);
+ backBranchgroup.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
+
+ back = new Background(backgroundColor);
+ back.setApplicationBounds(new BoundingSphere());
+ back.setCapability(Background.ALLOW_COLOR_WRITE);
+ backBranchgroup.addChild(back);
+ scene.addChild(backBranchgroup);
+ }
+
+ class BranchManager
+ {
+ BranchGroup bg;
+
+ BranchManager()
+ {
+ bg = new BranchGroup();
+ bg.setCapability(BranchGroup.ALLOW_DETACH);
+ }
+
+ /**
+ * This method adds a shape3D to the main scene
+ *
+ * @param s
+ * The Shape to add
+ */
+ void add3DObject(Shape3D s)
+ {
+ bg.addChild(s);
+ }
+
+ void add2DText(TransformGroup tg)
+ {
+ bg.addChild(tg);
+ }
+
+ void insertBranch()
+ {
+ bg.compile();
+ scene.addChild(bg);
+ }
+ }
+
+ class PanelLight extends JPanel
+ {
+ private static final long serialVersionUID = 1L;
+ private JButton[] buttonLights;
+ private Viewer3D viewer3d;
+
+ PanelLight(Viewer3D viewer3d)
+ {
+ this.viewer3d = viewer3d;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ buttonLights = new JButton[4];
+ setLayout(new GridLayout(2, 2, 10, 10));
+ for (int i = 0; i < 4; i++)
+ {
+ ImageIcon ilight = Utils.dimensionne_image("light" + i + ".png", viewer3d);
+ buttonLights[i] = new JButton(ilight);
+ add(buttonLights[i]);
+ buttonLights[i].addActionListener(viewer3d);
+ buttonLights[i].setActionCommand("light" + i);
+ }
+ setText();
+ }
+
+ void setText()
+ {
+ TitledBorder tb = BorderFactory.createTitledBorder(Logo.messages.getString("3d.light"));
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ setBorder(tb);
+ }
+ }
+
+ class PanelFog extends JPanel
+ {
+
+ private static final long serialVersionUID = 1L;
+ private JButton buttonFog;
+ private Viewer3D viewer3d;
+
+ PanelFog(Viewer3D viewer3d)
+ {
+ this.viewer3d = viewer3d;
+ initGui();
+ }
+
+ private void initGui()
+ {
+ ImageIcon ifog =Utils.dimensionne_image("fog.png", viewer3d);
+ buttonFog = new JButton(ifog);
+ buttonFog.setActionCommand(Viewer3D.ACTION_FOG);
+ buttonFog.addActionListener(viewer3d);
+ add(buttonFog);
+ setText();
+ }
+
+ void setText()
+ {
+ TitledBorder tb = BorderFactory.createTitledBorder(Logo.messages.getString("3d.fog"));
+ tb.setTitleFont(WSManager.getWorkspaceConfig().getFont());
+ setBorder(tb);
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/perspective/World3D.java b/logo/src/xlogo/kernel/perspective/World3D.java
new file mode 100644
index 0000000..d3ffcc5
--- /dev/null
+++ b/logo/src/xlogo/kernel/perspective/World3D.java
@@ -0,0 +1,318 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel.perspective;
+
+import xlogo.storage.WSManager;
+import xlogo.storage.user.UserConfig;
+
+public class World3D
+{
+
+ double theta = Math.toRadians(45);
+
+ double phi = Math.toRadians(30);
+
+ public double r = 1500;
+
+ public double xCamera = r * Math.cos(theta) * Math.cos(phi);
+
+ public double yCamera = r * Math.sin(theta) * Math.cos(phi);
+
+ public double zCamera = r * Math.sin(phi); ;
+
+ public double screenDistance = 1000;
+
+ private double[][] array3D;
+
+ public World3D()
+ {
+ initArray3D();
+
+ }
+
+ /**
+ * This method converts the coordinates in coord to the screen coord
+ */
+ public void toScreenCoord(double[] coord)
+ {
+ // Coord in the camera world
+ toCameraWorld(coord);
+ /*
+ * double x=array3D[0][0]*coord[0]+array3D[1][0]*coord[1];
+ * double
+ * y=array3D[0][1]*coord[0]+array3D[1][1]*coord[1]+array3D[2][1]*coord
+ * [2];
+ * double
+ * z=array3D[0][2]*coord[0]+array3D[1][2]*coord[1]+array3D[2][2]*coord
+ * [2]+r;
+ */
+ cameraToScreen(coord);
+ }
+
+ /**
+ * This method converts coordinates conatined in coord from camera world to
+ * screen
+ */
+ public void cameraToScreen(double[] coord)
+ {
+ UserConfig uc = WSManager.getUserConfig();
+
+ double x = coord[0];
+ double y = coord[1];
+ double z = coord[2];
+ coord[0] = screenDistance * x / z + uc.getImageWidth() / 2;
+ coord[1] = uc.getImageHeight() / 2 - screenDistance * y / z;
+ }
+
+ /**
+ * This method initializes the 3D array
+ */
+ private void initArray3D()
+ {
+ double cost = Math.cos(theta);
+ double sint = Math.sin(theta);
+ double cosp = Math.cos(phi);
+ double sinp = Math.sin(phi);
+ array3D = new double[4][4];
+ array3D[0][0] = -sint;
+ array3D[0][1] = -cost * sinp;
+ array3D[0][2] = -cost * cosp;
+ array3D[0][3] = 0;
+ array3D[1][0] = cost;
+ array3D[1][1] = -sint * sinp;
+ array3D[1][2] = -sint * cosp;
+ array3D[1][3] = 0;
+ array3D[2][0] = 0;
+ array3D[2][1] = cosp;
+ array3D[2][2] = -sinp;
+ array3D[2][3] = 0;
+ array3D[3][0] = 0;
+ array3D[3][1] = 0;
+ array3D[3][2] = r;
+ array3D[3][3] = 1;
+ }
+
+ /**
+ * This method converts coordinates from Real world to coodinates in camera
+ * world
+ *
+ * @param coord
+ * The real World coordinates
+ * @return Array that contains coordinates in camera world
+ */
+
+ public void toCameraWorld(double[] coord)
+ {
+ double x = array3D[0][0] * coord[0] + array3D[1][0] * coord[1];
+ double y = array3D[0][1] * coord[0] + array3D[1][1] * coord[1] + array3D[2][1] * coord[2];
+ double z = array3D[0][2] * coord[0] + array3D[1][2] * coord[1] + array3D[2][2] * coord[2] + r;
+ coord[0] = x;
+ coord[1] = y;
+ coord[2] = z;
+ }
+
+ /**
+ * This method multiply two matrices
+ *
+ * @param a
+ * The first matrix
+ * @param b
+ * The second matrix
+ * @return The matrix product
+ */
+ public double[][] multiply(double[][] a, double[][] b)
+ {
+ int n = a.length;
+ int p = b[0].length;
+ int s = a[0].length;
+ double[][] m = new double[n][p];
+ for (int i = 0; i < n; i++)
+ {
+ for (int j = 0; j < p; j++)
+ {
+ m[i][j] = 0;
+ for (int k = 0; k < s; k++)
+ {
+ m[i][j] += a[i][k] * b[k][j];
+ }
+ }
+ }
+ return m;
+ }
+
+ /**
+ * This method returns a matrix for a Z Axis rotation
+ *
+ * @param angle
+ * The rotation angle
+ * @return The rotation matrix
+ */
+ public double[][] rotationZ(double angle)
+ {
+ double[][] m = new double[3][3];
+ double cos = Math.cos(Math.toRadians(angle));
+ double sin = Math.sin(Math.toRadians(angle));
+ m[0][0] = cos;
+ m[1][0] = sin;
+ m[0][1] = -sin;
+ m[1][1] = cos;
+ m[2][2] = 1;
+ m[2][1] = m[2][0] = m[0][2] = m[1][2] = 0;
+ return m;
+ }
+
+ /**
+ * This method returns a matrix for a Y Axis rotation
+ *
+ * @param angle
+ * The rotation angle
+ * @return The rotation matrix
+ */
+ public double[][] rotationY(double angle)
+ {
+ double[][] m = new double[3][3];
+ double cos = Math.cos(Math.toRadians(angle));
+ double sin = Math.sin(Math.toRadians(angle));
+ m[0][0] = cos;
+ m[2][0] = sin;
+ m[0][2] = -sin;
+ m[2][2] = cos;
+ m[1][1] = 1;
+ m[0][1] = m[1][0] = m[1][2] = m[2][1] = 0;
+ return m;
+ }
+
+ /**
+ * This method returns a matrix for a X Axis rotation
+ *
+ * @param angle
+ * The rotation angle
+ * @return The rotation matrix
+ */
+ public double[][] rotationX(double angle)
+ {
+ double[][] m = new double[3][3];
+ double cos = Math.cos(Math.toRadians(angle));
+ double sin = Math.sin(Math.toRadians(angle));
+ m[0][0] = 1;
+ m[1][1] = cos;
+ m[2][1] = sin;
+ m[1][2] = -sin;
+ m[2][2] = cos;
+ m[1][0] = m[2][0] = m[0][1] = m[0][2] = 0;
+ return m;
+ }
+
+ /**
+ * This method with the 3 Euler's angle builds a rotation matrix
+ *
+ * @param heading
+ * The turtle heading
+ * @param roll
+ * The turtle roll
+ * @param pitch
+ * The turtle pitch
+ * @return The rotation Matrix
+ */
+
+ public double[][] EulerToRotation(double roll, double pitch, double heading)
+ {
+ double[][] m = new double[3][3];
+ double rpitch = Math.toRadians(pitch);
+ double rheading = Math.toRadians(heading);
+ double rroll = Math.toRadians(roll);
+ double a = Math.cos(rpitch);
+ double b = Math.sin(rpitch);
+ double c = Math.cos(rroll);
+ double d = Math.sin(rroll);
+ double e = Math.cos(rheading);
+ double f = Math.sin(rheading);
+ double bd = b * d;
+ double bc = b * c;
+ m[0][0] = c * e - bd * f;
+ m[0][1] = -c * f - bd * e;
+ m[0][2] = -a * d;
+ m[1][0] = a * f;
+ m[1][1] = a * e;
+ m[1][2] = -b;
+ m[2][0] = d * e + bc * f;
+ m[2][1] = -d * f + bc * e;
+ m[2][2] = a * c;
+ return m;
+ }
+
+ /**
+ *
+ */
+ public double[] rotationToEuler(double[][] m)
+ {
+ double[] v = new double[3];
+
+ double angle_x, angle_y, angle_z;
+ double a, tr_x, tr_y;
+ // Angle x
+ angle_x = -Math.asin(m[1][2]);
+
+ a = Math.cos(angle_x);
+ // Gimbal Lock?
+ if (Math.abs(a) > 0.005)
+ {
+ // No gimbal Lock
+ // Angle z
+ tr_x = m[1][1] / a;
+ tr_y = m[1][0] / a;
+ angle_z = Math.atan2(tr_y, tr_x);
+ // Angle y
+ tr_x = m[2][2] / a;
+ tr_y = -m[0][2] / a;
+ angle_y = Math.atan2(tr_y, tr_x);
+ v[0] = Math.toDegrees(angle_x);
+ v[1] = -Math.toDegrees(angle_y);
+ v[2] = -Math.toDegrees(angle_z);
+ }
+ else
+ { // Gimbal Lock
+ angle_y = 0;
+ // Angle z
+ tr_x = m[0][0];
+ tr_y = m[2][0];
+ angle_z = Math.atan2(tr_y, tr_x);
+ v[0] = 270;
+ v[1] = 0;
+ v[2] = Math.toDegrees(angle_z);
+ }
+
+ for (int i = 0; i < v.length; i++)
+ {
+ if (v[i] < 0)
+ v[i] += 360;
+ }
+ return v;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/ErrorManager.java b/logo/src/xlogo/kernel/userspace/ErrorManager.java
new file mode 100644
index 0000000..f1b18ec
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/ErrorManager.java
@@ -0,0 +1,201 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import xlogo.interfaces.ErrorDetector.AmbiguityDetector.AmbiguityListener;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.userspace.procedures.Procedure;
+
+public class ErrorManager implements FileErrorCollector
+{
+ private final FileErrorCollector fileErrorDetector;
+ private final AmbiguityDetector ambiguityDetector;
+
+ private final HashMap<String, Void> errorFiles = new HashMap<String, Void>();
+ private final HashMap<String, Collection<String>> ambiguousProcToFiles = new HashMap<String, Collection<String>>();
+
+ private final ArrayList<ErrorListener> errorListeners = new ArrayList<ErrorListener>();
+
+ public ErrorManager(FileErrorCollector fileErrorDetector, AmbiguityDetector ambiguityDetector)
+ {
+ this.fileErrorDetector = fileErrorDetector;
+ this.ambiguityDetector = ambiguityDetector;
+
+ initListeners();
+ }
+
+ private void initListeners()
+ {
+ fileErrorDetector.addErrorListener(new ErrorListener(){
+
+ @Override
+ public void errorsDetected(String fileName)
+ {
+ errorFiles.put(fileName, null);
+ notifyErrorDetected(fileName);
+ }
+
+ @Override
+ public void allErrorsCorrected(String fileName)
+ {
+ errorFiles.remove(fileName);
+
+ for (Collection<String> fileNames : ambiguousProcToFiles.values())
+ {
+ if (fileNames.contains(fileName))
+ return;
+ }
+ // no more errors or ambiguities found
+ notifyAllErrorsCorrected(fileName);
+ }
+ });
+
+ ambiguityDetector.addAmbiguityListener(new AmbiguityListener(){
+
+ @Override
+ public void ambiguityResolved(String procedureName, String fileName)
+ {
+ Collection<String> ambigFiles = ambiguousProcToFiles.get(procedureName);
+
+ ambigFiles.remove(fileName);
+
+ if (ambigFiles.size() == 0)
+ ambiguousProcToFiles.remove(procedureName);
+
+ // [this check is not necessary. if it was ambiguous, it did not have errors.]
+ //if (errorFiles.containsKey(fileName))
+ // return;
+ // No more file errors
+
+ for (Collection<String> fileNames : ambiguousProcToFiles.values())
+ {
+ if (fileNames.contains(fileName))
+ return;
+ }
+ // No more ambiguities for file
+
+ notifyAllErrorsCorrected(fileName);
+ }
+
+ @Override
+ public void ambiguityDetected(String procedureName, Map<String, Procedure> fileToProcedure)
+ {
+ ambiguousProcToFiles.put(procedureName, new ArrayList<String>(fileToProcedure.keySet()));
+ for (String fileName : fileToProcedure.keySet())
+ notifyErrorDetected(fileName);
+ }
+ });
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ return errorFiles.size() > 0 || ambiguousProcToFiles.size() > 0;
+ }
+
+ @Override
+ public boolean hasErrors(String fileName)
+ {
+ if (errorFiles.containsKey(fileName))
+ return true;
+
+ for (Collection<String> conflictingFiles : ambiguousProcToFiles.values())
+ if (conflictingFiles.contains(fileName))
+ return true;
+ return false;
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrorMessages = new ArrayList<ProcedureErrorMessage>();
+ // Not the most efficient impl possible
+ allErrorMessages.addAll(fileErrorDetector.getAllErrors());
+ allErrorMessages.addAll(ambiguityDetector.getAllErrors());
+ return allErrorMessages;
+ }
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ TreeMap<String, Void> allErrorFiles = new TreeMap<String, Void>();
+
+ for(String fileName : errorFiles.keySet())
+ allErrorFiles.put(fileName, null);
+
+ for(Collection<String> files : ambiguousProcToFiles.values())
+ for(String fileName : files)
+ allErrorFiles.put(fileName, null);
+
+ return allErrorFiles.keySet();
+ }
+
+ public Collection<String> getErrorMessages(String fileName)
+ {
+ ArrayList<String> messages = new ArrayList<String>();
+
+ for(ProcedureErrorMessage pem : getAllErrors())
+ if(pem.fileNames.contains(fileName))
+ messages.add(pem.toString());
+
+ return messages;
+ }
+
+ // These error event will directly update the gui => run on event dispatcher thread
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ errorListeners.remove(listener);
+ }
+
+ private void notifyErrorDetected(final String fileName)
+ {
+ for (ErrorListener listener : errorListeners)
+ listener.errorsDetected(fileName);
+ }
+
+ private void notifyAllErrorsCorrected(final String fileName)
+ {
+ for (ErrorListener listener : errorListeners)
+ listener.allErrorsCorrected(fileName);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java b/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java
new file mode 100644
index 0000000..7d74560
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java
@@ -0,0 +1,73 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.HashMap;
+import java.util.Set;
+
+public class GlobalVariableTable
+{
+ /**
+ * All defined variables with their current value.
+ */
+ protected HashMap<String, String> globale;
+
+ public GlobalVariableTable()
+ {
+ globale = new HashMap<String, String>();
+ }
+
+ public Set<String> getVariables()
+ {
+ return globale.keySet();
+ }
+
+ public String getValue(String var)
+ {
+ return globale.get(var.toLowerCase());
+ }
+
+ public void define(String var, String value)
+ {
+ globale.put(var.toLowerCase(), value);
+ }
+
+ public void deleteVariable(String st)
+ {
+ globale.remove(st.toLowerCase());
+ }
+
+ /**
+ * Delete all Variables from the workspace
+ */
+ public void deleteAllVariables()
+ {
+ globale.clear();
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java b/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java
new file mode 100644
index 0000000..2e2afc8
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java
@@ -0,0 +1,106 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.procedures.ProcedureErrorType;
+
+/**
+ * This is used to report document structure errors
+ * or ambiguity errors, either within or among files.
+ * @author Marko Zivkovic
+ *
+ */
+public class ProcedureErrorMessage
+{
+ ProcedureErrorType type;
+ String procedureDescription;
+ Collection <String> fileNames;
+
+ /**
+ * The description may be either the name (if known) or the line, where the error was found.
+ * @param type
+ * @param procedureDescription
+ * @param fileName
+ */
+ public ProcedureErrorMessage(ProcedureErrorType type, String procedureDescription, String fileName)
+ {
+ this.type = type;
+ this.procedureDescription = procedureDescription;
+ this.fileNames = new ArrayList<String>();
+ this.fileNames.add(fileName);
+ }
+
+ /**
+ * This can be used for ambiguity messages
+ * @see #ProcedureErrorMessage(ProcedureErrorType, String, String)
+ */
+ public ProcedureErrorMessage(ProcedureErrorType type, String procedureDescription, Collection<String> fileNames)
+ {
+ this.type = type;
+ this.procedureDescription = procedureDescription;
+ this.fileNames = fileNames;
+ }
+
+ public ProcedureErrorType getErrorType()
+ {
+ return type;
+ }
+
+ public String getProcedureDescription()
+ {
+ return procedureDescription;
+ }
+
+ public Collection<String> getFileNames()
+ {
+ return fileNames;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (String fileName : fileNames)
+ {
+ sb.append(fileName);
+ sb.append(", ");
+ }
+ sb.delete(sb.length()-2, sb.length()-1);
+
+ sb.append(procedureDescription);
+ sb.append(": ");
+ sb.append(Logo.messages.getString(type.getDescription()));
+
+ return sb.toString();
+ }
+
+} \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/userspace/PropertyListTable.java b/logo/src/xlogo/kernel/userspace/PropertyListTable.java
new file mode 100644
index 0000000..7764d60
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/PropertyListTable.java
@@ -0,0 +1,165 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.HashMap;
+import java.util.Set;
+
+public class PropertyListTable
+{
+
+ /**
+ * For all Property Lists
+ */
+ private HashMap<String, HashMap<String, String>> propList;
+
+ public PropertyListTable()
+ {
+ propList = new HashMap<String, HashMap<String,String>>();
+ }
+
+
+ /**
+ * This method adds in the property List called "name" a value for the
+ * corresponding key
+ *
+ * @param name
+ * The property List 's name
+ * @param key
+ * The key for the value to add
+ * @param value
+ * The value to add
+ */
+ public void addPropList(String name, String key, String value)
+ {
+ if (!propList.containsKey(name))
+ {
+ propList.put(name, new HashMap<String, String>());
+ }
+ propList.get(name).put(key, value);
+ }
+
+ /**
+ * This method removes a Property List
+ *
+ * @param name
+ * The property List 's name
+ */
+ public void removePropList(String name)
+ {
+ if (propList.containsKey(name))
+ {
+ propList.remove(name);
+ }
+ }
+
+ /**
+ * This method removes a couple (key, value) from a Property List
+ *
+ * @param name
+ * The property List 's name
+ * @param key
+ * The key to delete
+ */
+ public void removePropList(String name, String key)
+ {
+ if (propList.containsKey(name))
+ {
+ if (propList.get(name).containsKey(key))
+ propList.get(name).remove(key);
+ if (propList.get(name).isEmpty())
+ propList.remove(name);
+ }
+ }
+
+ /**
+ * This method returns a list that contains all couple key value
+ *
+ * @param name
+ * The Property List's name
+ * @return A list with all keys-values
+ */
+ public String displayPropList(String name)
+ {
+ if (propList.containsKey(name))
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ Set<String> set = propList.get(name).keySet();
+ for (String key : set)
+ {
+ sb.append(key);
+ sb.append(" ");
+ String value = propList.get(name).get(key);
+ if (value.startsWith("\""))
+ value = value.substring(1);
+ sb.append(value);
+ sb.append(" ");
+ }
+ sb.append("] ");
+ return sb.toString();
+ }
+ else
+ return "[ ] ";
+ }
+
+ /**
+ * This method return a value from a Property List
+ *
+ * @param name
+ * The Property List's name
+ * @param key
+ * The key for the chosen value
+ * @return The value for this key
+ */
+ public String getPropList(String name, String key)
+ {
+ if (!propList.containsKey(name)) { return "[ ]"; }
+ if (!propList.get(name).containsKey(key))
+ return "[ ]";
+ return propList.get(name).get(key);
+ }
+
+ /**
+ * Returns all defined Property List names
+ *
+ * @return A list with all Property List names
+ */
+ public Set<String> getPropListKeys()
+ {
+ return propList.keySet();
+ }
+
+ /**
+ * Delete all property Lists from the workspace
+ */
+ public void deleteAllPropertyLists()
+ {
+ propList.clear();
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/UserSpace.java b/logo/src/xlogo/kernel/userspace/UserSpace.java
new file mode 100644
index 0000000..d3b79d6
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/UserSpace.java
@@ -0,0 +1,790 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.interfaces.BroadcasterErrorFileContainer;
+import xlogo.interfaces.X4SModeSwitcher;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.gui.GuiMap;
+import xlogo.kernel.userspace.context.ContextManager;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.files.LogoFileContainer;
+import xlogo.kernel.userspace.files.LogoFilesManager;
+import xlogo.kernel.userspace.procedures.ExecutablesProvider;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.kernel.userspace.procedures.ProceduresManager;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * This is a facade for what was before called Workspace in XLogo, and much more.
+ * XLogo's Workspace had:
+ * <p>
+ * - All Defined Procedures <br>
+ * - All Global variables <br>
+ * - All GUI Objects (generated by a Logo program)<br>
+ * - All Property lists
+ * <p>
+ * The new "Workspace" does not resemble the old workspace at all, although it still provides access to these symbol tables.
+ * <p>
+ * New in XLogo4Schools: multiple Files. Procedures must be unique within all files of a context.
+ * If multiple procedures with the same name are defined <br>
+ * - within one file : The file will not be usable, until errors and ambiguities are fixed (like in XLogo), <br>
+ * - among multiple files : All of these files will not be executable, <br> //TODO or the ambiguous procedures will not be executed
+ * unless the ambiguity is resolved.
+ * <p>
+ * New are also the Contexts for user mode, contest mode, network mode.
+ * The contest mode is completely new. In XLogo, Network mode was implemented by replacing the whole workspace.
+ * Now only the Contexts with symbol tables are replaced. Why? Because we have an event driven architecture now.
+ * If the whole workspace (referring to XLogo's workspace, including event dispatchers) would be replaced, then all subscribers to the workspace would have to be re-mapped.
+ * By keeping the LogoFileManager and the ProceduresManager, subscribers to the user space (which are redirected to the managers) must not be re-mapped when a context switch happens.
+ * <p>
+ * (Option for future: Procedures can be qualified with
+ * {@code fileName.procedureName}. But : A dot is not a legal character for a
+ * name anymore)
+ *
+ * @author Marko Zivkovic
+ *
+ */
+public class UserSpace implements X4SModeSwitcher, LogoFileContainer, BroadcasterErrorFileContainer,
+ ExecutablesProvider, FileErrorCollector
+{
+
+ /**
+ * This is included for every call that is delegated. This way all errors are caught and if we're lucky,
+ * The application can still be used or terminated normally. At least the user has discovered a new bug and he or she
+ * can report it.
+ *
+ * TODO write a log file?
+ *
+ * @param e
+ */
+ public static void showErrorDialog(Exception e)
+ {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+
+ DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.WS_ERROR_TITLE),
+ sw.toString());
+ }
+
+ private final ContextManager contextManager; // Switches context when modes change, notifies the other managers. & causes mode and clock events
+ private final LogoFilesManager filesManager; // handles file specific requests & causes file events
+ private final ProceduresManager proceduresManager; // handles procedure specific requests & causes defined/undefined events
+ private final ErrorManager errorManager; // handles error specific requests & causes error events
+
+ public UserSpace()
+ {
+ contextManager = new ContextManager();
+ proceduresManager = new ProceduresManager(contextManager);
+ filesManager = new LogoFilesManager(contextManager);
+ errorManager = new ErrorManager(filesManager, proceduresManager);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * X4S MODE SWITCHER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String getSerializedContext()
+ {
+ try
+ {
+ return contextManager.getSerializedContext();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return "";
+ }
+
+ @Override
+ public boolean isNetworkMode()
+ {
+ try
+ {
+ return contextManager.isNetworkMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public boolean isRecordMode()
+ {
+ try
+ {
+ return contextManager.isRecordMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public boolean isUserMode()
+ {
+ try
+ {
+ return contextManager.isUserMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return true;
+ }
+
+ @Override
+ public void startRecordMode(String[] fileNames) throws IllegalArgumentException, IOException
+ {
+ try
+ {
+ contextManager.startRecordMode(fileNames);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void stopRecordMode()
+ {
+ try
+ {
+ contextManager.stopRecordMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ public void pushNetworkMode(String networkContext)
+ {
+ try
+ {
+ contextManager.pushNetworkMode(networkContext);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void popNetworkMode()
+ {
+ try
+ {
+ contextManager.popNetworkMode();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void addModeChangedListener(ModeChangeListener listener)
+ {
+ try
+ {
+ contextManager.addModeChangedListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeModeChangedListener(ModeChangeListener listener)
+ {
+ try
+ {
+ contextManager.removeModeChangedListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ try
+ {
+ contextManager.addBroadcastListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ try
+ {
+ contextManager.removeBroadcastListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * LOGO FILE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String[] getFileNames()
+ {
+ try
+ {
+ return filesManager.getFileNames();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new String[0];
+ }
+
+ @Override
+ public String readFile(String name)
+ {
+ try
+ {
+ return filesManager.readFile(name);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return "";
+ }
+
+ @Override
+ public String getOpenFileName()
+ {
+ try
+ {
+ return filesManager.getOpenFileName();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public boolean existsFile(String name)
+ {
+ try
+ {
+ return filesManager.existsFile(name);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void createFile(String fileName) throws IOException
+ {
+ try
+ {
+ filesManager.createFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeFile(String fileName)
+ {
+ try
+ {
+ filesManager.removeFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * The file is expected to exist on the file system and to have a .lgo extension
+ * @param filePath
+ * @throws IOException
+ */
+ public void importFile(File filePath) throws IOException
+ {
+ try
+ {
+ filesManager.importFile(filePath);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * @throws IOException
+ * @see {@link LogoFilesManager#exportFile(String, File)}
+ */
+ public void exportFile(String fileName, File dest) throws IOException
+ {
+ try
+ {
+ filesManager.exportFile(fileName, dest);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * @throws IOException
+ * @see {@link LogoFilesManager#exportFile(String, File, String)}
+ */
+ public void exportFile(String fileName, File location, String targetName) throws IOException
+ {
+ try
+ {
+ filesManager.exportFile(fileName, location, targetName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public String makeUniqueFileName(String base)
+ {
+ try
+ {
+ return filesManager.makeUniqueFileName(base);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ try
+ {
+ filesManager.renameFile(oldName, newName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void openFile(String fileName)
+ {
+ try
+ {
+ filesManager.openFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void closeFile(String fileName)
+ {
+ try
+ {
+ filesManager.closeFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void writeFileText(String fileName, String content) throws IOException
+ {
+ try
+ {
+ filesManager.writeFileText(fileName, content);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void storeFile(String fileName) throws IOException
+ {
+ try
+ {
+ filesManager.storeFile(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void addFileListener(FileContainerChangeListener listener)
+ {
+ try
+ {
+ filesManager.addFileListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeFileListener(FileContainerChangeListener listener)
+ {
+ try
+ {
+ filesManager.removeFileListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ public String getLastEditedFileName()
+ {
+ try
+ {
+ return filesManager.getLastEditedFileName();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ public boolean isFilesListEditable()
+ {
+ try
+ {
+ return filesManager.isFilesListEditable();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return true;
+ }
+
+ public boolean hasErrors(String fileName)
+ {
+ try
+ {
+ return filesManager.hasErrors(fileName) || proceduresManager.hasAmbiguousProcedures(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void editAll()
+ {
+ try
+ {
+ filesManager.editAll();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /**
+ * Resets the UserSpace by deleting all files (and procedures), all
+ * variables and all property lists.
+ * <p>
+ * Note : deleting all files will cause a chain of events.
+ */
+ public void eraseAll()
+ {
+ try
+ {
+ filesManager.eraseAll(); // should remove all files and procedures and fire the right events
+ LogoContext context = contextManager.getContext();
+ context.getGlobals().deleteAllVariables();
+ context.getPropertyLists().deleteAllPropertyLists();
+ context.getGuiMap().clear();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * This is the implementation of the Logo Command define.
+ * Its effect has changed since we have multiple files now, but the semantics are preserved.
+ * Logo programs will behave exactly as before. <p>
+ * If the procedure is ambiguous, cannot decide which one to redefine => IllegalArgumentException <br>
+ * If the procedure is already defined once, that definition will be redefined in its original LogoFile. <br>
+ * If the procedure is not yet defined, a new File will be created "Generated Procedures" if does not yet exist,
+ * and the new procedure will be put there.
+ *
+ * @throws IllegalArgumentException if the procedure's name is ambiguous in the current context: I Don't know which one to redefine.
+ * @throws IllegalArgumentException if the procedure is not executable or it's owner does not exist in this context.
+ * @throws IOException - if the file "gerated procedure" could not be created in the context.
+ */
+ @Override
+ public void defineProcedure(Procedure procedure) throws IOException
+ {
+ try
+ {
+ String procedureName = procedure.getName();
+ if (proceduresManager.isProcedureAmbiguous(procedureName))
+ throw new IllegalArgumentException("Attempt to redefine ambiguous procedure.");
+
+ if (!isExecutable(procedureName))
+ {
+ String fileName = Logo.messages.getString("ws.generated.procedure");
+ if (!existsFile(fileName))
+ createFile(fileName);
+ }
+
+ proceduresManager.defineProcedure(procedure);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public Collection<Procedure> getExecutables()
+ {
+ try
+ {
+ return proceduresManager.getExecutables();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<Procedure>();
+ }
+
+ @Override
+ public Procedure getExecutable(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.getExecutable(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public boolean isExecutable(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.isExecutable(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void eraseProcedure(String procedureName)
+ {
+ try
+ {
+ proceduresManager.eraseProcedure(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * The following symbol tables do not need a manager and no events
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ public GuiMap getGuiMap()
+ {
+ try
+ {
+ return contextManager.getContext().getGuiMap();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new GuiMap();
+ }
+
+ public PropertyListTable getPropertyLists()
+ {
+ try
+ {
+ return contextManager.getContext().getPropertyLists();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new PropertyListTable();
+ }
+
+ public GlobalVariableTable getGlobals()
+ {
+ try
+ {
+ return contextManager.getContext().getGlobals();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new GlobalVariableTable();
+ }
+
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ try
+ {
+ return proceduresManager.getAllProcedureNames();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ try
+ {
+ return proceduresManager.getAllProcedureNames(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.getProcedureOwner(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return null;
+ }
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ try
+ {
+ proceduresManager.addProcedureMapListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ try
+ {
+ proceduresManager.removeProcedureMapListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ /*
+ * Errors & Ambiguities
+ */
+
+ /**
+ * conflicting w.r.t. ambiguity of their procedures
+ */
+ @Override
+ public Collection<String> getAllConflictingFiles()
+ {
+ try
+ {
+ return proceduresManager.getAllConflictingFiles();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+ @Override
+ public boolean hasAmbiguousProcedures(String fileName)
+ {
+ try
+ {
+ return proceduresManager.hasAmbiguousProcedures(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public boolean isProcedureAmbiguous(String procedureName)
+ {
+ try
+ {
+ return proceduresManager.isProcedureAmbiguous(procedureName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public void addAmbiguityListener(AmbiguityListener listener)
+ {
+ try
+ {
+ proceduresManager.addAmbiguityListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeAmbiguityListener(AmbiguityListener listener)
+ {
+ try
+ {
+ proceduresManager.removeAmbiguityListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ try
+ {
+ return errorManager.hasErrors();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return false;
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ try
+ {
+ return errorManager.getAllErrors();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<ProcedureErrorMessage>();
+ }
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ try
+ {
+ return errorManager.getAllErroneousFiles();
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+
+ }
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ try
+ {
+ errorManager.addErrorListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ try
+ {
+ errorManager.removeErrorListener(listener);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ }
+
+ public Collection<String> getErrorMessages(String fileName)
+ {
+ try
+ {
+ return errorManager.getErrorMessages(fileName);
+ }
+ catch (Exception e) { showErrorDialog(e); }
+ return new ArrayList<String>();
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/ContextManager.java b/logo/src/xlogo/kernel/userspace/context/ContextManager.java
new file mode 100644
index 0000000..fe5dab7
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/ContextManager.java
@@ -0,0 +1,301 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.interfaces.X4SModeSwitcher;
+
+/**
+ * One of the four main roles in the {@link xlogo.kernel.userspace.UserSpace} <br>
+ * <b> The Contexts and Priorities and multiplicity</b>
+ * 1. UserContext [1] : default context, lowest priority, cannot be killed.
+ * 2. RecordContext [0..1] : medium priority, killing it will kill all networking contexts too.
+ * 3. NetworkContext [0..*] : highest priority, can be stacked.
+ *
+ * @author Marko
+ */
+public class ContextManager implements X4SModeSwitcher, ContextSwitcher, MessageBroadcaster
+{
+ private LogoContext currentContext;
+
+ private final UserContext userContext = new UserContext();
+ private RecordContext recordContext;
+ private final Stack<NetworkContext> networkStack = new Stack<NetworkContext>();
+
+ private MessageListener contextMessageListener;
+
+ public ContextManager()
+ {
+ currentContext = userContext;
+ initContextMessageListener();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * CONTEXT PROVIDER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * After a context has been removed by stopXXXMode(), contextSwitch() will determine the next current context.
+ * It only peeks at the context collections, but does not modify them. Calling this does never harm,
+ * but after one of the context collections has been changed, it should be called.<br>
+ * It notifies ContextProviderListeners if the context has actually changed after calling this.
+ * <p>
+ * The policy for context switches is defines in the interface here : {@link X4SModeSwitcher}
+ * <p>
+ * Note, this should be called before the mode switch events, because we first want to completely context switch, before we change the mode.
+ * Generally always publish events after the event has actually occurred.
+ *
+ */
+ protected void contextSwitch()
+ {
+ LogoContext old = currentContext;
+
+ if (networkStack.size() > 0)
+ currentContext = networkStack.peek();
+ else
+ currentContext = recordContext != null ? recordContext : userContext;
+
+ if(old != currentContext)
+ notifyContextSwitched(currentContext);
+ }
+
+ /**
+ * returns the current context after the policy described in the in the interface {@link X4SModeSwitcher}
+ */
+ @Override
+ public LogoContext getContext()
+ {
+ return currentContext;
+ }
+
+ // Context switch : internal communication : no direct gui update, must not run on event dispatcher thread
+
+ private final ArrayList<ContextSwitchListener> contextSwitchListeners = new ArrayList<ContextSwitcher.ContextSwitchListener>();
+
+ @Override
+ public void addContextSwitchListener(ContextSwitchListener listener)
+ {
+ contextSwitchListeners.add(listener);
+ }
+
+ @Override
+ public void removeContextSwitchListener(ContextSwitchListener listener)
+ {
+ contextSwitchListeners.remove(listener);
+ }
+
+ public void notifyContextSwitched(LogoContext newContext)
+ {
+ for (ContextSwitchListener listener : contextSwitchListeners)
+ listener.contextSwitched(newContext);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * MODE SWITCHER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String getSerializedContext()
+ {
+ return currentContext.toString();
+ }
+
+ @Override
+ public boolean isUserMode()
+ {
+ return currentContext instanceof UserContext;
+ }
+
+ @Override
+ public boolean isRecordMode()
+ {
+ return currentContext instanceof RecordContext;
+ }
+
+ @Override
+ public boolean isNetworkMode()
+ {
+ return currentContext instanceof NetworkContext;
+ }
+
+ /**
+ * A record mode can only be started from user mode. In other words,
+ * a record mode cannot be started, if there is another <b>record or network</b> mode active.
+ * @throws IllegalStateException If current mode is not user mode.
+ * @throws IllegalArgumentException If the fileNames are not well formed, null, or ambiguous
+ * @throws IOException If the record files could not be created => recording is impossible => record mode is impossible
+ */
+ @Override
+ public void startRecordMode(String[] fileNames) throws IllegalArgumentException, IOException
+ {
+ if (!isUserMode())
+ throw new IllegalStateException();
+
+ recordContext = new RecordContext(fileNames);
+ recordContext.addBroadcastListener(contextMessageListener);
+
+ contextSwitch();
+
+ notifyContestModeStarted();
+ }
+
+ /**
+ * This will first regularly kill all network contexts, one after the other. Listeners will be notified for every kill. <br>
+ * Afterwards it will kill the current record context. <br>
+ * When this call returns, current mode is user mode.
+ *
+ * @throws IllegalStateException If there is no recordContext available
+ */
+ @Override
+ public void stopRecordMode() throws IllegalStateException
+ {
+ if (recordContext == null)
+ return; // might be a quick double klick that causes this
+
+ while(isNetworkMode())
+ popNetworkMode();
+
+ recordContext = null;
+
+ contextSwitch();
+
+ notifyRecordModeStopped();
+ }
+
+ /**
+ * @throws IllegalArgumentException If serializedContext is corrupted
+ */
+ @Override
+ public void pushNetworkMode(String serializedContext) throws IllegalArgumentException
+ {
+ NetworkContext nc = new NetworkContext(serializedContext);
+ networkStack.push(nc);
+
+ contextSwitch();
+
+ if (networkStack.size() == 1)
+ notifyNetworkModeStarted();
+ }
+
+ /**
+ * This will kill the current network context.
+ * @throws IllegalStateException - if there is no network context to kill.
+ */
+ @Override
+ public void popNetworkMode() throws IllegalStateException
+ {
+ if (!isNetworkMode())
+ throw new IllegalStateException("There is no network context to kill.");
+
+ networkStack.pop();
+
+ contextSwitch();
+
+ if (!isNetworkMode())
+ notifyNetworkModeStopped();
+ }
+
+ // Mode Change Listeners : update GUI => run on event dispatcher thread
+
+ private ArrayList<ModeChangeListener> modeChangeListeners = new ArrayList<ModeChangeListener>();
+
+ @Override
+ public void addModeChangedListener(ModeChangeListener listener)
+ {
+ modeChangeListeners.add(listener);
+ }
+
+ @Override
+ public void removeModeChangedListener(ModeChangeListener listener)
+ {
+ modeChangeListeners.remove(listener);
+ }
+
+ private void notifyContestModeStarted()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.recordModeStarted();
+ }
+
+ private void notifyRecordModeStopped()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.recordModeStopped();
+ }
+
+ private void notifyNetworkModeStarted()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.networkModeStarted();
+ }
+
+ private void notifyNetworkModeStopped()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.networkModeStopped();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * CONTEXT MESSAGE LISTENERS (used to broadcast clock events from record mode)
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ private void initContextMessageListener()
+ {
+ contextMessageListener = new MessageListener(){
+
+ @Override
+ public void messageEvent(String source, String message)
+ {
+ for (MessageListener listener : broadcastListeners)
+ listener.messageEvent(source, message);
+ }
+ };
+ }
+
+ private final ArrayList<MessageListener> broadcastListeners = new ArrayList<MessageListener>();
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ broadcastListeners.add(listener);
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ broadcastListeners.remove(listener);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java b/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java
new file mode 100644
index 0000000..e38c645
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java
@@ -0,0 +1,43 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+
+public interface ContextSwitcher
+{
+ public LogoContext getContext();
+
+ public void addContextSwitchListener(ContextSwitchListener listener);
+
+ public void removeContextSwitchListener(ContextSwitchListener listener);
+
+ public interface ContextSwitchListener
+ {
+ public void contextSwitched(LogoContext newContext);
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/LogoContext.java b/logo/src/xlogo/kernel/userspace/context/LogoContext.java
new file mode 100644
index 0000000..15bdc1f
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/LogoContext.java
@@ -0,0 +1,312 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.interfaces.ProcedureMapper.ProcedureMapListener;
+import xlogo.kernel.gui.GuiMap;
+import xlogo.kernel.userspace.GlobalVariableTable;
+import xlogo.kernel.userspace.PropertyListTable;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.storage.global.GlobalConfig;
+/**
+ * A LogoContext contains all the symbol tables for execution of Logo programs <p>
+ * Parts of this resemble the old Workspace class, but this has a new purpose and it is used differently.
+ * In XLogo4Schools, a LogoContext is only a container for a Logo environment - all the symbol tables.
+ * <p>
+ * Note: To be consistent, the execution stack should be included in the context too, but this would (for now) need too much work
+ * to refactor the existing interpreter. It is for sure something that should be done in the future, when the entire interpreter is refactored.
+ *
+ * @author Marko Zivkovic -
+ * @author Lo�c Le Coq - methods inherited from XLogo are marked with author-tag
+ */
+public class LogoContext
+{
+ private LogoFile openFile;
+
+ private final GlobalVariableTable globals = new GlobalVariableTable();
+
+ private final PropertyListTable propertyLists = new PropertyListTable();
+
+ private final Map<String, LogoFile> files = new HashMap<String, LogoFile>();
+
+ private final HashMap<String, HashMap<String, Procedure>> procedureTable = new HashMap<String, HashMap<String, Procedure>>();
+
+ // private Map<String, Procedure> executables = new HashMap<String,
+ // Procedure>();
+
+ private final GuiMap guiMap = new GuiMap();
+
+ public LogoContext() { }
+
+ /*
+ * Symbol table getters.
+ */
+
+ /**
+ * All the files in this context
+ * @return
+ */
+ public Map<String, LogoFile> getFilesTable()
+ {
+ return files;
+ }
+
+ /**
+ * All Executable procedures from all files. Procedures with equal names,
+ * from different files, will be included in the same list. <br>
+ * This Map is used to keep track of all defined procedures and to resolve
+ * name conflicts.
+ */
+ public HashMap<String, HashMap<String, Procedure>> getProcedureTable()
+ {
+ return procedureTable;
+ }
+
+ /**
+ * Global Logo variables
+ * @return
+ */
+
+ public GlobalVariableTable getGlobals()
+ {
+ return globals;
+ }
+
+ /**
+ * Logo property lists
+ * @return
+ */
+ public PropertyListTable getPropertyLists()
+ {
+ return propertyLists;
+ }
+
+ /**
+ * For all Gui Objects (Buttons, ComboBoxes...)
+ */
+ public GuiMap getGuiMap()
+ {
+ return guiMap;
+ }
+
+ /*
+ * Context dependent operations
+ */
+
+ public String[] getFileOrder()
+ {
+ return files.keySet().toArray(new String[files.size()]);
+ }
+
+ public void openFile(String fileName)
+ {
+ openFile = files.get(fileName);
+ }
+
+ public void closeFile()
+ {
+ openFile = null;
+ }
+
+ public LogoFile getOpenFile()
+ {
+ return openFile;
+ }
+
+ /**
+ * This is the preferred method to create a file within a context, because different contexts prefer different LogoFile configurations.
+ * @throws IllegalStateException if fileName already exists in the files table
+ * @throws IOException If the file could not be created on the file system and the text not written.
+ */
+ public void createFile(String fileName, String text) throws IOException
+ {
+ if (files.containsKey(fileName))
+ throw new IllegalStateException("Attempt to create already existing file.");
+
+ LogoFile file = LogoFile.createNewFile(fileName);
+ file.setText(text);
+ file.store();
+
+ files.put(fileName, file);
+ installListeners(file);
+ }
+
+ public void importFile(File path, String newFileName) throws IOException
+ {
+ if (files.containsKey(newFileName))
+ throw new IllegalStateException("Attempt to create already existing file.");
+
+ if (!path.isFile())
+ throw new IllegalArgumentException("The specified file does not exist : " + path.toString());
+
+ String extension = GlobalConfig.LOGO_FILE_EXTENSION;
+ if(!path.getName().endsWith(extension))
+ throw new IllegalArgumentException("Only accept " + extension + " files, but received : " + path.toString());
+
+ String fileName = path.getName();
+ fileName = path.getName().substring(0, fileName.length() - extension.length());
+
+ LogoFile file = LogoFile.importFile(path, newFileName);
+ files.put(newFileName, file);
+ installListeners(file);
+ }
+
+ /**
+ * Note: does not perform checks on validity of file names.
+ * @param oldName
+ * @param newName
+ */
+ public void renameFile(String oldName, String newName)
+ {
+ if (oldName.equals(newName))
+ return;
+
+ LogoFile file = files.get(oldName);
+
+ // must first re-map in files table because file.setFileName fires events
+ files.remove(oldName);
+ files.put(newName, file);
+
+ file.setFileName(newName);
+ }
+
+ private final ArrayList<ProcedureMapListener> procedureMapListener = new ArrayList<ProcedureMapListener>();
+
+ /**
+ * This should be used when a file is created or added to this context.
+ * Listeners that were previously added with {@link #addProcedureMapListener(ProcedureMapListener)} will be installed.
+ * @param file
+ */
+ public void installListeners(LogoFile file)
+ {
+ for (ProcedureMapListener listener : procedureMapListener)
+ file.addProcedureMapListener(listener);
+ }
+
+ /**
+ * This helps the Procedure Manager to register to ProcedureMap events from the logo files.
+ * The ProcedureManager does not need to be notified explicitly when a new file is created.
+ * @see #installListeners(LogoFile)
+ * @param listener
+ */
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListener.add(listener);
+ for (LogoFile file : files.values())
+ file.addProcedureMapListener(listener);
+ }
+
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListener.remove(listener);
+ for (LogoFile file : files.values())
+ file.removeProcedureMapListener(listener);
+ }
+
+ /*
+ * MISC
+ */
+
+ /**
+ * That's the String that is sent via TCP, and interpreted by the receiver in {@link NetworkContext},
+ * using {@link #setWorkspace(String)}
+ * @author Lo�c Le Coq
+ * @author Marko Zivkovic
+ * refactored using the new data structures.
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ for (String key : getGlobals().getVariables())
+ {
+ sb.append("-");
+ sb.append(key);
+ sb.append("\n");
+ sb.append(getGlobals().getValue(key));
+ sb.append("\n");
+ }
+
+ for (HashMap<String, Procedure> fileToProc : procedureTable.values())
+ {
+ if (fileToProc.size() != 1)
+ continue;
+
+ Procedure procedure = null;
+ for (Procedure uniqueProc : fileToProc.values())
+ procedure = uniqueProc; // retrieve the only procedure in fileToProc
+
+ sb.append(Logo.messages.getString("pour") + " " + procedure.name);
+ for (int j = 0; j < procedure.nbparametre; j++)
+ {
+ sb.append(" :" + procedure.variable.get(j));
+ }
+ sb.append("\n");
+ sb.append(procedure.instruction);
+ sb.append(Logo.messages.getString("fin"));
+ sb.append("\n\n");
+ }
+ return (new String(sb));
+ }
+
+ /**
+ * @return true : {@link FileContainerChangeListener} should be fired.
+ */
+ public boolean fireFileEvents()
+ {
+ return true;
+ }
+
+ /**
+ * @return true : {@link AmbiguityListener},
+ * {@link ExecutablesChangedListener},
+ * {@link ProceduresDefinedListener} should be fired.
+ */
+ public boolean fireProcedureEvents()
+ {
+ return true;
+ }
+
+ /**
+ * Default is true. Other contexts might override this.
+ * @return Whether it is allowed to create, delete, or rename files.
+ * @note : this is only a suggestion for the files manager and the gui, modification of the table is still possible
+ */
+ public boolean isFilesListEditAllowed()
+ {
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/NetworkContext.java b/logo/src/xlogo/kernel/userspace/context/NetworkContext.java
new file mode 100644
index 0000000..937e513
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/NetworkContext.java
@@ -0,0 +1,206 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.files.LogoFile;
+
+/**
+ * The network context is very different compared to the other contexts. <br>
+ * 1. It parses its contents from a serialized context string ({@link #toString()}) <br>
+ * 2. Its files are created virtually ({@link xlogo.storage.Storable#isVirtual()}). They are gone when the network mode stops, leaving no trace. <br>
+ * 3. {@link #fireFileEvents()}} and {@link fireProcedureEvents()}} suggest the managers to not fire events,
+ * because the network files are not meant to be displayed in the editor.
+ * In XLogo this effect was achieved by setting the property affichable of procedures.
+ * @author Marko Zivkovic
+ */
+public class NetworkContext extends LogoContext
+{
+
+ public NetworkContext(String networkContext) throws IllegalArgumentException
+ {
+ super();
+ setContext(networkContext);
+ }
+
+ /**
+ * This is used to include a remote context into XLogo4Schools and allow remote
+ * procedure calls via tcp. XLogo had this implemented in the Workspace class.
+ * <p>
+ * Initially, in remote mode, the workspace was temporarily replaced by a new one.
+ * To indicate that the workspace with its procedures should not be displayed
+ * in the editor, Lo�c had added setAffichable(false), meaning not displayable.<br>
+ *
+ * Note that in XLogo, the workspace contained exactly one file. Therefore
+ * a simple swap in swap would do. However, in XLogo4Schools, we need more
+ * state information, and some of the state must be preserved.
+ * For example, in network mode, I do not want the user's procedures
+ * to be removed from the procedure list. I just want to disable the list.<br>
+ * <p>
+ * With this Network context, no events will be fired that would indicate,
+ * that some procedures or files have changed. But the ContextChangedListeners
+ * will be notified that we are now in network mode, thus they can disable
+ * the files list.
+ * <p>
+ * <p>
+ * Note that in XLogo, the workspace text has to be set in the editor just
+ * because analysProcedure() reads the Logo source code from there.
+ * In the network mode, It was actually not necessary to have it in the editor,
+ * because they didn't intend do display the text anyway (setAffichable(false)).
+ * setWorkspace and toString have probably been added late, so they just "hacked"
+ * that new property in.
+ * The existing "architecture" did not allow a cleaner extension of the
+ * system, so they must have added "affichable" for this only purpose.
+ * <p>
+ * In the current implementation, "affichable" is completely removed. <br>
+ * I regulate this effect by either firing events or not.
+ * <p>
+ * Note, parsing of the context is inherited from XLogo. Its implementation is very optimistic.
+ * It assumes that only well-formed strings are provided. Therefore it throws no actual exceptions.
+ * I added IllegalArgumentException to provide a more general interface,
+ * and to prepare clients for the future (?) when this parsing is re-implemented.
+ *
+ * @param app
+ * @param wp
+ * @author Marko Zivkovic, Loic
+ */
+ protected void setContext(String wp) throws IllegalArgumentException
+ {
+ StringReader sr = new StringReader(wp);
+ BufferedReader bfr = new BufferedReader(sr);
+ try
+ {
+ String input = parseVariables(bfr);
+
+ if (input != null)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(input);
+ sb.append("\n");
+ readerToBuilder(sb, bfr);
+
+ String vFileName = "netowork_file";
+ createFile(vFileName, "");
+ LogoFile file = getFilesTable().get(vFileName);
+ file.setText(sb.toString());
+ }
+ }
+ catch (IOException e)
+ {}
+ finally
+ {
+ try
+ {
+ bfr.close();
+ }
+ catch (IOException e)
+ {}
+ sr.close();
+ }
+ }
+
+ /**
+ * append all lines of br to sb
+ */
+ private void readerToBuilder(StringBuilder sb, BufferedReader br) throws IOException
+ {
+ String line = null;
+ while((line = br.readLine()) != null)
+ {
+ sb.append(line);
+ sb.append("\n");
+ }
+ sb.deleteCharAt(sb.length()-1);
+ }
+
+ private String parseVariables(BufferedReader bfr) throws IOException, IllegalArgumentException
+ {
+ String input = "";
+ while ((input = bfr.readLine()) != null)
+ {
+ if (!input.startsWith(Logo.messages.getString("pour")))
+ {
+ String var = input.substring(1); // - expected
+ String value = bfr.readLine();
+ getGlobals().define(var, value);
+ }
+ else
+ break;
+ }
+ return input;
+ }
+
+ /**
+ * Files created in network mode are virtual.
+ */
+ @Override
+ public void createFile(String fileName, String text) throws IllegalArgumentException
+ {
+ LogoFile vFile = LogoFile.createNewVirtualFile(fileName);
+ getFilesTable().put(fileName, vFile);
+
+ LogoFile file = LogoFile.createNewVirtualFile(fileName);
+ file.setText(text);
+
+ getFilesTable().put(fileName, file);
+
+ installListeners(file);
+ }
+
+ /**
+ * @return In network mode : false : {@link FileContainerChangeListener} events will not be fired.
+ */
+ public boolean fireFileEvents()
+ {
+ return false;
+ }
+
+ /**
+ * @return In network mode : false : {@link AmbiguityListener},
+ * {@link xlogo.interfaces.ExecutablesChangedListener},
+ * {@link xlogo.interfaces.ProceduresDefinedListener} events will not be fired.
+ */
+ public boolean fireProcedureEvents()
+ {
+ return false;
+ }
+
+ /**
+ * The Network context suggest that the user should not be allowed to create files from the gui.
+ * @see LogoContext#isFilesListEditAllowed()
+ */
+ @Override
+ public boolean isFilesListEditAllowed()
+ {
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/RecordContext.java b/logo/src/xlogo/kernel/userspace/context/RecordContext.java
new file mode 100644
index 0000000..48237de
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/RecordContext.java
@@ -0,0 +1,146 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.kernel.userspace.files.RecordFile;
+
+/**
+ * The context for contest/record mode.
+ * When creating a new RecordContext, a number of RecordFiles are created, for each name provided in the constructor
+ * <p>
+ * Note that a RecordContext only works correctly with RecordFiles.
+ * Therefore FileManagers should always use context.createFile(); to add new files, and never put them directly into the files table.
+ * <p>
+ * Besides, RecordContext suggests to not create new files ({@link #isFilesListEditAllowed()}),
+ * but files can still be created by using Logo commands, such as define or load.
+ *
+ * @author Marko
+ */
+public class RecordContext extends LogoContext implements MessageBroadcaster
+{
+ private String[] fileOrder;
+
+ private MessageListener fileTimerListener;
+ private ArrayList<MessageListener> timerEventListeners = new ArrayList<MessageListener>();
+
+ public RecordContext(final String[] recordModeFileNames) throws IOException
+ {
+ super();
+ this.fileOrder = recordModeFileNames;
+ initFileTimerListener();
+ setupRecordFiles();
+ }
+
+ protected void setupRecordFiles() throws IOException
+ {
+ for(String fileName : fileOrder)
+ createFile(fileName, "");
+ }
+
+ private void initFileTimerListener()
+ {
+ fileTimerListener = new MessageListener(){
+
+ @Override
+ public void messageEvent(String source, String message)
+ {
+ for (MessageListener listener : timerEventListeners)
+ listener.messageEvent(source, message);
+ }
+ };
+ }
+
+ @Override
+ public void createFile(String fileName, String text) throws IOException
+ {
+ RecordFile file = RecordFile.createNewFile(fileName);
+
+ if (text != null && text.length() > 0)
+ {
+ file.setText(text);
+ file.store();
+ }
+
+ installListeners(file);
+ file.addBroadcastListener(fileTimerListener);
+
+ getFilesTable().put(fileName, file);
+ }
+
+ public void openFile(String fileName)
+ {
+ super.openFile(fileName);
+ RecordFile file = (RecordFile) getFilesTable().get(fileName);
+ file.startRecord();
+ }
+
+ @Override
+ public void closeFile()
+ {
+ RecordFile file = (RecordFile) getOpenFile();
+ file.pauseRecord();
+ super.closeFile();
+ }
+
+ @Override
+ public String[] getFileOrder()
+ {
+ // TODO must extend file order for command "define" [otherwise constant number of files in record mode]
+ // - although it's not really correct, no visible consequences in app => no priority ... program works just as well
+ return fileOrder;
+ }
+
+ /**
+ * The Record context suggest that the user should not be allowed to create files from the gui.
+ * @see LogoContext#isFilesListEditAllowed()
+ */
+ @Override
+ public boolean isFilesListEditAllowed()
+ {
+ return false;
+ }
+
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.add(listener);
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.add(listener);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/UserContext.java b/logo/src/xlogo/kernel/userspace/context/UserContext.java
new file mode 100644
index 0000000..064e665
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/context/UserContext.java
@@ -0,0 +1,191 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+
+public class UserContext extends LogoContext
+{
+
+ /**
+ * Load and parse all the files in current user's source directory. <br>
+ * This happens only once, in the constructor. <br>
+ * Refresh is currently not planned, but it would be easy to add.
+ *
+ * @param userDir
+ */
+ public UserContext()
+ {
+ loadUserFiles();
+ }
+
+ private void loadUserFiles()
+ {
+ UserConfig userConfig = WSManager.getUserConfig();
+
+ if (userConfig.isVirtual())
+ return;
+
+ File sourceDir = userConfig.getSourceDirectory();
+ if (!sourceDir.exists())
+ sourceDir.mkdirs();
+
+ if (!sourceDir.isDirectory())
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.error.userdir.not.dir"));
+ return;
+ }
+
+ StringBuilder ioErrors = new StringBuilder();
+
+ for (String fileName : getLogoFileNamesFromDirectory(sourceDir))
+ {
+ String name = fileName.substring(0, fileName.length() - GlobalConfig.LOGO_FILE_EXTENSION.length());
+ userConfig.addFile(name);
+ try
+ {
+ LogoFile file = LogoFile.loadFile(name);
+ getFilesTable().put(file.getPlainName(), file);
+
+ }
+ catch (IOException e)
+ {
+ ioErrors.append(e.toString());
+ ioErrors.append("\n\n");
+ }
+ }
+
+ // must remove files from fileOrder that could not be found anymore.
+ for (String fileName : new ArrayList<String>(userConfig.getFileOrder()))
+ {
+ if (!getFilesTable().containsKey(fileName))
+ userConfig.getFileOrder().remove(fileName);
+ }
+
+ if (ioErrors.length() > 0)
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.error.could.not.load.logo.files") + "\n" + ioErrors.toString());
+ }
+ }
+
+ /**
+ * Caller must make sure that newName does not already exist.
+ */
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ super.renameFile(oldName, newName);
+ WSManager.getUserConfig().renameFile(oldName, newName);
+ }
+
+ @Override
+ public void createFile(String fileName, String text) throws IOException
+ {
+ /*
+ * Eager creation of files in file order list in user config.
+ */
+
+ if (!WSManager.getUserConfig().isVirtual())
+ super.createFile(fileName, text);
+ else
+ {
+ LogoFile file = LogoFile.createNewVirtualFile(fileName);
+ file.setText(text);
+ getFilesTable().put(fileName, file);
+ installListeners(file);
+ }
+ WSManager.getUserConfig().addFile(fileName);
+ }
+
+ @Override
+ public void importFile(File path, String newFileName)
+ {
+ try
+ {
+ super.importFile(path, newFileName);
+ }
+ catch (IOException e)
+ {
+ DialogMessenger.getInstance().dispatchError(MessageKeys.GENERAL_ERROR_TITLE, "Could not import file : \n" + e.toString());
+ }
+ WSManager.getUserConfig().addFile(newFileName);
+ }
+
+ private String[] getLogoFileNamesFromDirectory(File dir)
+ {
+ return dir.list(new FilenameFilter(){
+ public boolean accept(File file, String name)
+ {
+ return name.endsWith(".lgo");
+ }
+ });
+ }
+
+ @Override
+ public String[] getFileOrder()
+ {
+ /*
+ * Lazy deletion from file order list in user config.
+ */
+ ArrayList<String> list = new ArrayList<String>(WSManager.getUserConfig().getFileOrder());
+ Map<String,LogoFile> filesTable = getFilesTable();
+
+ if (filesTable.size() != list.size())
+ {
+ Iterator<String> iter = list.iterator();
+ String current;
+
+ while(iter.hasNext())
+ {
+ current = iter.next();
+ if(!filesTable.containsKey(current))
+ iter.remove();
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFile.java b/logo/src/xlogo/kernel/userspace/files/LogoFile.java
new file mode 100644
index 0000000..92fe23c
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/LogoFile.java
@@ -0,0 +1,755 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.interfaces.ErrorDetector;
+import xlogo.interfaces.ProcedureMapper;
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.procedures.ExecutablesContainer;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.kernel.userspace.procedures.Procedure.State;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.StorableDocument;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.NumberOfBackups;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+
+/**
+ * This class holds the text file a user entered in the editor.
+ * It analyzes the text and maintains a symbol table for all defined procedures that live within it.
+ * <p>
+ * The file does never store itself implicitly, except for when it is created using {@link #createNewFile(String)} or renamed using {@link #setFileName(String)}
+ * In every other case, {@link #store()}} or {@link #storeCopyToFile(File)}} must be invoked explicitly.
+ * <p>
+ * The file's text can be set using {@link #setTextFromReader(BufferedReader)}} (preferred) or {@link #setText(String)}}.
+ * Both will try to parse the signature of all procedures using the constructor of {@link xlogo.kernel.userspace.procedures.Procedure}
+ *
+ * @author Marko Zivkovic, (Lo�c Le Coq's parsing of procedures is not recognizable anymore.)
+ *
+ */
+public class LogoFile extends StorableDocument implements ExecutablesContainer, ProcedureMapper, ErrorDetector
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1117062836862782516L;
+
+ /**
+ * UserConfig of the owner of this file
+ */
+ private UserConfig userConfig;
+
+ /**
+ * Contains only executable procedures
+ */
+ private Map<String, Procedure> executables;
+
+ /**
+ * Contains all procedures, no matter what the state is.
+ * The order of the list is relevant to reproduce the editor text after the Logo command 'eraseprocedure'
+ * (after {@link #deleteProcedure(String)}})
+ */
+ private ArrayList<Procedure> allProcedures;
+
+ /**
+ * A flag that indicated whether the last parsing ended with errors or ambiguities
+ */
+ private boolean hasError;
+
+ /*
+ * CONSTRUCTOR & STATIC CONSTRUCTORS, FILE LOADERS
+ */
+
+ /**
+ * The LogoFile automatically sets its location to the current user's src directory, if that user is not virtual.
+ * @param fileName
+ * @throws IllegalArgumentException see : {@link Storable#setFileName()}
+ */
+ protected LogoFile(String fileName) throws IllegalArgumentException
+ {
+ super();
+ this.userConfig = WSManager.getUserConfig();
+ if (!userConfig.isVirtual())
+ setLocation(userConfig.getSourceDirectory());
+ setFileName(fileName);
+ executables = new HashMap<String, Procedure>();
+ allProcedures = new ArrayList<Procedure>();
+ }
+
+ public static LogoFile createNewVirtualFile(String fileName)
+ {
+ LogoFile file = null;
+ try
+ {
+ file = new LogoFile(fileName);
+ file.makeVirtual();
+ }
+ catch (IllegalArgumentException ignore) { }
+ return file;
+ }
+ /**
+ * Create a new file and store it in the user's source directory.
+ * @throws IOException
+ * @throws IllegalArgumentException
+ */
+ public static LogoFile createNewFile(String fileName) throws IOException, IllegalArgumentException
+ {
+ LogoFile file = new LogoFile(fileName);
+ file.setupFileSystem();
+ return file;
+ }
+
+ /**
+ * Load the specified file from the user's source directory and parse procedure structures.
+ * @param fileName - without extension
+ * @return
+ * @throws IOException
+ */
+ public static LogoFile loadFile(String fileName) throws IOException
+ {
+ UserConfig userConfig = WSManager.getUserConfig();
+ File path = userConfig.getLogoFilePath(fileName);
+ String text = Utils.readLogoFile(path.toString());
+ LogoFile file = new LogoFile(fileName);
+ file.setText(text);
+ if (userConfig.isVirtual())
+ file.makeVirtual();
+ return file;
+ }
+
+ /**
+ * Open any file on the file system and integrate it in the UserSpace.
+ * The file will be stored and made visible under the specified newFileName
+ * @param file
+ * @param newFileName
+ * @throws IOException
+ */
+ public static LogoFile importFile(File path, String newFileName) throws IOException
+ {
+ String text = Utils.readLogoFile(path.toString());
+ LogoFile file = new LogoFile(newFileName);
+ file.setText(text);
+ if (WSManager.getUserConfig().isVirtual())
+ file.makeVirtual();
+ file.store();
+ return file;
+ }
+
+ protected UserConfig getUserConfig()
+ {
+ return userConfig;
+ }
+ /**
+ * This assumes that the file name is well formed. No additional checks are performed
+ * Rename this LogoFile and the file on the file system, if it exists there. Notify all FileChangeListeners.
+ * This accepts name with or without .lgo extension.
+ * @param newFileName - without extension
+ */
+ @Override
+ public void setFileName(String newFileName)
+ {
+ if (newFileName == null || newFileName.length() == 0)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.EMPTY_NAME));
+ return;
+ }
+
+ if (!Storable.checkLegalName(newFileName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME) + " : " + newFileName);
+ return;
+ }
+
+ String oldPlainName = getPlainName();
+ super.setFileName(newFileName);
+ String newPlainName = getPlainName();
+
+ if (oldPlainName != null)
+ notifyRenamed(oldPlainName, newPlainName);
+ }
+
+ @Override
+ public String getFileNameExtension()
+ {
+ return GlobalConfig.LOGO_FILE_EXTENSION;
+ }
+
+ private void notifyRenamed(String oldName, String newName)
+ {
+ for(ProcedureMapListener listener : procedureMapListeners)
+ listener.ownerRenamed(oldName, newName);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * STORE & LOAD FILE
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * If this is not virtual, create this file on the file system and create a backup folder for it.
+ * @throws IOException
+ */
+ protected void setupFileSystem() throws IOException
+ {
+ if (isVirtual())
+ return;
+
+ File source = getFilePath();
+ File backupFolder = userConfig.getFileBackupDir(getPlainName());
+
+ if (!source.getParentFile().exists())
+ source.getParentFile().mkdirs();
+
+ if (!backupFolder.exists())
+ backupFolder.mkdirs();
+
+ storeCopyToFile(source);
+ }
+
+ /**
+ * If this is not virtual, store the file in the source folder of the UserSpace, <br>
+ * and another copy in the backup folder, if this is required by {@link WorkspaceConfig#getNumberOfBackups()}.
+ */
+ @Override
+ public void store() throws IOException
+ {
+ super.store();
+ if (isVirtual())
+ return;
+ doBackup();
+ }
+
+ @Override
+ public void delete()
+ {
+ super.delete();
+ Collection<String> procedures = new ArrayList<String>(executables.keySet());
+ executables.clear();
+ allProcedures.clear();
+ notifyDeleted(procedures);
+ }
+
+
+ /**
+ * Store a backup copy of this file.
+ * If the number of maximally allowed backups is exceeded,
+ * delete the oldest copies until the number of backups equals the limit
+ * defined by {@link WorkspaceConfig#getNumberOfBackups()}}
+ * @throws IOException
+ */
+ private void doBackup() throws IOException
+ {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ NumberOfBackups nob = wc.getNumberOfBackups();
+
+ File backupFile = userConfig.getBackupFilePath(getPlainName());
+ File backupFolder = backupFile.getParentFile();
+ if (!backupFolder.exists())
+ backupFolder.mkdirs();
+
+ if (nob != NumberOfBackups.NO_BACKUPS)
+ storeCopyToFile(backupFile);
+
+ if (nob == NumberOfBackups.INFINITE)
+ return;
+
+ int max = nob.getNumber(); // max is >= 0
+ // Assume no outer manipulation of that directory
+ File[] backups = backupFolder.listFiles();
+
+ int actual = backups.length;
+ if (actual <= max)
+ return;
+
+ // must delete the oldest backups
+ Arrays.sort(backups, new Comparator<File>(){
+ public int compare(File f1, File f2)
+ {
+ return f2.getName().compareTo(f1.getName().toString());
+ }
+ });
+
+ while (actual > max)
+ {
+ actual--;
+ backups[actual].delete();
+ }
+ }
+
+ /**
+ * The file path of this LogoFile in the source directory.
+ */
+ @Override
+ public File getFilePath()
+ {
+ if (super.getFilePath() != null)
+ return super.getFilePath();
+ return userConfig.getLogoFilePath(getPlainName());
+ }
+
+ @Override
+ public File getLocation()
+ {
+ if (super.getLocation() != null)
+ return super.getLocation();
+ return userConfig.getSourceDirectory();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * SERIALIZATION AND DESERIALIZATION
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ protected String generateText()
+ {
+ StringBuilder text = new StringBuilder();
+
+ for(Procedure proc : allProcedures)
+ {
+ text.append(proc.getText());
+ text.append("\n");
+ }
+
+ return text.toString();
+ }
+
+ /**
+ * Changes made 21.6.2013 and July 2013
+ * <p>
+ * In XLogo, this was {@code Editor.analyseprocedure()}
+ * <p><p>
+ * <b>Refactored:</b><br>
+ * Initially all that happens in
+ * <li> {@link #setText(String)}
+ * <li> {@link #parseText(BufferedReader)}
+ * <li> {@link #nextProcedure(BufferedReader)}
+ * <li> {@link Procedure#Procedure() }
+ * <li> {@link #untilEnd() }<br>
+ * was composed into one big and unreadable procedure {@code Editor.analyseprocedure()}.
+ * Note that the Editor (a GUI Controller) was responsible to parse text.
+ * {@code Editor.analyseprocedure()} took the text it was analyzing from the Editor's text component directly.
+ * Thus, whenever a procedure was programmatically defined, or if a workspace was received from the network,
+ * the text had to be written to the editor first before {@code Editor.analyseprocedure()} was called.<p>
+ * Note that in the networking case, the received text was never meant to be displayed.
+ * In that case the Editor served merely as a temporary container such that analyseProcedure() could read the text from it.
+ * This was the only reason why the property "affichable" (displayable) was added to so many classes.
+ *
+ * <p><p>
+ * <b>New Mechanism:</b><br>
+ * In XLogo, as soon as an error was found in the document, an exception was thrown and displayed to the user. <br>
+ * The new approach is to first split the document wherever a line starts with a token 'end'.
+ * <p>
+ * [#belongs to procedure 1<br>
+ * ... <br>
+ * end][#belongs to procedure 2 <br>
+ * ... <br>
+ * end] ...
+ * <p>
+ * These parts of the document are given to the constructor {@code Procedure#Procedure(String)},
+ * so the procedure can maintain its own state
+ * <br>
+ * Based on the type of errors, a Procedure can now detect several errors at a time and report them.
+ * The LogoFile can then report all errors that have been collected from its procedures.
+ * This approach allows to give more precise error messages to the user.
+ * Example: It is now possible to say which procedure is missing an 'end'
+ * <br>
+ * In the new implementation, a Procedure is not necessarily executable.
+ * Whether it is executable, can be read from its state {@link Procedure.State}.
+ * Its state can be
+ * <li> UNINITIALIZED
+ * <li> EXECUTABLE
+ * <li> COMMENT_ONLY (for white space and comments at the end of the document)
+ * <li> ERROR
+ * <li> AMBIGUOUS_NAME <br>
+ * <p>
+ * Only EXECUTABLE procedures are included in the procedureTable of the {@link xlogo.kernel.userspace.context.LogoContext},
+ * but all procedures are maintained by LogoFile.
+ * @param str
+ * @throws DocumentStructureException
+ * @throws IOException
+ */
+ @Override
+ protected void parseText(BufferedReader br)
+ {
+ /*
+ * Keep old procedures before reset of procedure tables.
+ * procedures that remain in the end, will count as deleted.
+ * procedures that existed before, but have errors now, count as deleted.
+ */
+ HashMap<String, Void> deleted = new HashMap<String, Void>();
+ for (Procedure proc : executables.values())
+ deleted.put(proc.getName(), null);
+
+ /*
+ * Must notify that all old executables are deleted as soon as a single procedure has an error,
+ * We want the whole file to be not executable when there exists an error
+ */
+ Collection<String> oldExecutables = new ArrayList<String>(executables.keySet());
+
+ /*
+ * We don't want the procedures to become ordered by creation time in the editor.
+ * The Logo command "define" was affected by this change, hence it was adapted to work as before.
+ * <p>
+ * Because we delete all the procedures from the tables [unlike XLogo] every time before reading the file,
+ * the procedures will be stored in the order in which the user defined them last.
+ */
+ resetProcedureTables(); // Added by Marko Zivkovic, 21.6.2013
+
+ // When the file is empty, it has no errors...
+ hasError = false;
+
+ try
+ {
+ while (br.ready()) // next procedure
+ {
+ Procedure proc;
+ String procedureText = untilEnd(br);
+ if (procedureText.equals(""))
+ break;
+ proc = new Procedure(procedureText);
+ proc.setOwnerName(getPlainName());
+
+ if (proc.getState() == State.EXECUTABLE)
+ {
+ deleted.remove(proc.getName());
+ }
+ addProcedure(proc);
+
+ if(proc.getState() == State.ERROR || proc.getState() == State.AMBIGUOUS_NAME)
+ hasError = true;
+ }
+ }
+ catch (IOException e){} // This should not happen, because no actual IO happens
+ finally { try { br.close(); } catch (IOException e) { } }
+
+ if(hasError)
+ {
+ notifyDeleted(oldExecutables);
+ return;
+ }
+
+ if (deleted.size() > 0)
+ notifyDeleted(deleted.keySet());
+
+ if (executables.size() > 0)
+ notifyDefined(executables.keySet());
+ }
+
+ /**
+ * @return String until the token 'end' is found on a line, or until the end of the BufferedReader
+ * @throws IOException
+ */
+ private static String untilEnd(BufferedReader br) throws IOException
+ {
+ String end = Logo.messages.getString("fin").toLowerCase();
+ StringBuffer text = new StringBuffer();
+ String line;
+
+ while (br.ready())
+ {
+ line = br.readLine();
+ if (line == null)
+ break;
+ else if (line.trim().toLowerCase().equals(end))
+ {
+ text.append(end);
+ break;
+ }
+ else
+ text.append(line + "\n");
+ }
+
+ return text.toString();
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Note: does not notify <br>
+ * Delete all procedures
+ */
+ private void resetProcedureTables()
+ {
+ allProcedures.clear();
+ executables.clear();
+ invalidateText();
+ setText(null);
+ }
+
+ /**
+ * Implementation of the Logo Command "define". <br>
+ * FileChangeListeners are notified.
+ * <p>
+ * In XLogo, the command define had no effect, when an error was detected while parsing.
+ * The same is true here, because an IllegalStateException is thrown if procedure is not executable.
+ * <p>
+ * In XLogo4Schools, to preserve semantics, we create a {@link Procedure} using its normal constructor and then check for errors.
+ * If errors exist in the procedure text, the procedure should not be defined in its destined file either.
+ * The responsibility whether a procedure is added to a file lies therefore in the interpreter.
+ * <p>
+ * Existing procedures with the same name are just redefined, as in XLogo.
+ * <p>
+ * @param procedure Expects an executable procedure.
+ * @throws IllegalStateException - if procedure is not Executable or its name is ambiguous in this file.
+ */
+ @Override
+ public void defineProcedure(Procedure procedure)
+ {
+ if (procedure.getState() != State.EXECUTABLE)
+ throw new IllegalStateException("Attempt to define procedure which is not executable.");
+
+ Procedure other = executables.get(procedure.name);
+
+ invalidateText();
+
+ if (other != null)
+ {
+ if (other.getState() == State.AMBIGUOUS_NAME)
+ throw new IllegalStateException("Attempt to redefine ambiguous procedure.");
+
+ other.redefine(procedure);
+
+ }else
+ {
+ allProcedures.add(procedure);
+ executables.put(procedure.name, procedure);
+ }
+ notifyDefined(procedure.getName());
+ }
+
+ /**
+ * This is for the Logo command 'eraseprocedure'
+ * @param name
+ * @throws IllegalArgumentException
+ */
+ @Override
+ public void eraseProcedure(String name)
+ {
+ Procedure proc = getExecutable(name);
+ if(proc == null)
+ throw new IllegalStateException("Attempt to erase procedure which exists not.");
+ allProcedures.remove(proc);
+ executables.remove(name);
+ invalidateText();
+ notifyDeleted(proc.getName());
+ }
+
+ /**
+ * Note: Does not notify listeners! <br>
+ * Semantics: If more than one procedures with the same name are defined in a document,
+ * all are marked ambiguous. The first one is kept in the executables list to track ambiguity.
+ * @param pr
+ */
+ protected void addProcedure(Procedure pr)
+ {
+ if (pr.getState() == State.EXECUTABLE)
+ {
+ Procedure other = executables.get(pr.name);
+
+ if(other != null)
+ {
+ other.makeAmbiguous();
+ pr.makeAmbiguous();
+ }
+ else
+ executables.put(pr.name, pr);
+ }
+ allProcedures.add(pr);
+ invalidateText();
+ }
+
+ @Override
+ public Procedure getExecutable(String name)
+ {
+ return executables.get(name);
+ }
+
+ /**
+ * @param name
+ * @return Whether an executable procedure with the specified name exists
+ */
+ @Override
+ public boolean isExecutable(String name)
+ {
+ return executables.get(name) != null;
+ }
+
+ @Override
+ public Collection<Procedure> getExecutables()
+ {
+ if (hasErrors())
+ return new ArrayList<Procedure>();
+ return executables.values();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * ERROR DETECTOR
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public boolean hasErrors()
+ {
+ return hasError;
+ }
+
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrors = new ArrayList<ProcedureErrorMessage>();
+ for(Procedure proc : allProcedures)
+ {
+ for (xlogo.kernel.userspace.procedures.ProcedureErrorType e : proc.getErrors())
+ {
+ String description = proc.getName();
+ if (description == null)
+ {
+ description = proc.getText().length() < 100 ?
+ proc.getText() :
+ proc.getText().substring(0, 100) + "...";
+ }
+
+ allErrors.add(new ProcedureErrorMessage(e, description, getPlainName()));
+ }
+ }
+ return allErrors;
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE MAPPER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Only executables.
+ * If the file has errors, no procedure is returned.
+ */
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ if (hasErrors())
+ return new ArrayList<String>();
+
+ ArrayList<String> procedureNames = new ArrayList<String>();
+
+ for (Procedure p : executables.values())
+ procedureNames.add(p.getName());
+
+ return procedureNames;
+ }
+
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ if (fileName.equals(getPlainName()))
+ return getAllProcedureNames();
+ return null;
+ }
+
+ /**
+ * Behaves similar like contains(). If the procedure is in the file's executable list, then returns this file's plainName. Otherwise null.
+ */
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ if (executables.containsKey(procedureName))
+ return getPlainName();
+ return null;
+ }
+
+ // Procedure Map Listeners
+
+ private final ArrayList<ProcedureMapListener> procedureMapListeners = new ArrayList<ProcedureMapListener>();
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.add(listener);
+
+ if(executables.size() > 0)
+ notifyDefined(executables.keySet()); // TODO hmmm
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.remove(listener);
+ }
+
+ protected void notifyDefined(Collection<String> procedures)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(getPlainName(), procedures);
+ }
+
+ protected void notifyDefined(String procedure)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(getPlainName(), procedure);
+ }
+
+ protected void notifyDeleted(Collection<String> collection)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(getPlainName(), collection);
+ }
+
+ protected void notifyDeleted(String procedure)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(getPlainName(), procedure);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java b/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java
new file mode 100644
index 0000000..9c077a9
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java
@@ -0,0 +1,39 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import xlogo.interfaces.BasicFileContainer;
+
+
+public interface LogoFileContainer extends BasicFileContainer
+{
+ /**
+ * Logo Command implementation
+ */
+ public void editAll();
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java b/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java
new file mode 100644
index 0000000..7a54902
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java
@@ -0,0 +1,560 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.context.ContextSwitcher;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.context.ContextSwitcher.ContextSwitchListener;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.global.GlobalConfig;
+
+/**
+ * This Manager is completely new, because XLogo did not support multiple files. <br>
+ * During the requirements analysis, we have decided to maintain a global scope for procedures.
+ * That means a procedure defined in file A is visible in file B.
+ * <p>
+ * If we find during testing that the global scope is confusing for children and it leads to many ambiguity conflicts,
+ * then the current architecture allows to easily switch to file-wide scope. Instead of retrieving executables from the context's procedure table,
+ * we can directly retrieve them from the currently open/active file.
+ *
+ * @author Marko Zivkovic
+ */
+public class LogoFilesManager implements LogoFileContainer, FileErrorCollector
+{
+ private final ContextSwitcher contextProvider;
+ private LogoContext context;
+
+ private final ArrayList<FileContainerChangeListener> fileListeners = new ArrayList<FileContainerChangeListener>();
+
+ public LogoFilesManager(ContextSwitcher contextProvider)
+ {
+ this.contextProvider = contextProvider;
+ initContextSwitchListener();
+ setContext(contextProvider.getContext());
+ }
+
+ private void initContextSwitchListener()
+ {
+ contextProvider.addContextSwitchListener(new ContextSwitchListener(){
+ @Override
+ public void contextSwitched(LogoContext newContext)
+ {
+ setContext(newContext);
+ }
+ });
+ }
+
+ private void setContext(LogoContext newContext)
+ {
+ LogoContext old = context;
+ context = newContext;
+
+ LogoFile openFile = newContext.getOpenFile();
+ if (openFile != null)
+ closeFile(openFile.getPlainName());
+
+ if (newContext.fireFileEvents()) // Example : Network context does not change GUI, only internal change => no events
+ {
+ if (old != null && old.fireFileEvents())
+ for(LogoFile file : old.getFilesTable().values())
+ notifyFileRemoved(file.getPlainName());
+
+ for (String fileName : newContext.getFileOrder())
+ {
+ notifyFileAdded(fileName);
+ if (context.getFilesTable().get(fileName).hasErrors())
+ notifyErrorsDetected(fileName);
+ }
+ }
+
+ if (old == null || old.isFilesListEditAllowed() != newContext.isFilesListEditAllowed())
+ notifyRightsChanged();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * X4S Specific features and Logo command implementations
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * The implementation of the Logo command {@code editall} or {@code edall} <br>
+ * In XLogo4Schools, we cannot open all files simultaneously to show all procedures. Instead, editall opens the file that was edited last.
+ */
+ public void editAll()
+ {
+ String fileName = getLastEditedFileName();
+ if (fileName == null)
+ return;
+ openFile(fileName);
+ }
+
+ @Override
+ public void importFile(File filePath) throws IOException
+ {
+ String name = filePath.getName().substring(0,
+ filePath.getName().length()
+ - GlobalConfig.LOGO_FILE_EXTENSION.length());
+
+ if(existsFile(name))
+ name = makeUniqueFileName(name);
+ context.importFile(filePath, name);
+ notifyFileAdded(name);
+ }
+
+ /**
+ * If file is a directory, the exported file will be named fileName.
+ * Otherwise the Logo-file will be exported to the file specified by dest
+ * @param fileName
+ * @param dest
+ * @throws IOException
+ */
+ public void exportFile(String fileName, File dest) throws IOException
+ {
+
+ if (dest.isDirectory())
+ exportFile(fileName, dest, fileName);
+ else
+ {
+ File parent = dest.getParentFile();
+ String targetName = dest.getName();
+ exportFile(fileName, parent, targetName);
+ }
+ }
+
+ /**
+ * @param fileName - of a file in the current context
+ * @param location - an existing directory on the file system
+ * @param targetName - the exported file's name
+ * @throws IOException
+ */
+ public void exportFile(String fileName, File location, String targetName) throws IOException
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+
+ if(file == null)
+ throw new IllegalArgumentException("The specified fileName does not exist in the context.");
+
+ if (!location.isDirectory())
+ throw new IllegalArgumentException("The specified location does not exist : " + location.toString());
+
+ String extendedName = targetName;
+
+ if(extendedName == null || extendedName.length() == 0)
+ extendedName = fileName;
+
+ String extension = GlobalConfig.LOGO_FILE_EXTENSION;
+
+ if(!extendedName.endsWith(extension))
+ extendedName += extension;
+
+ File target = new File(location.toString() + File.separator + extendedName);
+ file.storeCopyToFile(target);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * LOGO FILE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String[] getFileNames()
+ {
+ return context.getFileOrder();
+ }
+
+ @Override
+ public void createFile(String fileName) throws IOException
+ {
+ context.createFile(fileName, "");
+ notifyFileAdded(fileName);
+ }
+
+ @Override
+ public void writeFileText(String fileName, String content)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+
+ if (file == null)
+ throw new IllegalStateException("Attempt to write to inexistent file.");
+
+ boolean hadErrors = file.hasErrors();
+
+ file.setText(content);
+
+ if (file.hasErrors())
+ notifyErrorsDetected(fileName); // notify anyway
+ else if (hadErrors)
+ notifyErrorsCorrected(fileName);
+ }
+
+ @Override
+ public void storeFile(String fileName) throws IOException
+ {
+ context.getFilesTable().get(fileName).store();
+ }
+
+ /**
+ * The file is also deleted from the file system
+ */
+ @Override
+ public void removeFile(String fileName)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+ file.delete();
+ context.getFilesTable().remove(fileName);
+ notifyFileRemoved(fileName);
+ }
+
+ /**
+ * Deletes all files from the context and removes them from the context's tables. <br>
+ * Note: The events caused be deleting the files should cause all the procedures to disappear from the tables as well.
+ * [But the files manager doesn't care about procedures]
+ */
+ @Override
+ public void eraseAll()
+ {
+ Collection<LogoFile> files = context.getFilesTable().values();
+
+ while (!files.isEmpty())
+ {
+ LogoFile nextVictim = null;
+ for (LogoFile file : files)
+ {
+ nextVictim = file;
+ break;
+ }
+ nextVictim.delete();
+ context.getFilesTable().remove(nextVictim.getPlainName());
+ notifyFileRemoved(nextVictim.getPlainName());
+ }
+ context.getFilesTable().clear();
+ }
+
+ @Override
+ public boolean existsFile(String name)
+ {
+ return context.getFilesTable().containsKey(name);
+ }
+
+ @Override
+ public String readFile(String name)
+ {
+ return context.getFilesTable().get(name).getText();
+ }
+
+ /**
+ * Please make sure the renaming makes sense, otherwise an IllegalStateException is thrown at you.
+ */
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ if (oldName.equals(newName))
+ return;
+
+ if(!existsFile(oldName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.RENAME_INEXISTENT_FILE));
+ return;
+ }
+
+ if (existsFile(newName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.WS_FILENAME_EXISTS_ALREADY));
+ return;
+ }
+
+ if (newName == null || newName.length() == 0)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.EMPTY_NAME));
+ return;
+ }
+
+ if (!Storable.checkLegalName(newName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME) + " : " + newName);
+ return;
+ }
+
+ context.renameFile(oldName, newName);
+ notifyFileRenamed(oldName, newName);
+ }
+
+ @Override
+ public String makeUniqueFileName(String base)
+ {
+ int i = 0;
+ String name = null;
+ do
+ {
+ name = base + i;
+ i++;
+ } while (existsFile(name));
+ return name;
+ }
+
+ /**
+ * @throws IllegalArgumentException if the specified file does not exist in the current context.
+ */
+ @Override
+ public void openFile(String fileName)
+ {
+ if(!existsFile(fileName))
+ throw new IllegalStateException("The specified file to open does not exist in the current context.");
+
+ LogoFile openFile = context.getOpenFile();
+ if(openFile != null)
+ closeFile(openFile.getPlainName());
+
+ context.openFile(fileName);
+ notifyFileOpened(fileName);
+ }
+
+ /**
+ * This can handle only one open file.
+ * If the wrong filename is closed, nothing happens<p>
+ * @throws IllegalStateException
+ */
+ @Override
+ public void closeFile(String fileName)
+ {
+ LogoFile openFile = context.getOpenFile();
+ if (openFile == null || !openFile.getPlainName().equals(fileName))
+ throw new IllegalStateException("Attempting to close a file that was not opened.");
+ context.closeFile();
+ notifyFileClosed(openFile.getPlainName());
+ }
+
+ /**
+ * returns null if no file is open.
+ */
+ @Override
+ public String getOpenFileName()
+ {
+ LogoFile file = context.getOpenFile();
+ if (file == null)
+ return null;
+ return file.getPlainName();
+ }
+
+ public boolean isFilesListEditable()
+ {
+ return context.isFilesListEditAllowed();
+ }
+
+ /**
+ * the name of the file that was edited last in this context.
+ */
+ @Override
+ public String getLastEditedFileName()
+ {
+ Calendar latest = Calendar.getInstance();
+ latest.setTimeInMillis(0);
+
+ LogoFile result = null;
+ for (LogoFile file : context.getFilesTable().values())
+ {
+ Calendar fileDefinedAt = file.getLastSync();
+ if (latest.before(fileDefinedAt))
+ {
+ result = file;
+ latest = fileDefinedAt;
+ }
+ }
+ if (result == null)
+ return null;
+
+ return result.getPlainName();
+ }
+
+ // Change listeners : these event update the gui, they must run on the event dispatcher thread
+
+ @Override
+ public void addFileListener(FileContainerChangeListener listener)
+ {
+ if (listener == null)
+ throw new IllegalArgumentException("listener must not be null.");
+ fileListeners.add(listener);
+ }
+
+ @Override
+ public void removeFileListener(FileContainerChangeListener listener)
+ {
+ fileListeners.remove(listener);
+ }
+
+ private void notifyFileAdded(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileAdded(fileName);
+ }
+
+ private void notifyFileRemoved(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileRemoved(fileName);
+ }
+
+ private void notifyFileRenamed(final String oldName, final String newName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileRenamed(oldName, newName);
+ }
+
+ private void notifyFileOpened(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileOpened(fileName);
+ }
+
+ private void notifyFileClosed(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileClosed(fileName);
+ }
+
+ private void notifyRightsChanged()
+ {
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.editRightsChanged(context.isFilesListEditAllowed());
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * ERROR COLLECTOR : these events do not update the gui directly, they must not run on the event dispatcher thread
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ ArrayList<String> erroneousFiles = new ArrayList<String>();
+
+ for(LogoFile file : context.getFilesTable().values())
+ if(file.hasErrors())
+ erroneousFiles.add(file.getPlainName());
+
+ return erroneousFiles;
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ for(LogoFile file : context.getFilesTable().values())
+ if(file.hasErrors())
+ return true;
+ return false;
+ }
+
+ @Override
+ public boolean hasErrors(String fileName)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+ if (file == null)
+ throw new IllegalStateException("The specified fileName does not exist in this context.");
+
+ return file.hasErrors();
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrors = new ArrayList<ProcedureErrorMessage>();
+ for (LogoFile file : context.getFilesTable().values())
+ allErrors.addAll(file.getAllErrors());
+ return allErrors;
+ }
+
+ // Error listeners
+
+ private final ArrayList<ErrorListener> errorListeners = new ArrayList<ErrorListener>();
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+
+ private void notifyErrorsDetected(String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+ for (ErrorListener listener : errorListeners)
+ listener.errorsDetected(fileName);
+ }
+
+ private void notifyErrorsCorrected(String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+ for (ErrorListener listener : errorListeners)
+ listener.allErrorsCorrected(fileName);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/RecordFile.java b/logo/src/xlogo/kernel/userspace/files/RecordFile.java
new file mode 100644
index 0000000..9220f28
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/files/RecordFile.java
@@ -0,0 +1,234 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.swing.Timer;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+import xlogo.Logo;
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.UserConfig;
+import xlogo.utils.Utils;
+
+/**
+ * This is a {@link LogoFile} which is used in contest/record mode.
+ * @author Marko
+ */
+public class RecordFile extends LogoFile implements MessageBroadcaster
+{
+ private static final long serialVersionUID = -9137220313285199168L;
+
+ private Timer timer; // the SWING Timer dispatchers on the EventDispatcher Thread => update GUI ok
+ private Date started;
+ private Date last;
+ private long totalMillis;
+
+ /**
+ * @param fileName
+ */
+ protected RecordFile(String fileName)
+ {
+ super(fileName);
+ }
+
+ public static RecordFile createNewFile(String fileName) throws IOException
+ {
+ RecordFile file = new RecordFile(fileName);
+ file.setupFileSystem();
+ return file;
+ }
+
+
+ /**
+ * @throws NotImplementedException A virtual contest/record mode makes no sense.
+ */
+ public static RecordFile createNewVirtualFile(UserConfig userConfig, String fileName)
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ protected void setupFileSystem() throws IOException
+ {
+ File contestFileDir = getUserConfig().getContestFileDir(getPlainName());
+
+ if (!contestFileDir.exists())
+ contestFileDir.mkdirs();
+ }
+
+ @Override
+ public File getFilePath()
+ {
+ return getUserConfig().getContestFilePath(getPlainName());
+ }
+
+ @Override
+ public void store()
+ {
+ long now = Calendar.getInstance().getTime().getTime();
+ recordFile(getTimeStampHeader(totalMillis, started.getTime(), now));
+ //pauseRecord(); // This is already called by Context at open/close.
+ // We actually never store normally, and we don't export these files.
+ }
+
+ /**
+ * Set the timer
+ */
+ public void startRecord()
+ {
+ this.started = Calendar.getInstance().getTime();
+ this.last = Calendar.getInstance().getTime();
+
+ timer = new Timer(1000,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent arg0)
+ {
+ Date now = Calendar.getInstance().getTime();
+ totalMillis += now.getTime() - last.getTime();
+ last = now;
+
+ String time = UserConfig.getMinSec(totalMillis);
+ String fileName = getPlainName();
+
+ for(MessageListener listener : timerEventListeners)
+ listener.messageEvent(fileName, time);
+ }
+ }
+ );
+ timer.setRepeats(true);
+ timer.start();
+ }
+
+ /**
+ * Stop the timer and record recent changes with time stamp in contest directory.
+ * (Make sure the recent changes from the editor are before calling this)
+ */
+ public void pauseRecord()
+ {
+ timer.stop();
+ }
+
+
+ private void recordFile(final String header)
+ {
+ new Thread(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ // Write to file's folder
+ File recordFile = getUserConfig().getRecordFilePath(getPlainName());
+ File recordFolder = recordFile.getParentFile();
+ if (!recordFolder.exists())
+ recordFolder.mkdirs();
+
+ String content = header + getText();
+
+ try
+ {
+ Utils.writeLogoFile(recordFile.toString(), content);
+ }
+ catch (IOException e)
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.error.could.not.record.file") + "\n\n " + e.toString());
+ }
+
+ // append to command line too ...
+ PrintWriter out = null;
+ File logoFile = WSManager.getUserConfig().getCommandLineContestFile();
+ try
+ {
+ out = new PrintWriter(new BufferedWriter(new FileWriter(logoFile, true)));
+ out.println("");
+ out.println(getPlainName());
+ out.println(content);
+ out.println("\n");
+ }
+ catch (Exception e)
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.could.not.store") + "\n" + e.toString());
+ }
+ finally
+ {
+ if (out != null)
+ out.close();
+ }
+ }
+ }).run();
+
+ }
+
+
+ private String getTimeStampHeader(long totalTime, long lastEditStarted, long lastEditEnded)
+ {
+ String tot = UserConfig.getMinSec(totalTime);
+ String lastStart = UserConfig.getTimeString(lastEditStarted);
+ String now = UserConfig.getTimeString(lastEditEnded);
+
+ return "# Total Time : " + tot + "\n# Edited from : " + lastStart + "\n# Until : " + now + "\n\n";
+ }
+
+ /*
+ * Timer Listeners
+ */
+
+ private final ArrayList<MessageListener> timerEventListeners = new ArrayList<MessageListener>();
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ if(listener == null)
+ throw new IllegalArgumentException("Listener must not be null.");
+ timerEventListeners.add(listener);
+ listener.messageEvent(getPlainName(), UserConfig.getMinSec(totalMillis));
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.remove(listener);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java
new file mode 100644
index 0000000..73c7295
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java
@@ -0,0 +1,59 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import java.io.IOException;
+import java.util.Collection;
+
+public interface ExecutablesContainer
+{
+
+ public Collection<Procedure> getExecutables();
+
+ public Procedure getExecutable(String procedureName);
+
+ public boolean isExecutable(String name);
+
+ /**
+ * The Logo command {@code eraseProcedure} <p>
+ * Delete the specified procedure. <br>
+ * If there exists an ambiguity and multiple procedures with the same name
+ * are defined, remove all of them.
+ *
+ * @param name
+ */
+ public void eraseProcedure(String procedureName);
+
+ /**
+ * The Logo command {@code define}
+ * @param procedure
+ * @throws IOException
+ */
+ public void defineProcedure(Procedure procedure) throws IOException;
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java
new file mode 100644
index 0000000..6ae20ea
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java
@@ -0,0 +1,36 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import xlogo.interfaces.ProcedureMapper;
+import xlogo.interfaces.ErrorDetector.AmbiguityDetector;
+
+public interface ExecutablesProvider extends ExecutablesContainer, ProcedureMapper, AmbiguityDetector
+{
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/Procedure.java b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java
new file mode 100644
index 0000000..c485432
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java
@@ -0,0 +1,763 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written Lo�c Le Coq.
+ * The were heavily refactored, changed and extended by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ *
+ * @author Loïc Le Coq
+ */
+package xlogo.kernel.userspace.procedures;
+
+import java.util.Calendar;
+import java.util.Stack;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.io.*;
+
+import xlogo.Logo;
+import xlogo.kernel.Primitive;
+import xlogo.utils.Utils;
+
+/**
+ * The Procedure in XLogo4Schools receives pieces of text, where a single procedure is expected to be defined.
+ * It parses the text and sets its fields accordingly.
+ * It maintains states, so we know whether a procedure is executable or whether it has errors and what type of error.
+ * With the new implementation, multiple types of errors can be detected at the same time.
+ *
+ * <p>
+ * Affichable (displayable) is removed. In XLogo4Schools, every procedure is displayable.
+ * In XLogo, affichable was added to mark procedures that have been received over the network.
+ * They should only be running while the TCPConnection is open.
+ * They won't ever get displayed in the editor and they are removed, after the TCP connection is closed.
+ * In the new implementation, I can treat the received workspace as a virtual file,
+ * add its procedures to the UserSpace, without adding the file to the file list.
+ * Hence it cannot be opened while the procedures can still be executed.
+ *
+ * @author Marko Zivkovic, Loic
+ */
+public class Procedure
+{
+ public static final String specialCharacters = ":+-*/() []=<>&|";
+
+ /* Marko : TODO the following properties need to be encapsulated completely (Legacy from XLogo)
+ * I removed those properties that were named _sauve etc. which was used to temporarily redefine procedures,
+ * and then restore them in case there was an error while parsing.
+ */
+
+ // false lorsque c'est une procédure d'un fichier de démarrage
+ /**
+ * Whitespace and comments above a procedure in the editor
+ */
+ public String comment;
+ public int nbparametre;
+ public String name;
+ public ArrayList<String> variable = new ArrayList<String>();
+ public Stack<String> optVariables = new Stack<String>(); // Marko : why Stack??? [so bad]
+ public Stack<StringBuffer> optVariablesExp = new Stack<StringBuffer>(); // Marko : why Stack?? [so bad]
+ public String instruction = "";
+ public StringBuffer instr = null;
+
+ // Marko : I added these
+ private String text = null;
+ private String ownerName = null;
+ private Calendar defineTime = null;
+
+ /**
+ * Create a procedure from a piece of text from the editor.
+ * The expected structure is as follows.
+ * <p>
+ * [leading <b>empty lines</b> and <b>comments</b> => stored into comment] <br>
+ * <b>to procedureName</b> [<b>variables</b>] [<b>optional variables</b>] <br>
+ * [<b>body</b>] <br>
+ * <b>end</b><br>
+ *
+ * @param text
+ * @throws IOException
+ */
+ public Procedure(String text)
+ {
+ try
+ {
+ defineTime = Calendar.getInstance();
+ this.text = text;
+ StringReader sr = new StringReader(text);
+ BufferedReader br = new BufferedReader(sr);
+ String line = parseComments(br);
+ if (state == State.COMMENT_ONLY)
+ return;
+ StringTokenizer st = new StringTokenizer(line);
+ parseName(st);
+ if (errors.contains(ProcedureErrorType.MISSING_TO))
+ return;
+ parseVariables(st);
+ parseBody(br);
+
+ if (state == State.UNINITIALIZED)
+ setState(State.EXECUTABLE);
+ }
+ catch(IOException ignore) {
+ /* this should not happen, no actual IO */
+ }
+ }
+
+ /**
+ * Create a procedure with all necessary values.<br>
+ * If name is not legal, the Procedure state will switch to error,
+ * otherwise it will be executable
+ */
+ public void redefine(Procedure newDefinition)
+ {
+ defineTime = Calendar.getInstance();
+ this.name = newDefinition.name;
+ this.nbparametre = newDefinition.nbparametre;
+ this.variable = newDefinition.variable;
+ this.optVariables = newDefinition.optVariables;
+ this.optVariablesExp = newDefinition.optVariablesExp;
+ this.text = newDefinition.text;
+ this.errors = newDefinition.errors;
+
+ ProcedureErrorType e = checkName(name);
+ if (e != ProcedureErrorType.NO_ERROR)
+ addError(e);
+ else
+ setState(State.EXECUTABLE);
+ }
+
+ public Calendar getDefineTime()
+ {
+ return defineTime;
+ }
+
+ public String getText()
+ {
+ return text;
+ }
+
+ /*
+ * PROCEDURE STATE
+ */
+
+ private State state = State.UNINITIALIZED;
+
+ private ArrayList<ProcedureErrorType> errors = new ArrayList<ProcedureErrorType>();
+
+ /**
+ * @see {@link State}
+ * @return
+ */
+ public State getState()
+ {
+ return state;
+ }
+
+ private void setState(State state)
+ {
+ this.state = state;
+ }
+
+ /**
+ * Use this to indicate that this procedure is not executable because there is an ambiguity within its file.
+ */
+ public void makeAmbiguous()
+ {
+ this.state = State.AMBIGUOUS_NAME;
+ if (!errors.contains(ProcedureErrorType.AMBIGUOUS))
+ errors.add(ProcedureErrorType.AMBIGUOUS);
+ }
+
+ /**
+ * States are: UNINITIALIZED, EXECUTABLE, COMMENT_ONLY, ERROR
+ * @author Marko
+ */
+ public enum State
+ {
+ /**
+ * No values are set, or no check has been performed.
+ */
+ UNINITIALIZED("procedure.unititialized"),
+ /**
+ * The procedure has a correct structure and can be executed.
+ */
+ EXECUTABLE("procedure.executable"),
+ /**
+ * The procedure contains only a comment, no procedure definition. It is not executable
+ */
+ COMMENT_ONLY("procedure.not.executable"),
+ /**
+ * The procedure structure could no be parsed entirely because it contains errors.
+ */
+ ERROR("procedure.error"),
+ /**
+ * There is another procedures with the same name in the same file.
+ */
+ AMBIGUOUS_NAME("procedure.ambiguous");
+
+ private String description;
+
+ private State(String description)
+ {
+ this.description = description;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @return {@link #getDescription()}
+ */
+ public String toString()
+ {
+ return getDescription();
+ }
+ }
+
+ /*
+ * ERROR REPORTING
+ */
+
+ public ArrayList<ProcedureErrorType> getErrors()
+ {
+ return errors;
+ }
+
+ private void addError(ProcedureErrorType e)
+ {
+ errors.add(e);
+ state = State.ERROR;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * OLD XLOGO FEATURES ... TODO behavior not always clear
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ public void decoupe()
+ {
+ // Only cut procedures which are visible in the editor
+ if (null == instr)
+ {
+ instr = new StringBuffer();
+ try
+ {
+ String line = "";
+ StringReader sr = new StringReader(instruction);
+ BufferedReader bfr = new BufferedReader(sr);
+ int lineNumber = 0;
+ // Append the number of the line
+ instr.append("\\l");
+ instr.append(lineNumber);
+ instr.append(" ");
+ while (bfr.ready())
+ {
+ lineNumber++;
+ // read the line
+ try
+ {
+ line = bfr.readLine().trim();
+ }
+ catch (NullPointerException e1)
+ {
+ break;
+ }
+ // delete comments
+ line = deleteComments(line);
+ line = Utils.decoupe(line).toString().trim();
+ instr.append(line);
+ if (!line.equals(""))
+ {
+ instr.append(" ");
+ // Append the number of the line
+ instr.append("\\l");
+ instr.append(lineNumber);
+ instr.append(" ");
+ }
+ }
+ }
+ catch (IOException e)
+ {}
+ // System.out.println("****************************"+name+"\n"+instr+"\n\n");
+ }
+ }
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ private String deleteComments(String line)
+ {
+ int index = line.indexOf("#");
+ while (index != -1)
+ {
+ if (index == 0)
+ {
+ return "";
+ }
+ else if (line.charAt(index - 1) != '\\')
+ {
+ return line.substring(0, index);
+ }
+ else
+ index = line.indexOf("#", index + 1);
+ }
+ return line;
+ }
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ public String toString()
+ {
+ // return("nom "+name+" nombre paramètres "+nbparametre+" identifiant "+id+"\n"+variable.toString()+"\n"+instr+"\ninstrction_sauve"+instruction_sauve+"\ninstr_sauve\n"+instr_sauve);
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(comment);
+ sb.append(Logo.messages.getString("pour") + " " + name);
+ for (int j = 0; j < nbparametre; j++)
+ {
+ sb.append(" :");
+ sb.append(variable.get(j));
+ }
+ for (int j = 0; j < optVariables.size(); j++)
+ {
+ sb.append(" [ :");
+ sb.append(optVariables.get(j));
+ sb.append(" ");
+ sb.append(optVariablesExp.get(j));
+ sb.append(" ]");
+ }
+ sb.append("\n");
+ sb.append(instruction);
+ sb.append(Logo.messages.getString("fin"));
+ sb.append("\n");
+ // System.out.println("a"+sb+"a");
+ return new String(sb);
+ }
+
+ /**
+ * @author Lo�c Le Coq
+ */
+ public StringBuffer cutInList()
+ {
+ // Only cut procedures which are visible in the editor
+ StringBuffer sb = new StringBuffer();
+ try
+ {
+ String line = "";
+ StringReader sr = new StringReader(instruction);
+ BufferedReader bfr = new BufferedReader(sr);
+ while (bfr.ready())
+ {
+ try
+ {
+ line = bfr.readLine().trim();
+ }
+ catch (NullPointerException e1)
+ {
+ break;
+ }
+ // delete comments
+ // line=deleteComments(line);
+ line = Utils.decoupe(line).toString().trim();
+ sb.append("[ ");
+ sb.append(line);
+ sb.append(" ] ");
+ }
+ }
+ catch (IOException e)
+ {}
+ return sb;
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PARSING
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /**
+ * Read and save the comments that appear before the procedure
+ * @param br
+ * @return the first line that (probably) contains a procedure definition,
+ * or null if there are no more procedures
+ * @throws IOException
+ * @throws DocumentStructureException
+ */
+ String parseComments(BufferedReader br) throws IOException
+ {
+ String line = null;
+ comment = "";
+ while (br.ready())
+ {
+ line = br.readLine();
+ if (line == null)
+ break;
+
+ if (isComment(line))
+ {
+ comment += line + "\n";
+ line = null;
+ }
+ else if (line.trim().equals(""))
+ {
+ comment += "\n";
+ line = null;
+ }
+ else
+ break;
+ }
+
+ if (line == null)
+ {
+ setState(State.COMMENT_ONLY);
+ }
+
+ return line;
+ }
+
+ /**
+ * Expects a line that starts with "to procedName"
+ * @return Error, NO_ERROR if name is ok
+ * @throws DocumentStructureException
+ */
+ void parseName(StringTokenizer st)
+ {
+ String token = st.nextToken();
+
+ if (!token.toLowerCase().equals(Logo.messages.getString("pour").toLowerCase()))
+ {
+ addError(ProcedureErrorType.MISSING_TO);
+ return;
+ }
+
+ if (!st.hasMoreTokens())
+ {
+ addError(ProcedureErrorType.MISSING_NAME);
+ return;
+ }
+
+ name = st.nextToken().toLowerCase();
+
+ ProcedureErrorType e = checkName(name);
+ if (!e.equals(ProcedureErrorType.NO_ERROR))
+ addError(e);
+ }
+
+ void parseVariables(StringTokenizer st)
+ {
+ // Then, We read the variables
+ // :a :b :c :d .....
+ String token = null;
+ while (st.hasMoreTokens())
+ {
+ token = st.nextToken();
+ if (token.startsWith(":"))
+ {
+ String var = token.substring(1);
+ ProcedureErrorType e = checkValidVariable(var);
+ if (e != ProcedureErrorType.NO_ERROR)
+ {
+ addError(e);
+ return;
+ }
+ var = var.toLowerCase();
+ variable.add(var);
+ }
+ else
+ break;
+ }
+
+ nbparametre = variable.size();
+
+ if (token == null || token.startsWith(":"))
+ return;
+
+ // read all remaining tokens into string buffer
+ StringBuffer sb = new StringBuffer();
+ sb.append(token);
+ //sb.append(token);
+ while (st.hasMoreTokens())
+ {
+ sb.append(" ");
+ sb.append(st.nextToken());
+ }
+ // And finally, optional variables if there are some.
+ // [:a 100] [:b 20] [:c 234] ...........
+
+ while (sb.length() > 0)
+ {
+ if (sb.indexOf("[") != 0)
+ {
+ addError(ProcedureErrorType.VAR_EXTRA_CHARS);
+ return;
+ }
+
+ sb.deleteCharAt(0);
+ String[] arg = new String[2];
+ extractOptionalVariable(sb, arg);
+ optVariables.push(arg[0].toLowerCase());
+ /* Bug Fixed: list as Optionnal arguments
+ ** Eg:
+ ** to a [:var [a b c]]
+ * end
+ * when the string is formatted, we check that a white space
+ * is needed at the end of the argument
+ */
+
+ StringBuffer exp = Utils.decoupe(arg[1]);
+ if (exp.charAt(exp.length() - 1) != ' ')
+ exp.append(" ");
+ optVariablesExp.push(exp);
+ }
+ }
+
+ /**
+ * Reads from sb into args the name and the default value of an optional variable [:a value]. <br>
+ * value can be expression: number, word, list, ...
+ * @author Lo�c Le Coq
+ * @author Marko Zivkovic refactored
+ */
+ void extractOptionalVariable(StringBuffer sb, String[] args)
+ {
+
+ String variable="";
+ String expression="";
+ int compteur=1;
+ int id=0;
+ int id2=0;
+ boolean espace = false;
+ for (int i = 0; i < sb.length(); i++)
+ {
+ char ch = sb.charAt(i);
+ if (ch == '[')
+ compteur++;
+ else if (ch == ']')
+ {
+ if (id == 0)
+ {
+ addError(ProcedureErrorType.OPT_VAR_BRACKET);
+ return;
+ }
+ compteur--;
+ }
+ else if (ch == ' ')
+ {
+ if (!variable.equals(""))
+ {
+ if (!espace)
+ id = i;
+ espace = true;
+ }
+ }
+ else
+ {
+ if (!espace)
+ variable += ch;
+ }
+ if (compteur == 0)
+ {
+ id2 = i;
+ break;
+ }
+ }
+ if (!variable.startsWith(":"))
+ {
+ addError(ProcedureErrorType.VAR_COLON_EXPECTED);
+ return;
+ }
+
+ variable = variable.substring(1, variable.length()).toLowerCase();
+ ProcedureErrorType pet = checkValidVariable(variable);
+
+ if (pet != ProcedureErrorType.NO_ERROR)
+ {
+ addError(pet);
+ return;
+ }
+
+
+ if (compteur != 0)
+ {
+ addError(ProcedureErrorType.OPT_VAR_BRACKET);
+ return;
+ }
+
+ expression = sb.substring(id + 1, id2).trim();
+
+ if (expression.equals(""))
+ {
+ addError(ProcedureErrorType.VAR_MISSING_EXPRESSION);
+ return;
+ }
+ /* System.out.println(id+" "+id2);
+ System.out.println("a"+expression+"a");
+ System.out.println("a"+variable+"a");*/
+ sb.delete(0, id2 + 1);
+ // delete unnecessary space
+ while (sb.length() != 0 && sb.charAt(0) == ' ')
+ sb.deleteCharAt(0);
+ args[0] = variable;
+ args[1] = expression;
+ }
+
+ /**
+ * Sets as body everything up the 'end' key word.
+ */
+ private void parseBody(BufferedReader br) throws IOException
+ {
+ StringBuffer body = new StringBuffer();
+ String to = Logo.messages.getString("pour").toLowerCase() + " ";
+ String end = Logo.messages.getString("fin").toLowerCase();
+ instruction = null;
+ String line;
+ String lower;
+
+ while (br.ready())
+ {
+ line = br.readLine();
+ if (null == line)
+ break;
+ lower = line.toLowerCase();
+ if (lower.trim().equals(end)) // end of procedure
+ {
+ instruction = body.toString();
+ if (br.ready() && br.readLine() != null) // Additional characters after end
+ addError(ProcedureErrorType.CHARS_AFTER_END);
+ return; // We are done
+ }
+ if (lower.startsWith(to)) // new procedure definition before end
+ {
+ addError(ProcedureErrorType.TO_BEFORE_END);
+ return;
+ }
+ body.append(line);
+ body.append("\n");
+ }
+
+ // no end was found
+ addError(ProcedureErrorType.MISSING_END);
+
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * UTILITY
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /**
+ * @return true if and only if line starts with '#'
+ */
+ static boolean isComment(String line)
+ {
+ if (line.trim().startsWith("#"))
+ return true;
+ else
+ return false;
+ }
+
+ static boolean isNumber(String token)
+ {
+ try
+ {
+ Double.parseDouble(token);
+ return true;
+ }
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * This method is essentially changed to fit with the new workspace (=UserSpace & LogoFile).<br>
+ * On the fly it was rewritten completely, because of its initial programming style and inefficiency. <br>
+ * (note that it was very long, and now it is only 3 lines)
+ *
+ * @param token
+ * @return Error - NO_ERROR if token is a legal procedure name
+ * @author Marko Zivkovic
+ */
+ static ProcedureErrorType checkName(String token)
+ {
+ if (isNumber(token))
+ return ProcedureErrorType.NAME_IS_NUMBER;
+ ProcedureErrorType e = checkSpecialCharacter(token);
+ if (e != ProcedureErrorType.NO_ERROR)
+ return e;
+
+ if(Primitive.primitives.containsKey(token))
+ return ProcedureErrorType.NAME_IS_KEY_WORD;
+
+ return ProcedureErrorType.NO_ERROR;
+ }
+
+ /**
+ * @param token
+ * @return null if a valid variable name, error message otherwise
+ */
+ static ProcedureErrorType checkValidVariable(String token)
+ {
+ if (token.length() == 0)
+ return ProcedureErrorType.VAR_WHITESPACE_AFTER_COLON;
+
+ if (isNumber(token))
+ return ProcedureErrorType.VAR_IS_NUMBER;
+
+ return checkSpecialCharacter(token);
+ }
+
+ static ProcedureErrorType checkSpecialCharacter(String var)
+ {
+ StringTokenizer check = new StringTokenizer(var, specialCharacters, true);
+
+ if ((check.countTokens() > 1) || ":+-*/() []=<>&|".indexOf(check.nextToken()) > -1)
+ return ProcedureErrorType.VAR_HAS_SPECIAL;
+ return ProcedureErrorType.NO_ERROR;
+ }
+
+ /*
+ * OWNER : This is typically a file, but a procedure must not know whether it is owned by a file or something else
+ */
+
+ public String getOwnerName()
+ {
+ return ownerName;
+ }
+
+ public void setOwnerName(String ownerName)
+ {
+ this.ownerName = ownerName;
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java b/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java
new file mode 100644
index 0000000..3029320
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java
@@ -0,0 +1,80 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import xlogo.messages.MessageKeys;
+
+public enum ProcedureErrorType
+{
+ NO_ERROR(MessageKeys.LOGO_ERROR_NO_ERROR),
+ MISSING_TO(MessageKeys.LOGO_ERROR_MISSING_TO),
+ MISSING_NAME(MessageKeys.LOGO_ERROR_MISSING_NAME),
+ NAME_IS_NUMBER(MessageKeys.LOGO_ERROR_NAME_IS_NUMBER), //Logo.messages.getString("erreur_nom_nombre_procedure")
+ /*
+ * Logo.messages.getString("caractere_special1") + "\n" + Logo.messages.getString("caractere_special2")
+ * + "\n" + Logo.messages.getString("caractere_special3") + " " + token
+ */
+ NAME_HAS_SPECIAL_CHAR(MessageKeys.LOGO_ERROR_NAME_SPECIAL),
+ VAR_WHITESPACE_AFTER_COLON(MessageKeys.LOGO_ERROR_VAR_WHITE_AFTER_COLON),
+ /*
+ * Logo.messages.getString("caractere_special_variable") + "\n"
+ + Logo.messages.getString("caractere_special2") + "\n"
+ + Logo.messages.getString("caractere_special3") + " :" + var
+ */
+ VAR_HAS_SPECIAL(MessageKeys.LOGO_ERROR_VAR_SPECIAL),
+ VAR_IS_NUMBER(MessageKeys.LOGO_ERROR_VAR_IS_NUMBER),
+ VAR_COLON_EXPECTED(MessageKeys.LOGO_ERROR_VAR_MISSING_COLON),
+ OPT_VAR_BRACKET(MessageKeys.LOGO_ERROR_OPT_VAR_BRACKET),
+ VAR_EXTRA_CHARS(MessageKeys.LOGO_ERROR_VAR_EXTRA_CHARS),
+ VAR_MISSING_EXPRESSION(MessageKeys.LOGO_ERROR_MISSING_EXPR),
+ CHARS_AFTER_END(MessageKeys.LOGO_ERROR_MORE_CHARS_AFTER_END),
+ MISSING_END(MessageKeys.LOGO_ERROR_MISSING_END),
+ TO_BEFORE_END(MessageKeys.LOGO_ERROR_TO_BEFORE_END),
+ NAME_IS_KEY_WORD(MessageKeys.LOGO_ERROR_NAME_IS_KEY_WORD),
+ AMBIGUOUS(MessageKeys.LOGO_ERROR_AMBIGUOUS);
+
+ private String description;
+
+ private ProcedureErrorType(String description)
+ {
+ this.description = description;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @return {@link #getDescription()}
+ */
+ public String toString()
+ {
+ return getDescription();
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java b/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java
new file mode 100644
index 0000000..bcaea16
--- /dev/null
+++ b/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java
@@ -0,0 +1,557 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.context.ContextSwitcher;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.context.ContextSwitcher.ContextSwitchListener;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure.State;
+
+/**
+ * This class maintains the procedure table of all files in a context, and it reports ambiguity among them.
+ * @author Marko Zivkovic
+ */
+public class ProceduresManager implements ExecutablesProvider
+{
+ private LogoContext context;
+
+ private final ArrayList<AmbiguityListener> ambiguityListeners = new ArrayList<AmbiguityListener>();
+
+ private ProcedureMapListener procedureMapListener;
+
+ public ProceduresManager(ContextSwitcher contextProvider)
+ {
+ initProcedureMapListener();
+ installContextSwitchListener(contextProvider);
+ setContext(contextProvider.getContext());
+ }
+
+ private void setContext(LogoContext newContext)
+ {
+ LogoContext old = this.context;
+ this.context = newContext;
+
+ if (old != null)
+ old.removeProcedureMapListener(procedureMapListener);
+
+ mapProcedures(newContext);
+
+ /* Record Mode => new files => events
+ // Network Mode => only virtual files, invisible => no events
+ // that was achieved with the boolean "affichable" (displayable) in XLogo
+ if (!newContext.fireProcedureEvents())
+ {
+
+ return;
+ }
+ */
+ if (old != null && old.fireProcedureEvents() && newContext.fireProcedureEvents()) // TODO changed
+ for(LogoFile file : old.getFilesTable().values())
+ notifyDeleted(file.getPlainName(), file.getAllProcedureNames());
+
+ // Note : context will immediately fire event to notify what his contents are.
+ // Then these events are forwarded here, in procedureMapListener.
+ newContext.addProcedureMapListener(procedureMapListener);
+ }
+
+ private void mapProcedures(LogoContext context)
+ {
+ context.getProcedureTable().clear();
+ for (LogoFile file : context.getFilesTable().values())
+ for (Procedure procedure : file.getExecutables())
+ addProcedure(procedure);
+ }
+
+ private void installContextSwitchListener(ContextSwitcher provider)
+ {
+ provider.addContextSwitchListener(new ContextSwitchListener(){
+
+ @Override
+ public void contextSwitched(LogoContext newContext)
+ {
+ setContext(newContext);
+ }
+ });
+ }
+
+ private void initProcedureMapListener()
+ {
+ // This one listens for changes in files.
+ procedureMapListener = new ProcedureMapListener()
+ {
+ /**
+ * This event is received when a document is assigned a new text and it contains no errors,
+ * or when this ProcedureManager is assigned a new context that already contains files.
+ */
+ @Override
+ public void defined(String fileName, Collection<String> procedures)
+ {
+ for (String procedureName : procedures)
+ addProcedure(fileName, procedureName);
+
+ if (procedures.size() > 0)
+ notifyDefined(fileName, procedures);
+
+ for (String procedureName : procedures)
+ {
+ Map<String, Procedure> fileNameToProcedure = context.getProcedureTable().get(procedureName);
+ if (fileNameToProcedure.size() > 1)
+ {
+ notifyAmbiguityDetected(procedureName, context.getProcedureTable().get(procedureName));
+ }
+ }
+
+ }
+
+ /**
+ * This event is received when the Logo command "define" (re-)defined a command in a file (which already exists!)
+ */
+ @Override
+ public void defined(String fileName, String procedureName)
+ {
+ addProcedure(fileName, procedureName);
+
+ notifyDefined(fileName, procedureName);
+
+ // Check ambiguity
+ Map<String, Procedure> fileNameToProcedure = context.getProcedureTable().get(procedureName);
+ if (fileNameToProcedure.size() > 1)
+ {
+ notifyAmbiguityDetected(procedureName, fileNameToProcedure);
+ return;
+ }
+ }
+
+ /**
+ * This event is received when a file is deleted or when it is assigned a new text in which old procedures are missing.
+ */
+ @Override
+ public void undefined(String fileName, Collection<String> procedures)
+ {
+ for (String procedure : procedures)
+ undefined(fileName, procedure);
+ }
+
+ /**
+ * This event is received when the Logo command "eraseProcedure" removes a single procedure from a document
+ * Depending on how many procedures with the same name are left across files after this procedure is removed,
+ * this will fire ExecutableChanged events or AmbiguityResolved events.
+ */
+ @Override
+ public void undefined(String fileName, String procedureName)
+ {
+ Map<String,HashMap<String, Procedure>> procedureTable = context.getProcedureTable();
+
+ Map<String, Procedure> fileToProc = procedureTable.get(procedureName);
+
+ if (fileToProc == null)
+ return;
+
+ // remove from fileToProc entry
+ fileToProc.remove(fileName);
+
+ notifyDeleted(fileName, procedureName);
+
+ if (fileToProc.size() == 0)
+ procedureTable.remove(procedureName);
+ else
+ notifyAmbiguityResolved(procedureName, fileName);
+
+ if (fileToProc.size() == 1)
+ {
+ String winnerFile = null;
+ for (String wf : fileToProc.keySet())
+ winnerFile = wf; // size == 1 => only 1 iteration
+
+ notifyAmbiguityResolved(procedureName, winnerFile);
+ }
+ }
+
+ @Override
+ public void ownerRenamed(String oldName, String newName)
+ {
+ // Note : very important that this event is catched after filemanager has renamed files
+ // very critical. but implementation guarantees this order.
+ LogoFile file = context.getFilesTable().get(newName);
+ // Rename keys in procedureTable
+ Map<String, Procedure> fileToProc;
+ for (Procedure proc : file.getExecutables())
+ {
+ fileToProc = context.getProcedureTable().get(proc.getName());
+ fileToProc.remove(oldName);
+ fileToProc.put(newName, proc);
+ }
+ }
+ };
+ }
+
+ /**
+ * @see #addProcedure(Procedure)
+ */
+ protected void addProcedure(String fileName, String procedureName) throws IllegalArgumentException
+ {
+ Procedure procedure = context.getFilesTable().get(fileName).getExecutable(procedureName.toLowerCase());
+ addProcedure(procedure);
+ }
+
+ /**
+ * Adds the procedure to the procedureTable without firing events.
+ * Use {@link #defineProcedure(Procedure)}} if you want to notify listeners
+ * @param procedure
+ * @throws IllegalArgumentException
+ */
+ protected void addProcedure(Procedure procedure) throws IllegalArgumentException
+ {
+ String procedureName = procedure.getName();
+
+ if (procedure.getState() != State.EXECUTABLE && procedure.getState() != State.AMBIGUOUS_NAME)
+ throw new IllegalArgumentException("Only executable procedures should be added to the context.");
+
+ HashMap<String, HashMap<String, Procedure>> procedureTable = context.getProcedureTable();
+ HashMap<String, Procedure> fileToProc = procedureTable.get(procedureName);
+
+ if (fileToProc == null) // The procedure name is not yet defined in the context.
+ {
+ // Must create fileToProc entry in procedureTable
+ fileToProc = new HashMap<String, Procedure>();
+ procedureTable.put(procedureName, fileToProc);
+ }
+
+ fileToProc.put(procedure.getOwnerName(), procedure);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * EXECUTABLES CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * @param {@link Procedure#getOwnerName()} is supposed to be already mapped in the filesTable, and the procedure should be executable
+ * @throws IllegalArgumentException if the procedure's owner name is not mapped;
+ * @see {@link LogoFile#defineProcedure(Procedure)}
+ */
+ @Override
+ public void defineProcedure(Procedure procedure)
+ {
+ LogoFile file = context.getFilesTable().get(procedure.getOwnerName());
+ if (file == null)
+ throw new IllegalArgumentException("The file name \""
+ + procedure.getOwnerName()
+ + "\" specified in procedure is not mapped to a file.");
+
+ //addProcedure(procedure);
+ file.defineProcedure(procedure); // this will cause the necessary event cascade
+ }
+
+ /**
+ * Erase
+ */
+ @Override
+ public void eraseProcedure(String procedureName)
+ {
+ String lower = procedureName.toLowerCase();
+ while(context.getProcedureTable().containsKey(lower))
+ {
+ // contains key => there must be one
+ Procedure nextVictim = null;
+ for (Procedure proc : context.getProcedureTable().get(lower).values())
+ {
+ // iterate only once : otherwise concurrent modification exception
+ nextVictim = proc;
+ break;
+ }
+ context.getFilesTable().get(nextVictim.getOwnerName()).eraseProcedure(nextVictim.getName());
+ }
+ }
+
+ /**
+ * Executable procedures of all files. Ambiguous procedures are not included. <br>
+ * This list should not be used to do computations on it, because it must be created every time.
+ * Its purpose is to feed GUI lists
+ */
+ @Override
+ public ArrayList<Procedure> getExecutables()
+ {
+ ArrayList<Procedure> executables = new ArrayList<Procedure>();
+ for(Map<String, Procedure> fileToProc : context.getProcedureTable().values())
+ if (fileToProc.size() == 1) // only one exists
+ for(Procedure proc : fileToProc.values())
+ executables.add(proc); // only one is added
+ return executables;
+ }
+
+ /**
+ * @return The specified procedure if and only if there is exactly one
+ * procedure definition for this name in the context, otherwise null.
+ */
+ @Override
+ public Procedure getExecutable(String procedureName)
+ {
+ Map<String, Procedure> fileToProc = context.getProcedureTable().get(procedureName.toLowerCase());
+
+ if (fileToProc == null || fileToProc.size() > 1)
+ return null;
+ // There is exactly 1 procedure
+ for (Procedure proc : fileToProc.values())
+ return proc;
+ // Code won't reach here
+ return null;
+ }
+
+ /**
+ * @return true if and only if there exists exactly one procedure definition for the specified name in this context
+ */
+ @Override
+ public boolean isExecutable(String procedureName)
+ {
+ Map<String, Procedure> fileToProc = context.getProcedureTable().get(procedureName.toLowerCase());
+ return (fileToProc != null && fileToProc.size() == 1);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE MAPPER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Get all executable procedure names
+ * @see #getExecutables()
+ */
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ ArrayList<String> executables = new ArrayList<String>();
+ for(Map<String, Procedure> fileToProc : context.getProcedureTable().values())
+ if (fileToProc.size() == 1) // only one exists
+ for(Procedure proc : fileToProc.values())
+ executables.add(proc.getName()); // only one is added
+ return executables;
+ }
+
+ /**
+ * Get all procedures of that file, if its procedures are not conflicting with other files' procedures.
+ */
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ if (!hasAmbiguousProcedures(fileName))
+ return context.getFilesTable().get(fileName).getAllProcedureNames();
+ return null;
+ }
+
+ /**
+ * @return null if procedure does not exist or if it is ambiguous
+ */
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ Procedure procedure = getExecutable(procedureName.toLowerCase());
+ return procedure == null ? null : procedure.getOwnerName();
+ }
+
+ // Procedure Map Listeners : update gui => run on event dispatcher thread
+
+ private final ArrayList<ProcedureMapListener> procedureMapListeners = new ArrayList<ProcedureMapListener>();
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.add(listener);
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.remove(listener);
+ }
+
+ protected void notifyDefined(final String fileName, final Collection<String> procedures)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ if (procedures.size() == 0)
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(fileName, procedures);
+ }
+
+ protected void notifyDefined(final String fileName, final String procedure)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(fileName, procedure);
+
+ }
+
+ protected void notifyDeleted(final String fileName, final Collection<String> procedures)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ if (procedures.size() == 0)
+ return;
+
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(fileName, procedures);
+ }
+
+ protected void notifyDeleted(final String fileName, final String procedure)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(fileName, procedure);
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * AMBIGUITY DETECTOR
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * @return Whether there exists a procedure name that is defined in more
+ * than one file in the UserSpace
+ */
+ @Override
+ public boolean hasErrors()
+ {
+ for (Entry<String, ? extends Map<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ if (entry.getValue().size() > 1)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns all ambiguity problems
+ */
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> ambiguities = new ArrayList<ProcedureErrorMessage>();
+
+ for (Entry<String, HashMap<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ HashMap<String, Procedure> fileToProc = entry.getValue();
+
+ if (fileToProc.size() < 2)
+ continue;
+
+ ambiguities.add(
+ new ProcedureErrorMessage(
+ ProcedureErrorType.AMBIGUOUS,
+ entry.getKey(),
+ fileToProc.keySet()
+ )
+ );
+ }
+
+ return ambiguities;
+ }
+
+ @Override
+ public Collection<String> getAllConflictingFiles()
+ {
+ ArrayList<String> conflictingFiles = new ArrayList<String>();
+
+ for (Entry<String, HashMap<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ HashMap<String, Procedure> fileToProc = entry.getValue();
+
+ if (fileToProc.size() < 2)
+ continue;
+
+ conflictingFiles.addAll(fileToProc.keySet());
+ }
+
+ return conflictingFiles;
+ }
+
+ @Override
+ public boolean hasAmbiguousProcedures(String fileName)
+ {
+ for (Procedure proc : context.getFilesTable().get(fileName).getExecutables())
+ {
+ if(isProcedureAmbiguous(proc.getName()))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isProcedureAmbiguous(String name)
+ {
+ Map<String, Procedure> ambigs = context.getProcedureTable().get(name.toLowerCase());
+ if (ambigs == null)
+ return false;
+ return ambigs.size() > 1;
+ }
+
+
+ @Override
+ public void addAmbiguityListener(AmbiguityListener listener)
+ {
+ ambiguityListeners.add(listener);
+ }
+
+ @Override
+ public void removeAmbiguityListener(AmbiguityListener listener)
+ {
+ ambiguityListeners.remove(listener);
+ }
+
+ private void notifyAmbiguityDetected(String procedureName, Map<String, Procedure> fileToProcedure)
+ {
+ if(!context.fireProcedureEvents())
+ return;
+ for (AmbiguityListener listener : ambiguityListeners)
+ listener.ambiguityDetected(procedureName, fileToProcedure);
+ }
+
+ private void notifyAmbiguityResolved(String procedureName, String winnerFile)
+ {
+ if(!context.fireProcedureEvents())
+ return;
+ for (AmbiguityListener listener : ambiguityListeners)
+ listener.ambiguityResolved(procedureName, winnerFile);
+ }
+
+
+
+
+}
diff --git a/logo/src/xlogo/messages/Message.java b/logo/src/xlogo/messages/Message.java
new file mode 100644
index 0000000..50a6749
--- /dev/null
+++ b/logo/src/xlogo/messages/Message.java
@@ -0,0 +1,52 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages;
+
+public abstract class Message
+{
+ private String title;
+ private String message;
+
+ public Message(String title, String message)
+ {
+ this.title = title;
+ this.message = message;
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+ public String getTitle()
+ {
+ return title;
+ }
+
+ public abstract void displayMessage();
+} \ No newline at end of file
diff --git a/logo/src/xlogo/messages/MessageKeys.java b/logo/src/xlogo/messages/MessageKeys.java
new file mode 100644
index 0000000..8da9013
--- /dev/null
+++ b/logo/src/xlogo/messages/MessageKeys.java
@@ -0,0 +1,180 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages;
+
+/**
+ * XLogo4Schools keys for the language property files.
+ * Old XLogo keys are not included.
+ * @author Marko
+ */
+public class MessageKeys
+{
+ public static final String WS_SETTINGS_REQUIRE_PW = "ws.settings.require_password";
+ public static final String WS_SETTINGS_PASSWORD = "ws.settings.password";
+ public static final String WS_SETTINGS_RETYPE_PW = "ws.settings.retype.password";
+ public static final String WS_SETTINGS_SAVE_PW = "ws.settings.save.password";
+ public static final String WS_SETTINGS_WORKSPACE_LABEL = "ws.settings.workspace.label";
+ public static final String WS_SETTINGS_LOCATION = "ws.settings.location";
+ public static final String WS_SETTINGS_LANGUAGE = "ws.settings.language";
+ public static final String WS_SETTINGS_BACKUPS = "ws.settings.backups";
+ public static final String WS_SETTINGS_USER = "ws.settings.user";
+ public static final String WS_SETTINGS_ADD = "ws.settings.add";
+ public static final String WS_SETTINGS_REMOVE = "ws.settings.remove";
+ public static final String WS_SETTINGS_IMPORT = "ws.settings.import";
+ public static final String WS_SETTINGS_ENABLE_USER_ACCOUNT_CREATION = "ws.settings.enable.user.account.creation";
+ public static final String WS_SETTINGS_SAVE_AND_CLOSE = "ws.settings.save.and.close";
+ public static final String WS_SETTINGS_TITLE = "ws.settings.title";
+ public static final String WS_SETTINGS_GLOBAL = "ws.settings.global";
+ public static final String WS_SETTINGS_GLOBAL_SETTINGS = "ws.settings.global.settings";
+ public static final String WS_SETTINGS_WORKSPACE = "ws.settings.workspace";
+ public static final String WS_SETTINGS_WORKSPACE_SETTINGS = "ws.settings.workspace.settings";
+ public static final String WS_SETTINGS_CANNOT_STORE_PW = "ws.settings.cannot.store.pw";
+ public static final String WS_SETTINGS_PW_MUST_BE_EQUAL = "ws.settings.pw.must.be.equal";
+ public static final String WS_SETTINGS_VIRTUAL_WS_NOT_STORED = "ws.settings.virtual.ws.not.stored";
+ public static final String WS_SETTINGS_COULD_NOT_ENTER_WP = "ws.settings.could.not.enter.wp";
+ public static final String WS_SETTINGS_CREATE_NEW_WP = "ws.settings.create.new.wp";
+ public static final String WS_SETTINGS_WP_NAME_NOT_EMPTY = "ws.settings.wp.name.non.empty";
+ public static final String WS_SETTINGS_WP_EXISTS_ALREADY = "ws.settings.wp.exists.already";
+ public static final String WS_SETTINGS_COULD_NOT_CREATE_DIRECTORY = "ws.settings.could.not.create.directory";
+ public static final String WS_SETTINGS_NEED_DIR_NOT_FILE = "ws.settings.need.dir.not.file";
+ public static final String WS_SETTINGS_COULD_NOT_CREATE_WS = "ws.settings.could.not.create.ws";
+ public static final String WS_SETTINGS_WANT_DELETE_DIR_1 = "ws.settings.want.delete.dir.1";
+ public static final String WS_SETTINGS_WANT_DELETE_DIR_2 = "ws.settings.want.delete.dir.2";
+ public static final String WS_SETTINGS_COULD_NOT_ENTER_VIRTUAL_WS = "ws.settings.could.not.enter.virtual.ws";
+ public static final String WS_SETTINGS_DELETE_FROM_FS = "ws.settings.delete.from.fs";
+ public static final String WS_SETTINGS_NOT_LEGAL_WS_DIR = "ws.settings.not.legal.ws.dir";
+ public static final String WS_SETTINGS_ENTER_USER_NAME = "ws.settings.enter.user.name";
+ public static final String WS_SETTINGS_CREATE_NEW_USER = "ws.settings.create.new.user";
+ public static final String WS_SETTINGS_USER_EXISTS_ALREADY = "ws.settings.user.exists.already";
+ public static final String WS_SETTINGS_REMOVE_USER = "ws.settings.remove.user";
+ public static final String WS_SETTINGS_NOT_LEGAL_USER_DIR = "ws.settings.not.legal.user.dir";
+ public static final String WS_SETTINGS_CHOOSE_OTHER_NAME = "ws.settings.choose.other.name";
+ public static final String WS_SETTINGS_NAME_CONFLICT = "ws.settings.name.conflict";
+ public static final String WS_SETTINGS_COULD_NOT_IMPORT_USER = "ws.settings.could.not.import.user";
+ public static final String WS_CREATION_PANEL_NAME = "ws.creation.panel.name";
+ public static final String WS_CREATION_PANEL_LOCATION = "ws.creation.panel.location";
+ public static final String WS_CREATION_PANEL_BROWSE = "ws.creation.panel.browse";
+ public static final String WELCOME_WORKSPACE = "welcome.workspace";
+ public static final String WELCOME_USERNAME = "welcome.username";
+ public static final String WELCOME_SETTINGS = "welcome.settings";
+ public static final String WELCOME_ENTER = "welcome.enter";
+ public static final String WELCOME_TITLE = "welcome.title";
+ public static final String WELCOME_WRONG_PW = "welcome.wrong.pw";
+ public static final String WELCOME_ENTER_PW = "welcome.enter.pw";
+ public static final String WELCOME_COULD_NOT_ENTER_USER = "welcome.could.not.enter.user";
+ public static final String WS_COULD_NOT_STORE_GC = "storage.could.not.store.gc";
+ public static final String GERMAN_ENGLISH = "pref.general.abz.german.english";
+ public static final String WS_SETTINGS_DAMAGED = "ws.settings.damaged";
+
+ public static final String WS_PROC_UNINITIALIZED = "procedure.unititialized";
+ public static final String WS_PROC_EXECUTABLE = "procedure.executable";
+ public static final String WS_PROC_NOT_EXECUTABLE = "procedure.not.executable";
+ public static final String WS_PROC_ERROR = "procedure.error";
+ public static final String ERROR_PROC_DOES_NOT_EXIST = "error.proc.does.not.exist";
+
+ public static final String WS_GENERATED_PROCEDURES = "ws.generated.procedure";
+ public static final String WS_REDEFINE_AMBIG_PROC = "ws.redefine.ambiguous.procedure";
+ public static final String ERROR_CALL_AMBIG_PROC = "error.call.ambiguous.procedure.name";
+ public static final String WS_COULD_NOT_CREATE_FILE = "ws.could.not.create.file";
+ public static final String WS_ERROR_TITLE = "ws.error.title";
+ public static final String WS_ERROR_COULD_NOT_LOAD_LOGO_FILES = "ws.error.could.not.load.logo.files";
+ public static final String I_AM_SORRY = "i.am.sorry";
+ public static final String WS_ERROR_USERDIR_NOT_DIR = "ws.error.userdir.not.dir";
+ public static final String WS_ERROR_COULD_NOT_STORE_CONFIGS = "ws.error.could.not.store.configs";
+ public static final String WS_ERROR_COULD_NOT_CREATE_FILE = "ws.error.could.not.create.logo.file";
+ public static final String NEW_FILE = "new.file";
+
+ public static final String CONTEST_ERROR_TITLE = "contest.error.title";
+ public static final String CONTEST_ERROR_RECORD_FAILED = "contest.error.could.not.record.file";
+ public static final String CONTEST_MODE_START = "contest.mode.start";
+ public static final String CONTEST_MODE_STOP = "contest.mode.stop";
+ public static final String CONTEST_MODE_FILENAME = "contest.mode.filename";
+ public static final String CONTEST_MODE_BONUS_FILENAME = "contest.mode.bonus.filename";
+ public static final String CONTEST_COULD_NOT_create = "contest.could.not.create";
+ public static final String CONTEST_COULD_NOT_STORE_ = "contest.could.not.store";
+
+ public static final String WS_FILENAME_EXISTS_ALREADY = "ws.filename.exists.already";
+ public static final String WS_ATTEMPT_OPEN_INEXISTENT_FILE = "ws.attempt.open.inexistent.file";
+ public static final String WS_ATTEMPT_CLOSE_NOT_OPEN_FILE = "ws.attempt.close.not.open.file";
+ public static final String WS_AUTO_SAVE_FAILED = "ws.automatic.save.failed";
+ public static final String GENERAL_ERROR_TITLE = "general.error.title";
+ public static final String EDITOR_DISPLAY_PROC_NOT_FOUND = "editor.display.procedure.not.found";
+
+ public static final String NAME_ERROR_TITLE = "name.error.dialog.title";
+ public static final String RENAME_INEXISTENT_FILE = "name.error.rename.inexistent.file";
+ public static final String EMPTY_NAME = "name.error.empty.name";
+ public static final String ILLEGAL_NAME = "illegal.name.msg";
+
+ public static final String COLOR_BLUE = "color.blue";
+ public static final String COLOR_DARK = "color.dark";
+ public static final String COLOR_GRAY = "color.gray";
+ public static final String COLOR_GREEN = "color.green";
+ public static final String COLOR_LIGHT = "color.light";
+ public static final String COLOR_ORANGE = "color.orange";
+ public static final String COLOR_PURPLE = "color.purple";
+ public static final String COLOR_RED = "color.red";
+ public static final String COLOR_YELLOW = "color.yellow";
+ public static final String LOGO_FILE_HAS_ERROR = "logo.file.has.error";
+ public static final String ERROR_WHILE_CREATING_EDITOR = "error.while.creating.editor";
+
+ public static final String LOGO_ERROR_NO_ERROR = "error.no.error";
+ public static final String LOGO_ERROR_MISSING_TO = "error.missing.to";
+ public static final String LOGO_ERROR_MISSING_NAME = "error.missing.name";
+ public static final String LOGO_ERROR_NAME_IS_NUMBER = "error.name.is.number";
+ public static final String LOGO_ERROR_NAME_SPECIAL = "error.name.special";
+ public static final String LOGO_ERROR_VAR_WHITE_AFTER_COLON = "error.var.white.after.colon";
+ public static final String LOGO_ERROR_VAR_SPECIAL = "error.var.special";
+ public static final String LOGO_ERROR_VAR_IS_NUMBER = "error.var.is.number";
+ public static final String LOGO_ERROR_VAR_MISSING_COLON = "error.var.missing.colon";
+ public static final String LOGO_ERROR_OPT_VAR_BRACKET = "error.opt.var.bracket";
+ public static final String LOGO_ERROR_VAR_EXTRA_CHARS = "error.var.extra.chars";
+ public static final String LOGO_ERROR_MISSING_EXPR = "error.missing.expr";
+ public static final String LOGO_ERROR_MORE_CHARS_AFTER_END = "error.more.chars.after.end";
+ public static final String LOGO_ERROR_MISSING_END = "error.missing.end";
+ public static final String LOGO_ERROR_TO_BEFORE_END = "error.to.before.end";
+ public static final String LOGO_ERROR_NAME_IS_KEY_WORD = "error.name.is.key.word";
+ public static final String LOGO_ERROR_AMBIGUOUS = "error.procedure.ambiguous";
+
+ public static final String HIST_MSG_PROCEDURES_UNDEFINED = "hist.msg.procedures.undefined";
+ public static final String ERROR_NETWORK_CONTEXT = "error.network.context";
+
+ public static final String DIALOG_YES = "dialog.yes";
+ public static final String DIALOG_NO = "dialog.no";
+ public static final String US_COULD_NOT_STORE_RECENT_DATA = "us.could.not.store.recent.data";
+ public static final String WS_DOES_NOT_EXIST = "ws.does.not.exist";
+ public static final String WS_NOT_A_WORKSPACE_DIRECTORY = "ws.not.a.workspace.dir";
+ public static final String APP_HELLO = "app.hello";
+
+ public static final String FILE_CONTAINS_ERRORS = "file.contains.errors";
+
+ public static final String US_IMPORT = "us.import";
+ public static final String US_EXPORT = "us.export";
+ public static final String US_SAVE_IMAGE = "us.save.image";
+ public static final String US_EXPORT_MSG = "us.export.msg";
+
+}
diff --git a/logo/src/xlogo/messages/Messenger.java b/logo/src/xlogo/messages/Messenger.java
new file mode 100644
index 0000000..d27dc9c
--- /dev/null
+++ b/logo/src/xlogo/messages/Messenger.java
@@ -0,0 +1,33 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages;
+
+public interface Messenger
+{
+ public void dispatchMessage(String message);
+}
diff --git a/logo/src/xlogo/messages/async/AbstractAsyncMessenger.java b/logo/src/xlogo/messages/async/AbstractAsyncMessenger.java
new file mode 100644
index 0000000..e75fec5
--- /dev/null
+++ b/logo/src/xlogo/messages/async/AbstractAsyncMessenger.java
@@ -0,0 +1,141 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import xlogo.Application;
+
+/**
+ * <p> This class was implemented with the purpose to decouple various parts of XLogo4Schools from the Application-class. <br>
+ * Before, almost every part had to have a reference to {@link Application}. Without the reference to the current frame, no errors or other messages could be displayed.
+ * <p>
+ * This class also allows to dispatch messages before a Medium (such as JFrame for dialogs or HistoryPanel for history messages) is set up. Therefore it maintains an internal queue of messages.
+ * As soon as the medium is set up and ready, the queue will be emptied by displaying all the messages, one after the other.
+ * <p>
+ * This class is thread-safe.
+ */
+public abstract class AbstractAsyncMessenger<M extends AsyncMessage<T>, T> implements AsyncMessenger
+{
+ AsyncMediumAdapter<M, T> mediumAdapter;
+ private ConcurrentLinkedQueue<M> messageQueue;
+ private boolean isWorking;
+
+ public AbstractAsyncMessenger()
+ {
+ messageQueue = new ConcurrentLinkedQueue<M>();
+ }
+
+ /**
+ * @return whether medium is set (not null) and ready
+ */
+ protected synchronized boolean isMediumReady()
+ {
+ return mediumAdapter != null && mediumAdapter.isReady();
+ }
+
+ /**
+ * @return Whether the worker thread is currently not working off the message
+ */
+ protected synchronized boolean isReady()
+ {
+ return isMediumReady() && !isWorking;
+ }
+
+ private synchronized void setIsWorking(boolean isWorking)
+ {
+ this.isWorking = isWorking;
+ }
+
+ public synchronized void setMedium(AsyncMediumAdapter<M, T> medium)
+ {
+ this.mediumAdapter = medium;
+
+ if (medium == null)
+ return;
+
+ if (isReady())
+ workOffQueue();
+ else
+ medium.addMediumReadyListener(this);
+ }
+
+ /**
+ * This should be called by
+ */
+ public void onMediumReady()
+ {
+ workOffQueue();
+ }
+
+ protected void installReadyEventListener(AsyncMediumAdapter<M, T> medium)
+ {
+ medium.addMediumReadyListener(this);
+ }
+
+ protected synchronized AsyncMediumAdapter<M, T> getMedium()
+ {
+ return mediumAdapter;
+ }
+
+ protected void deliverMessage(M message)
+ {
+ messageQueue.add(message);
+ if (isReady())
+ workOffQueue();
+ }
+
+ protected synchronized void workOffQueue()
+ {
+ setIsWorking(true);
+
+ new Thread(new Runnable(){
+ @Override
+ public void run()
+ {
+ while(!messageQueue.isEmpty() && isMediumReady())
+ {
+ M msg = messageQueue.peek();
+ prepareMessage(msg);
+ msg.displayMessage();
+ messageQueue.remove();
+ }
+
+ setIsWorking(false);
+ }
+ }).run();
+ }
+
+ /**
+ * @param message
+ */
+ protected void prepareMessage(M message){
+ message.setMedium(mediumAdapter.getMedium());
+ }
+
+}
diff --git a/logo/src/xlogo/messages/async/AsyncMediumAdapter.java b/logo/src/xlogo/messages/async/AsyncMediumAdapter.java
new file mode 100644
index 0000000..a52c617
--- /dev/null
+++ b/logo/src/xlogo/messages/async/AsyncMediumAdapter.java
@@ -0,0 +1,35 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async;
+
+public interface AsyncMediumAdapter<MessageType extends AsyncMessage<Medium>, Medium>
+{
+ public void addMediumReadyListener(AsyncMessenger messenger);
+ public boolean isReady();
+ public Medium getMedium();
+}
diff --git a/logo/src/xlogo/messages/async/AsyncMessage.java b/logo/src/xlogo/messages/async/AsyncMessage.java
new file mode 100644
index 0000000..5c3c4a8
--- /dev/null
+++ b/logo/src/xlogo/messages/async/AsyncMessage.java
@@ -0,0 +1,51 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async;
+
+import xlogo.messages.Message;
+
+public abstract class AsyncMessage<Medium> extends Message
+{
+ Medium medium;
+
+ public AsyncMessage(String title, String message)
+ {
+ super(title, message);
+ }
+
+ public void setMedium(Medium medium)
+ {
+ this.medium = medium;
+ }
+
+ public Medium getMedium()
+ {
+ return medium;
+ }
+
+}
diff --git a/logo/src/xlogo/messages/async/AsyncMessenger.java b/logo/src/xlogo/messages/async/AsyncMessenger.java
new file mode 100644
index 0000000..99b3c3c
--- /dev/null
+++ b/logo/src/xlogo/messages/async/AsyncMessenger.java
@@ -0,0 +1,35 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async;
+
+import xlogo.messages.Messenger;
+
+public interface AsyncMessenger extends Messenger
+{
+ public void onMediumReady();
+}
diff --git a/logo/src/xlogo/messages/async/dialog/DialogMessage.java b/logo/src/xlogo/messages/async/dialog/DialogMessage.java
new file mode 100644
index 0000000..2ab099d
--- /dev/null
+++ b/logo/src/xlogo/messages/async/dialog/DialogMessage.java
@@ -0,0 +1,75 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async.dialog;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+
+import xlogo.messages.async.AsyncMessage;
+
+class DialogMessage extends AsyncMessage<JFrame>
+{
+ private DialogMessageType type;
+
+ public DialogMessage(DialogMessageType type, String title, String message)
+ {
+ super(title, message);
+ this.type = type;
+ }
+
+ @Override
+ public void displayMessage()
+ {
+ JOptionPane.showMessageDialog(getMedium(),
+ getMessage(),
+ getTitle(),
+ type.getOptionPaneInt());
+ }
+
+ public enum DialogMessageType
+ {
+ ERROR(JOptionPane.ERROR_MESSAGE),
+ WARNING(JOptionPane.WARNING_MESSAGE),
+ PLAIN(JOptionPane.PLAIN_MESSAGE),
+ QUESTION(JOptionPane.QUESTION_MESSAGE),
+ INFORMATION(JOptionPane.INFORMATION_MESSAGE);
+
+ private int msgType;
+
+ private DialogMessageType(int optionPaneInt)
+ {
+ msgType = optionPaneInt;
+ }
+
+ public int getOptionPaneInt()
+ {
+ return msgType;
+ }
+ }
+
+}
diff --git a/logo/src/xlogo/messages/async/dialog/DialogMessenger.java b/logo/src/xlogo/messages/async/dialog/DialogMessenger.java
new file mode 100644
index 0000000..95fa512
--- /dev/null
+++ b/logo/src/xlogo/messages/async/dialog/DialogMessenger.java
@@ -0,0 +1,74 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async.dialog;
+
+import javax.swing.JFrame;
+
+import xlogo.messages.async.AbstractAsyncMessenger;
+import xlogo.messages.async.AsyncMessage;
+import xlogo.messages.async.dialog.DialogMessage.DialogMessageType;
+
+/**
+ * The MessageManager is a singleton class that can be globally used to display error messages in dialogs.
+ *
+ * @author Marko Zivkovic
+ *
+ */
+public class DialogMessenger extends AbstractAsyncMessenger<AsyncMessage<JFrame>, JFrame>
+{
+ /**
+ * Implementation for thread-safe singleton found here : http://www.theserverside.de/singleton-pattern-in-java/ [accessed 14.8.2013]
+ */
+ private static DialogMessenger instance = new DialogMessenger();
+
+ public static DialogMessenger getInstance()
+ {
+ return instance;
+ }
+
+ protected DialogMessenger()
+ {
+ super();
+ }
+
+ @Override
+ public void dispatchMessage(String message)
+ {
+ deliverMessage(new DialogMessage(DialogMessageType.PLAIN, "", message));
+ }
+
+ public void dispatchMessage(String title, String message)
+ {
+ deliverMessage(new DialogMessage(DialogMessageType.PLAIN, title, message));
+ }
+
+ public void dispatchError(String title, String message)
+ {
+ deliverMessage(new DialogMessage(DialogMessageType.ERROR, title, message));
+ }
+}
diff --git a/logo/src/xlogo/messages/async/history/HistoryMessage.java b/logo/src/xlogo/messages/async/history/HistoryMessage.java
new file mode 100644
index 0000000..ae3222c
--- /dev/null
+++ b/logo/src/xlogo/messages/async/history/HistoryMessage.java
@@ -0,0 +1,79 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async.history;
+
+import xlogo.messages.async.AsyncMessage;
+
+class HistoryMessage extends AsyncMessage<HistoryWriter>
+{
+
+ public HistoryMessage(HistoryMessageType messageType, String message)
+ {
+ super(messageType.toString(), message);
+ }
+
+ @Override
+ public void displayMessage()
+ {
+ getMedium().writeMessage(getTitle(), getMessage());
+ }
+
+
+ public enum HistoryMessageType
+ {
+ /*
+ * Note: String values inherited from XLogo, such that HistoryPanel must not be changed too much (for now)
+ */
+
+ /**
+ * Syntax highlighted
+ */
+ NORMAL("normal"),
+ ERROR("erreur"),
+ /**
+ * When the user leaves the editor "You defined ..."
+ */
+ COMMENT("commentaire"),
+ /**
+ * Commands write or print
+ */
+ LOGO_OUTPUT("perso");
+
+ String type;
+
+ private HistoryMessageType(String type)
+ {
+ this.type = type;
+ }
+
+ public String toString()
+ {
+ return type;
+ }
+ }
+}
diff --git a/logo/src/xlogo/messages/async/history/HistoryMessenger.java b/logo/src/xlogo/messages/async/history/HistoryMessenger.java
new file mode 100644
index 0000000..e6bbbf7
--- /dev/null
+++ b/logo/src/xlogo/messages/async/history/HistoryMessenger.java
@@ -0,0 +1,85 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async.history;
+
+import xlogo.gui.HistoryPanel;
+import xlogo.messages.async.AbstractAsyncMessenger;
+import xlogo.messages.async.AsyncMessage;
+import xlogo.messages.async.history.HistoryMessage.HistoryMessageType;
+
+/**
+ * This class helps to decouple various old XLogo classes from {@link Application} and {@link HistoryPanel}
+ * @author Marko Zivkovic
+ * @superclass {@link AbstractAsyncMessenger}
+ */
+public class HistoryMessenger extends AbstractAsyncMessenger<AsyncMessage<HistoryWriter>, HistoryWriter>
+{
+ /**
+ * Implementation for thread-safe singleton found here : http://www.theserverside.de/singleton-pattern-in-java/ [accessed 14.8.2013]
+ */
+ private static HistoryMessenger instance = new HistoryMessenger();
+
+ private HistoryMessenger() {}
+
+ public static HistoryMessenger getInstance()
+ {
+ return instance;
+ }
+
+ /**
+ * Normal Logo style : syntax highlighter
+ */
+ @Override
+ public void dispatchMessage(String message)
+ {
+ deliverMessage(new HistoryMessage(HistoryMessageType.NORMAL, message));
+ }
+
+ /**
+ * Printed in red.
+ * @param message
+ */
+ public void dispatchError(String message)
+ {
+ deliverMessage(new HistoryMessage(HistoryMessageType.ERROR, message));
+ }
+
+ /**
+ * Printed in blue
+ * @param message
+ */
+ public void dispatchComment(String message)
+ {
+ deliverMessage(new HistoryMessage(HistoryMessageType.COMMENT, message));
+ }
+
+ public void dispatchLogoOutput(String message)
+ {
+ deliverMessage(new HistoryMessage(HistoryMessageType.LOGO_OUTPUT, message));
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/messages/async/history/HistoryWriter.java b/logo/src/xlogo/messages/async/history/HistoryWriter.java
new file mode 100644
index 0000000..65bcff6
--- /dev/null
+++ b/logo/src/xlogo/messages/async/history/HistoryWriter.java
@@ -0,0 +1,33 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.messages.async.history;
+
+public interface HistoryWriter
+{
+ public void writeMessage(String messageType, String message);
+}
diff --git a/logo/src/xlogo/storage/Storable.java b/logo/src/xlogo/storage/Storable.java
new file mode 100644
index 0000000..ad5adf2
--- /dev/null
+++ b/logo/src/xlogo/storage/Storable.java
@@ -0,0 +1,267 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.StringTokenizer;
+
+public abstract class Storable implements Serializable
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3506253939129765438L;
+
+ /**
+ * The file's name with extension
+ */
+ private String fileName;
+
+ /**
+ * The Directory where this is stored
+ */
+ private File location;
+
+ /**
+ * Dirty : an object is dirty if it was changed since it was loaded or stored the last time.
+ */
+ private transient boolean dirty = true;
+
+ /**
+ * Will not be stored if virtual.
+ */
+ private transient boolean isVirtual = false;
+
+ /*
+ * PATH BUILDERS
+ */
+
+ public static File getFile(File dir, String fileName)
+ {
+ return new File(dir.toString() + File.separator + fileName);
+ }
+
+
+ public static File getDirectory(File prefix, String dirName)
+ {
+ return new File(prefix.toString() + File.separator + dirName);
+ }
+
+ /*
+ * Abstract
+ */
+
+ /**
+ * Store this object to the file specified by {@link #getFilePath()} if it is dirty
+ * @throws IOException
+ */
+ public abstract void store() throws IOException;
+
+ public abstract void storeCopyToFile(File file) throws IOException, IllegalArgumentException;
+
+ /**
+ * Store this object to the specified file, regardless of whether this is virtual or not.
+ * @param file
+ * @throws IOException
+ * @throws IllegalArgumentException - null is not accepted
+ */
+
+ /*
+ * file name & location
+ */
+
+ public abstract String getFileNameExtension();
+
+ public String getFileName()
+ {
+ return getPlainName() + getFileNameExtension();
+ }
+
+ /**
+ * @return FileName without file extension
+ */
+ public String getPlainName()
+ {
+ return fileName;
+ }
+
+ /**
+ * If this exists on the file system, that file will be renamed. <p>
+ * If newFileName already existed, it is deleted first.
+ * @param newFileName
+ * @throws IllegalArgumentException - If the provided name is not legal.
+ */
+ public void setFileName(String newFileName) throws IllegalArgumentException //TODO make sure callers conform with contract
+ {
+ if (newFileName == null || newFileName.length() == 0)
+ throw new IllegalArgumentException("File name must not be null or empty.");
+
+ if (!checkLegalName(newFileName))
+ throw new IllegalArgumentException("The chose file name contains illegal characters.");
+
+ String ext = getFileNameExtension();
+ String oldName = getPlainName();
+ String newName = newFileName.endsWith(ext) && newFileName.length() > ext.length() ?
+ newFileName.substring(0, newFileName.length() - ext.length())
+ : newFileName;
+
+ if (newName.equals(oldName) && oldName != null)
+ return;
+
+ if (isVirtual || oldName == null)
+ {
+ this.fileName = newFileName;
+ return;
+ }
+
+ File oldPath = getFilePath();
+ this.fileName = newName;
+
+ if (!oldPath.exists())
+ return;
+
+ File newPath = getFilePath();
+ if(newPath.exists())
+ newPath.delete();
+
+ oldPath.renameTo(newPath);
+ }
+
+ /**
+ * @return the directory where this should be stored to.
+ */
+ public File getLocation()
+ {
+ return location;
+ }
+
+ /**
+ * If the specified location does not exist yet, it is created using mkdirs.<br>
+ * To set null or a file that is not a directory or a directory with no write permissions is an error, as long as this is not virtual.<br>
+ * Setting location has no effect if this is virtual.<br>
+ * @param location - the directory where this should be stored to.
+ * @throws IOException
+ * @throws IOException If the specified location is not a directory or no write permissions exist, or the chosen name is not legal.
+ */
+ public void setLocation(File location) throws IllegalArgumentException
+ {
+ if(isVirtual)
+ return;
+
+ if (location == null)
+ throw new IllegalArgumentException("Location must not be null.");
+
+ if(!location.isDirectory())
+ {
+ location.mkdirs();
+ }
+ if(!location.isDirectory() || !location.canWrite())
+ throw new IllegalArgumentException("Cannot store this to specified location : " + location.toString());
+ this.location = location;
+ makeDirty();
+ }
+
+ /**
+ * @return the file where this should be stored to. Returns null if {@link getLocation()} returns null.
+ */
+ public File getFilePath()
+ {
+ if (getLocation() == null)
+ return null;
+ return getFile(getLocation(), getFileName());
+ }
+
+ /**
+ * @return whether the file specified by {@link #getFilePath()} exists.
+ */
+ public boolean existsPhysically()
+ {
+ if (getFilePath() == null)
+ return false;
+
+ return getFilePath().exists();
+ }
+
+ /*
+ * isDirty
+ */
+
+ public boolean isDirty() { return dirty; }
+
+ /**
+ * Should be called from every setter that sets a property that should be stored later
+ * @see StorableObject#makeClean()
+ */
+ protected void makeDirty() { dirty = true; }
+
+ /**
+ * Should be called whenever this was synchronized with its version on the file system (load or store)
+ * @see StorableObject#makeDirty()
+ */
+ protected void makeClean() { dirty = false; }
+
+
+ /*
+ * isVirtual
+ */
+
+ /**
+ * @see #isVirtual()
+ */
+ protected void makeVirtual() { isVirtual = true; }
+ /**
+ * A virtual object will not be stored on the file system, even though {@link store()} was called.
+ * This allows to use the application without having an actual user account and without automatic saving.
+ * @return
+ */
+ public boolean isVirtual() { return isVirtual; }
+
+ // The best I found : http://stackoverflow.com/questions/893977/java-how-to-find-out-whether-a-file-name-is-valid
+ // some windows specific chars are not contained...
+ public static final String ILLEGAL_NAME_CHARACTERS = "/\n\r\t\0\f`?*\\<>|\":";
+
+ public static boolean checkLegalName(String name)
+ {
+ if (name == null || name.length() == 0)
+ return false;
+
+ //StringTokenizer check = new StringTokenizer(name, ILLEGAL_NAME_CHARACTERS, true);
+ //return (check.countTokens() == 1);
+
+ for(char c : name.toCharArray())
+ {
+ if (ILLEGAL_NAME_CHARACTERS.indexOf(c) > -1)
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/logo/src/xlogo/storage/StorableDocument.java b/logo/src/xlogo/storage/StorableDocument.java
new file mode 100644
index 0000000..104f64d
--- /dev/null
+++ b/logo/src/xlogo/storage/StorableDocument.java
@@ -0,0 +1,198 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.util.Calendar;
+
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+
+public abstract class StorableDocument extends Storable
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8218323197066522297L;
+
+ /**
+ * Contents of the file
+ */
+ private String text;
+
+ /**
+ * DEFINE TIME
+ */
+ private Calendar lastSync;
+
+ /**
+ * When this was created or saved the last time using {@link #store()}.
+ * Other store methods will not affect the time.
+ * @return
+ */
+ public Calendar getLastSync()
+ {
+ return lastSync;
+ }
+
+ public StorableDocument()
+ {
+ super();
+ text = "";
+ synced();
+ }
+
+ /**
+ * If this is not virtual, store the file in the source folder of the UserSpace, <br>
+ * and it also stores a copy in the backup folder, if this is required by {@link WorkspaceConfig#getNumberOfBackups()}.
+ */
+ @Override
+ public void store() throws IOException
+ {
+ synced();
+ if (isVirtual())
+ return;
+
+ File file = getFilePath();
+
+ if (!file.getParentFile().exists())
+ file.getParentFile().mkdirs();
+
+ storeCopyToFile(file);
+ }
+
+ protected void synced()
+ {
+ lastSync = Calendar.getInstance();
+ }
+
+ /**
+ *
+ * This is the counterpart to {@link #openFromAnyFile(UserConfig, File)} <br>
+ * Save (export) to any file on the file system.
+ * This works, even if the file is declared virtual.
+ */
+ @Override
+ public void storeCopyToFile(File file) throws IOException, IllegalArgumentException
+ {
+ try
+ {
+ FileOutputStream f = new FileOutputStream(file);
+ BufferedOutputStream b = new BufferedOutputStream(f);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ osw.write(getText());
+ osw.close();
+ b.close();
+ f.close();
+
+ }
+ catch (FileNotFoundException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+
+ /**
+ * Deletes the current file path
+ */
+ public void delete()
+ {
+ if (isVirtual())
+ return;
+ File file = getFilePath();
+ if (file != null && file.exists())
+ file.delete();
+ }
+
+ public String getText()
+ {
+ if (text == null)
+ text = generateText();
+ return text;
+ }
+
+ /**
+ * This is invoked in {@link getText()} when the current text string is currently not defined. <br>
+ * Implement this to map your data to a string. This is the counterpart to {@link #parseText(BufferedReader)}.
+ * @return
+ */
+ protected abstract String generateText();
+
+ /**
+ * Setting text will invalidate the current text.
+ * The new text is then parsed to the concrete document structure.
+ * If possible, use {@link #setTextFromReader(BufferedReader)} for performance reasons.
+ * @param text
+ */
+ public void setText(String text)
+ {
+ invalidateText();
+ if(text == null)
+ return;
+
+ String replIndent = text.replaceAll("\t", " ");
+ StringReader sr = new StringReader(replIndent);
+ BufferedReader br = new BufferedReader(sr);
+ parseText(br);
+ }
+
+ /**
+ * Setting text will invalidate the current text.
+ * The new text is then parsed to the concrete document structure.
+ * @param br
+ */
+ public void setTextFromReader(BufferedReader br)
+ {
+ invalidateText();
+ parseText(br);
+ }
+
+ protected abstract void parseText(BufferedReader text);
+
+ /**
+ * @return Whether the set text could be parsed without errors.
+ */
+ public abstract boolean hasErrors();
+
+ /**
+ * Call this whenever the internal object structure has changed and it should be serialized first, using {@link #generateText()},
+ * when {@link #getText()} is called the next time.
+ */
+ protected void invalidateText()
+ {
+ this.text = null;
+ }
+}
diff --git a/logo/src/xlogo/storage/StorableObject.java b/logo/src/xlogo/storage/StorableObject.java
new file mode 100644
index 0000000..085936a
--- /dev/null
+++ b/logo/src/xlogo/storage/StorableObject.java
@@ -0,0 +1,148 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * The base class for anything that must be stored persistently.
+ * @author Marko Zivkovic
+ */
+public abstract class StorableObject extends Storable implements Serializable {
+
+ private static final long serialVersionUID = -1738873382662156052L;
+
+ /*
+ * PATH BUILDERS
+ */
+
+ /**
+ * @param c
+ * @return X4S_ClassName.ser
+ */
+ @SuppressWarnings("rawtypes")
+ public static String getX4SObjectFileName(Class c)
+ {
+ return "X4S_" + c.getSimpleName();
+ }
+
+ /**
+ * This naming scheme shall be used to store Objects in XLogo4Schools
+ * @param dir - where the instance of c should be stored.
+ * @param c - Class of Objects to be stored persistently
+ * @return pathname for dir/X4S_ClassName.ser
+ */
+ @SuppressWarnings("rawtypes")
+ public static String getFilePath(File dir, Class c)
+ {
+ return dir.toString() + File.separator + getX4SObjectFileName(c);
+ }
+
+ /**
+ * @param dir
+ * @param c
+ * @return file for pathname as defined by {@link #getFilePath(File, Class)}
+ */
+ @SuppressWarnings("rawtypes")
+ public static File getFile(File dir, Class c)
+ {
+ String path = getFilePath(dir, c) + ".ser";
+ return new File(path);
+ }
+
+ @Override
+ public String getFileNameExtension()
+ {
+ return ".ser";
+ }
+
+ /**
+ * Constructor. The FileName will be equal to
+ */
+ public StorableObject()
+ {
+ setFileName(getX4SObjectFileName(getClass()));
+ }
+
+ /*
+ * Store & Load
+ */
+
+ public void store() throws IOException
+ {
+ if(isDirty() && !isVirtual())
+ storeCopyToFile(getFilePath());
+ }
+
+ public void storeCopyToFile(File file) throws IOException, IllegalArgumentException
+ {
+ if(file == null)
+ throw new IllegalArgumentException("file must not be null.");
+
+ if (!isVirtual())
+ {
+ FileOutputStream fileOut = new FileOutputStream(file);
+ ObjectOutputStream out = new ObjectOutputStream(fileOut);
+ out.writeObject(this);
+ out.close();
+ fileOut.close();
+ }
+ makeClean();
+ }
+
+ /**
+ * Load a Storable object from the specified file
+ * @param file
+ * @return the loaded Storable
+ * @throws IOException
+ * @throws ClassNotFoundException
+ * @throws ClassCastException
+ */
+ public static StorableObject loadObject(File file) throws IOException, ClassNotFoundException, ClassCastException
+ {
+ FileInputStream fileIn = new FileInputStream(file);
+ ObjectInputStream in = new ObjectInputStream(fileIn);
+ Object object = in.readObject();
+ in.close();
+ fileIn.close();
+
+ if (!(object instanceof StorableObject))
+ throw new ClassCastException("The specified file (" + file.toString() + ") does not contain an instance of Storable: " + object.getClass().toString());
+
+ StorableObject storable = (StorableObject) object;
+ storable.makeClean();
+ return storable;
+ }
+
+}
diff --git a/logo/src/xlogo/storage/WSManager.java b/logo/src/xlogo/storage/WSManager.java
new file mode 100644
index 0000000..73ef556
--- /dev/null
+++ b/logo/src/xlogo/storage/WSManager.java
@@ -0,0 +1,443 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage;
+
+import java.io.File;
+import java.io.IOException;
+
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+
+/**
+ * Singleton Class for maintaining XLogo4Schools workspace, properties, reading and writing the various config files
+ * <p>
+ * The workspace can be entered without using a user account. in that case, one enters as a <b>virtual user</b>.
+ * While working as virtual user, the changes of preferences and the programs are not stored persistently.
+ * However, one can still export and import program files. This behavior is similar to XLogo's file management that uses the classic save/open machanisms.
+ * <br><br>
+ * <p> XLogo4Schools maintains the following files and directories on the file system
+ * <p>
+ * <li> user_home/X4S_GlobalConfig.ser - this file stores information about how to access the various workspaces ({@link GlobalConfig})</li>
+ *
+ * <p>
+ * <li> workspaceLocation/ - the folder of a workspace on the file system</li>
+ * <li> workspaceLocation/X4S_WorkspaceConfig.ser - Information about the workspace ({@link WorkspaceConfig})</li>
+ * <li> workspaceLocation/user_i/ - Project folder of "user i" in the workspace</li>
+ * <li> workspaceLocation/user_i/X4S_UserConfig.ser - User preferences and settings ({@link UserConfig})</li>
+ * <li> workspaceLocation/user_i/file_j_v.lgo - Version v of file_j.lgo (the last n versions are kept)</li>
+ * <li> workspaceLocation/user_i/competition_protocol_k.txt - the protocol of the k'th recorded competition/session of user i</li>
+ * <p>
+ * The files with the ending .ser are serialized objects. They are loaded from the file system when a workspace or userspace is entered.
+ * The files are (re-)written to the file system whenever a user space, a workspace or XLogo4Schools is left.
+ * <p>
+ * <br><br>
+ * <p> <b>Invariant</b> : there is always an active GlobalConfig, WorkspaceConfig, and UserConfig.
+ * <p> Upon creation or loading of some config, it decides which sub-config should be loaded.
+ * <li> GlobalConfig tries to enter the last used workspace if possible, otherwise it enters the virtual workspace. </li>
+ * <li> WorksspaceConfig tries to enter the last active userspace if possible, otherwise it enters the virtual userspace. </li>
+ *
+ * @author Marko
+ */
+public class WSManager {
+
+
+ /*
+ * Singleton instantiation
+ */
+ private static WSManager instance;
+
+ public static WSManager getInstance(){
+ if (instance == null){
+ instance = new WSManager();
+ }
+ return instance;
+ }
+
+ /**
+ * This is a shortcut for {@code WSManager.getInstance().getWorkspaceConfigInstance()}
+ * This is usually not null {@link WSManager},
+ * but it is for a short time while a workspace is being switched or the program fails to enter a workspace.
+ * @return
+ */
+ public static WorkspaceConfig getWorkspaceConfig()
+ {
+ return getInstance().getWorkspaceConfigInstance();
+ }
+
+ /**
+ * This is a shortcut for {@code WSManager.getInstance().getGlobalConfigInstance()}
+ * This is never null by definition of {@link WSManager}
+ * @return
+ */
+ public static GlobalConfig getGlobalConfig()
+ {
+ return getInstance().getGlobalConfigInstance();
+ }
+
+ /**
+ * This is a shortcut for {@code WSManager.getInstance().getUserConfigInstance()}
+ * <p> Note that this might be null, if no user has entered his user space.
+ * @return
+ */
+ public static UserConfig getUserConfig()
+ {
+ return getInstance().getUserConfigInstance();
+ }
+
+ private WSManager() {
+ globalConfig = GlobalConfig.create();
+ }
+
+ /*
+ * Config access
+ */
+ private final GlobalConfig globalConfig;
+
+ /**
+ * @return the instance of the GlobalConfig
+ */
+ public GlobalConfig getGlobalConfigInstance()
+ {
+ return globalConfig;
+ }
+
+ /**
+ * @return the active workspace
+ */
+ public WorkspaceConfig getWorkspaceConfigInstance()
+ {
+ return getGlobalConfigInstance().getCurrentWorkspace();
+ }
+
+ /**
+ * @return the active user
+ */
+ public UserConfig getUserConfigInstance()
+ {
+ WorkspaceConfig wc = getWorkspaceConfigInstance();
+ if (wc == null)
+ return null;
+ else
+ return wc.getActiveUser();
+ }
+
+ /*
+ * WORKSPACE CONFIG : create, delete, enter
+ */
+
+ /**
+ * A new workspace is created in the defined directory.
+ * All Necessary files and folders are created and the workspace is logically added to the globalConfig.
+ * @see WorkspaceConfig#loadWorkspace(File)
+ * @param dir
+ * @param name
+ * @throws IOException
+ */
+ public void createWorkspace(File dir, String name) throws IOException
+ {
+ getGlobalConfigInstance().createWorkspace(dir, name);
+ }
+
+ public void deleteWorkspace(String wsName, boolean deleteFromDisk) throws SecurityException
+ {
+ File wsDir = getGlobalConfigInstance().getWorkspaceDirectory(wsName);
+ getGlobalConfigInstance().removeWorkspace(wsName);
+ if (deleteFromDisk)
+ deleteFullyRecursive(wsDir);
+ }
+
+ /**
+ * @param wsDir
+ * @throws IllegalArgumentException if wsDir is not a legal workspace directory
+ */
+ public void importWorkspace(File wsDir, String wsName) throws IllegalArgumentException
+ {
+ getGlobalConfigInstance().importWorkspace(wsDir, wsName);
+ }
+
+ /**
+ * Load the workspace
+ * <p>Always succeeds if workspaceName equals {@link WorkspaceConfig#VIRTUAL_WORKSPACE}
+ * @param workspaceName - the workspace to load and enter
+ * @throws IOException - if the old workspace could not be loaded
+ */
+ public void enterWorkspace(String workspaceName) throws IOException
+ {
+ getGlobalConfigInstance().enterWorkspace(workspaceName);
+ }
+
+ /* *
+ * @throws IOException If workspace could not be saved.
+ * /
+ public void leaveWorkspace() throws IOException
+ {
+ getGlobalConfig().leaveWorkspace();
+ }*/
+
+
+ /*
+ * USER CONFIG : create, delete, enter
+ */
+
+ /**
+ * A new user is created in the current workspace.
+ * All Necessary files and folders are created and the workspace is logically added to the globalConfig.
+ * @param username
+ */
+ public void createUser(String username)
+ {
+ getWorkspaceConfigInstance().createUser(username);
+ }
+
+ /**
+ * @param username
+ * @param deleteFromDisk
+ */
+ public void deleteUser(String username, boolean deleteFromDisk)
+ {
+ File location = StorableObject.getDirectory(getWorkspaceConfigInstance().getLocation(), username);
+ getWorkspaceConfigInstance().removeUser(username);
+ if (deleteFromDisk)
+ {
+ try{
+ deleteFullyRecursive(location);
+ }
+ catch(SecurityException e) {
+ System.out.println("Files not deleted: " + e.toString());
+ }
+ }
+ }
+
+ public void importUser(File userDir, String newUserName) throws IllegalArgumentException, IOException
+ {
+ getWorkspaceConfigInstance().importUser(userDir, newUserName);
+ }
+
+ /**
+ * @throws IOException If the old userConfig could not be stored.
+ */
+ public void enterUserSpace(String name) throws IOException
+ {
+ if (getWorkspaceConfigInstance() == null)
+ throw new IllegalStateException("Must be in WorkspaceDirectory first to enter UserSpace.");
+
+ getWorkspaceConfigInstance().enterUserSpace(name);
+ }
+
+ /*
+ * DIRECTORIES & FILE MANIPULATION
+ */
+
+ public static File[] listDirectories(File dir)
+ {
+ return dir.listFiles(new java.io.FileFilter() {
+ public boolean accept(File pathname) {
+ return pathname.isDirectory();
+ }
+ });
+ }
+
+ /**
+ * A directory is considered a workspace directory,
+ * if it contains a file for {@link WorkspaceConfig}, as defined by {@link StorableObject#getFilePath(File, Class)}
+ * @param dir
+ * @return
+ */
+ public static boolean isWorkspaceDirectory(File dir)
+ {
+ if (dir == null)
+ return false;
+
+ if(!dir.isDirectory())
+ return false;
+
+ File wcf = WorkspaceConfig.getFile(dir, WorkspaceConfig.class);
+ if(!wcf.isFile())
+ return false;
+
+ return true;
+ }
+
+ /**
+ * A directory is considered a user directory,
+ * if it contains a file for {@link UserConfig}, as defined by {@link StorableObject#getFilePath(File, Class)}
+ * @param dir
+ * @return
+ */
+ public static boolean isUserDirectory(File dir)
+ {
+ if (dir == null)
+ return false;
+
+ if(!dir.isDirectory())
+ return false;
+
+ File ucf = UserConfig.getFile(dir, UserConfig.class);
+ if(!ucf.isFile())
+ return false;
+
+ return true;
+ }
+
+ /**
+ * If "from" denotes a file, then "to" should be a file too.
+ * The contents of file "from" are copied to file "to".
+ * "to" is created, if it does not exists, using mkdirs.
+ *
+ * <p>If "from" denotes a directory, then "to" should be a directory too.
+ * The contents of directory "from" are copied recursively to directory "to".
+ * "to" is created, if it does not exists, using mkdirs.
+ * @param from - must exist
+ * @param to - must not exist
+ * @throws IOException
+ */
+ public static void copyFullyRecursive(File from, File to) throws IOException
+ {
+ if (!from.exists())
+ throw new IllegalArgumentException("'from' (" + from.toString() + ") must exist.");
+
+ if(from.isFile())
+ {
+ copyFile(from, to);
+ return;
+ }
+
+ // else to is directory
+ to.mkdirs();
+
+ for(File src : from.listFiles())
+ {
+ File dest = new File(to.toString() + File.separator + src.getName());
+ if (src.isFile())
+ {
+ copyFile(src, dest);
+ }else if (src.isDirectory())
+ {
+ copyFullyRecursive(src, dest);
+ }
+ }
+
+ }
+
+ public static void copyFile(File from, File to) throws IOException
+ {
+ if (!from.isFile())
+ throw new IllegalArgumentException("File 'from' (" + from.toString() + ") must exist.");
+
+ if (to.exists())
+ {
+ if (!to.isFile())
+ throw new IllegalArgumentException("File 'to' (" + from.toString() + ") must be a file.");
+ }
+ else
+ {
+ File parent = to.getParentFile();
+ if (!parent.exists())
+ to.getParentFile().mkdirs();
+ }
+
+ Utils.copyFile(from, to);
+ }
+
+ /**
+ * @param victim
+ * @throws SecurityException If one tries to delete some directory that is not under control of this application (Workspace or User)
+ */
+ public static void deleteFullyRecursive(File victim) throws SecurityException
+ {
+ if (!victim.exists())
+ return;
+
+ if (victim.isFile())
+ {
+ victim.delete();
+ return;
+ }
+
+ if (!isGlobalConfigDirectory(victim) && !isWorkspaceDirectory(victim) && !isUserDirectory(victim))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ "Security Violation",
+ "Attempt to delete a directory that is not under control of this application: " + victim.toString());
+ throw new SecurityException();
+ }
+
+ // Delete all sub-directories
+ for (File f : victim.listFiles())
+ {
+ uncheckedRecursiveDelete(f);
+ victim.delete();
+ }
+
+ // Delete directory itself
+ victim.delete();
+ }
+
+ /**
+ * CAUTION : Don't use this unless you are sure that the directory you want to delete is a XLogo4Schools directory.
+ * Otherwise it could delete just everything.
+ * @param victim
+ */
+ private static void uncheckedRecursiveDelete(File victim)
+ {
+ if (!victim.exists())
+ return;
+
+ if (victim.isFile())
+ {
+ victim.delete();
+ return;
+ }
+
+ // Delete all sub-directories
+ for (File f : victim.listFiles())
+ {
+ uncheckedRecursiveDelete(f);
+ victim.delete();
+ }
+
+ // Delete directory itself
+ victim.delete();
+ }
+
+ public static boolean isGlobalConfigDirectory(File dir)
+ {
+ if(!dir.isDirectory())
+ return false;
+
+ String name = dir.getName();
+ if(!name.startsWith("X4S_"))
+ return false;
+
+
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/storage/global/GlobalConfig.java b/logo/src/xlogo/storage/global/GlobalConfig.java
new file mode 100644
index 0000000..6cd9124
--- /dev/null
+++ b/logo/src/xlogo/storage/global/GlobalConfig.java
@@ -0,0 +1,545 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.global;
+
+import java.awt.Font;
+import java.awt.GraphicsEnvironment;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+import xlogo.Logo;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.StorableObject;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.WorkspaceConfig;
+
+/**
+ * This Config is stored in default location : "user.home". It contains information about the currently used workspaces on the computer
+ * @author Marko Zivkovic
+ */
+public class GlobalConfig extends StorableObject implements Serializable {
+
+ private static final long serialVersionUID = 2787615728665011813L;
+
+ public static final String LOGO_FILE_EXTENSION = ".lgo";
+ public static boolean DEBUG = true; // TODO set false
+
+ /**
+ * Creates the global config at default location, together with a virtual workspace
+ */
+ protected GlobalConfig()
+ {
+ try
+ {
+ setLocation(getDefaultLocation());
+ }
+ catch (IllegalArgumentException ignore) { } // This is thrown if name illegal, but it is legal
+ workspaces = new TreeMap<String, String>();
+ workspaces.put(WorkspaceConfig.VIRTUAL_WORKSPACE, "");
+ }
+
+ /**
+ * If GlobalConfig exists on the file system in default location, it is loaded, otherwise it will be created there.
+ * @return
+ */
+ public static GlobalConfig create()
+ {
+ File gcf = getFile(getDefaultLocation(), GlobalConfig.class);
+
+ GlobalConfig globalConfig = null;
+
+ try
+ {
+ if (gcf.exists())
+ globalConfig = (GlobalConfig) loadObject(gcf);
+ else
+ {
+ globalConfig = new GlobalConfig();
+ globalConfig.store();
+ }
+ }catch(Exception e)
+ {
+ // Best effort : We will try to operate the program without storing anything on disk
+ globalConfig = getNewVirtualInstance();
+ DialogMessenger.getInstance().dispatchError("Error while setting up XLogo4Schools", "Could not create or open GlobalConfig file at default location: " + e.toString());
+ }
+ globalConfig.enterLastUsedWorkspace();// This is used to have a workspace ready at the beginning, without any user interaction.
+ return globalConfig;
+ }
+
+ public static GlobalConfig getNewVirtualInstance()
+ {
+ GlobalConfig gc = new GlobalConfig();
+ gc.makeVirtual();
+ return gc;
+ }
+
+ /**
+ * @return File from system property "user.home"
+ */
+ public static File getDefaultLocation()
+ {
+ return new File(System.getProperty("user.home"));
+ }
+
+ /*
+ * Physical Workspaces (stored on file system)
+ */
+
+ public void createWorkspace(File dir, String workspaceName) throws IOException
+ {
+ if (WorkspaceConfig.createNewWorkspace(dir, workspaceName) != null)
+ addWorkspace(workspaceName, dir.toString());
+ }
+
+ public void importWorkspace(File workspaceDir, String wsName)
+ {
+ if(!WSManager.isWorkspaceDirectory(workspaceDir))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.WS_ERROR_TITLE),
+ workspaceDir + " " + Logo.messages.getString(MessageKeys.WS_NOT_A_WORKSPACE_DIRECTORY));
+ return;
+ }
+
+ addWorkspace(wsName, workspaceDir.getParent());
+ }
+
+ /**
+ * Load the specified workspace from the file system.
+ * @param workspaceName
+ * @return the specified workspace or null if it does not exist.
+ * @throws IOException
+ */
+ private WorkspaceConfig retrieveWorkspace(String workspaceName) throws IOException
+ {
+ if(!existsWorkspace(workspaceName))
+ {
+ System.out.print("Attempting to load an inexistent workspace.");
+ return null;
+ }
+ File dir = getWorkspaceLocation(workspaceName);
+ WorkspaceConfig wc = WorkspaceConfig.loadWorkspace(dir, workspaceName);
+
+ if (wc == null)
+ {
+ WSManager.getInstance().deleteWorkspace(workspaceName, false);
+ }
+ return wc;
+
+ }
+
+ /*
+ * Logical Workspaces (name and location stored in Map)
+ */
+
+ private TreeMap<String,String> workspaces;
+
+ /**
+ * @param workspaceName
+ * @param location where the workspace is located: location/workspaceName/
+ */
+ public void addWorkspace(String workspaceName, String location)
+ {
+ workspaces.put(workspaceName, location);
+ makeDirty();
+ notifyWorkspaceListChanged();
+ }
+
+ public void removeWorkspace(String workspaceName)
+ {
+ workspaces.remove(workspaceName);
+ makeDirty();
+ notifyWorkspaceListChanged();
+ }
+
+ /**
+ * @param wsName
+ * @return the location of the workspace in the file system, or null if the workspace does not exist
+ */
+ public File getWorkspaceLocation(String wsName)
+ {
+ String location = workspaces.get(wsName);
+ if(location == null)
+ return null;
+ return new File(location);
+ }
+
+ public File getWorkspaceDirectory(String wsName)
+ {
+ File wsLocation = getWorkspaceLocation(wsName);
+ if(wsLocation == null)
+ return null;
+ return new File(wsLocation.toString() + File.separator + wsName);
+ }
+
+ /**
+ * @return the names of all existing workspaces
+ */
+ public String[] getAllWorkspaces()
+ {
+ return (String[]) workspaces.keySet().toArray(new String[workspaces.size()]);
+ }
+
+ /**
+ * A workspace exists logically, if its location is known by the GlobalConfig.
+ * @param workspace
+ * @return
+ */
+ public boolean existsWorkspace(String workspace)
+ {
+ return getWorkspaceLocation(workspace) != null;
+ }
+
+ /*
+ * Last used workspace
+ */
+ private String lastUsedWorkspace;
+
+ public String getLastUsedWorkspace()
+ {
+ return lastUsedWorkspace;
+ }
+
+ /**
+ * Succeeds if the workspace exists
+ * @param workspace
+ */
+ private void setLastUsedWorkspace(String workspace)
+ {
+ if(existsWorkspace(workspace))
+ {
+ lastUsedWorkspace = new String(workspace);
+ makeDirty();
+ }
+ }
+
+ /**
+ * This is used to have a workspace ready at the beginning, without any user interaction.
+ * <p>
+ * Enters the workspace that was used the last time XLogo4Schools was run on this computer.
+ * If no regular workspace is available, a purely logical "virtual workspace" is entered instead.
+ */
+ private void enterLastUsedWorkspace()
+ {
+ String last = getLastUsedWorkspace();
+
+ if(last == null || !existsWorkspace(last))
+ last = WorkspaceConfig.VIRTUAL_WORKSPACE; // this exists, see constructor
+
+ try {
+ enterWorkspace(last);
+ } catch (IOException e1) {
+ try { enterWorkspace(WorkspaceConfig.VIRTUAL_WORKSPACE); } catch (IOException e2) { }
+ DialogMessenger.getInstance().dispatchError("Workspace Error", "Cannot enter workspace: " + e1.toString());
+ }
+ }
+
+ /*
+ * Current Workspace
+ */
+
+ private transient WorkspaceConfig currentWorkspace;
+
+ public WorkspaceConfig getCurrentWorkspace()
+ {
+ return currentWorkspace;
+ }
+
+ /**
+ * Load the workspace
+ * <p>Always succeeds if workspaceName equals {@link WorkspaceConfig#VIRTUAL_WORKSPACE}
+ * @param workspaceName - the workspace to load and enter
+ * @throws IOException - if the workspace could not be loaded
+ */
+ public void enterWorkspace(String workspaceName) throws IOException
+ {
+ if(currentWorkspace != null)
+ {
+ leaveWorkspace();
+ }
+ currentWorkspace = retrieveWorkspace(workspaceName);
+ if (currentWorkspace == null)
+ currentWorkspace = retrieveWorkspace(WorkspaceConfig.VIRTUAL_WORKSPACE);
+
+ setLastUsedWorkspace(workspaceName);
+
+ notifyWorkspacEntered();
+ }
+
+ /**
+ * @throws IOException If workspace could not be saved.
+ */
+ void leaveWorkspace() throws IOException
+ {
+ if(currentWorkspace == null)
+ throw new IllegalStateException("Attempt to leave workspace without being in one.");
+
+ if(currentWorkspace.getActiveUser() != null)
+ {
+ currentWorkspace.leaveUserSpace();
+ }
+
+ if(currentWorkspace.isDirty())
+ currentWorkspace.store();
+
+ currentWorkspace = null;
+ }
+
+ /*
+ * Password protection
+ */
+
+ /**
+ * if null, no password is requested
+ */
+ private byte[] masterPassword = null;
+
+ /**
+ * Need old password to authenticate
+ * @param oldPw initially null
+ * @param newPw
+ * @return success
+ */
+ public boolean setNewPassword(String oldPw, String newPw)
+ {
+ if(masterPassword == null || authenticate(oldPw))
+ {
+ if (newPw == null)
+ masterPassword = null;
+ else
+ masterPassword = hash(newPw);
+ makeDirty();
+ return true;
+ }else
+ {
+ return false;
+ }
+ }
+
+ public boolean isPasswordRequired()
+ {
+ return masterPassword != null;
+ }
+
+ public boolean authenticate(String password){
+ if (masterPassword == null)
+ return true;
+ String entered = null;
+ if (password != null)
+ entered = new String(hash(password));
+ String master = new String(masterPassword);
+ boolean auth = master.equals(entered);
+ return auth;
+ }
+
+ /**
+ * Hashing the password with MD5 is enough for this application. We just don't want to store readable plain text.
+ * Note that MD5 is generally considered insecure for security critical applications.
+ * @param text
+ * @return hashed bytes
+ */
+ private byte[] hash(String text)
+ {
+ if (text == null)
+ return null;
+
+ byte[] bytesOfMessage;
+ try {
+ bytesOfMessage = text.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e1) {
+ bytesOfMessage = text.getBytes(); // this should not happen anyway
+ }
+
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ return md.digest(bytesOfMessage);
+ } catch (NoSuchAlgorithmException e) {
+ return bytesOfMessage; // this should not happen anyway
+ }
+ }
+
+ /*
+ * PATH
+ */
+
+ /**
+ * This Stack contains all startup files
+ */
+ private ArrayList<String> path = new ArrayList<String>();
+
+ public ArrayList<String> getPath() {
+ return path;
+ }
+
+
+ public void setPath(ArrayList<String> path) {
+ this.path = path;
+ makeDirty();
+ }
+
+ public static String getVersion() {
+ return "XLogo4Schools 0.0.1";
+ }
+
+ /**
+ * Note : should be equal as in {@link Lanceur}
+ */
+ //private static String PROPERTIES_PREFIX = "ch.ethz.abz.xlogo4schools";
+
+ /**
+ * Note : should be equal as in {@link Lanceur}
+ */
+ private static int DEFAULT_MEMORY_ALLOC = 128;
+
+ private static int maximumMemory;
+
+ /**
+ * The Maximum amount of memory that this application is allowed to consume by the JVM
+ * @return
+ */
+ public static int getMaximumMemory()
+ {
+ if (maximumMemory < 64)
+ {
+ // This doesn't work as expected :-(
+ //Preferences prefs = Preferences.systemRoot().node(PROPERTIES_PREFIX);
+ //maximumMemory = prefs.getInt("appMemory", DEFAULT_MEMORY_ALLOC);
+ maximumMemory = DEFAULT_MEMORY_ALLOC;
+ }
+ return maximumMemory;
+ }
+
+ private transient int maxMemoryAtNextStart = getMaximumMemory();
+
+ public static final Font[] fonts = GraphicsEnvironment
+ .getLocalGraphicsEnvironment().getAllFonts();// Toolkit.getDefaultToolkit().getFontList();
+
+ /**
+ * @return The amount of memory in MB that Lanceur will cause JVM to allocate to XLogo4Schools the next time this application is started.
+ */
+ public int getMaxMemoryAtNextStart()
+ {
+ if (maxMemoryAtNextStart < 64)
+ maxMemoryAtNextStart = getMaximumMemory();
+ return maxMemoryAtNextStart;
+ }
+ /**
+ * @see #getMaxMemoryAtNextStart()
+ * cannot set this below 64MB
+ * @param maxMemory
+ */
+ public void setMaxMemoryAtNextStart(int maxMemory)
+ {
+ if (maxMemory < 64)
+ return;
+ // This doesn't work as well :-(
+ //Preferences prefs = Preferences.systemRoot().node(PROPERTIES_PREFIX);
+ //prefs.putInt("appMemory", maxMemory);
+ }
+
+ static public int police_id(Font font) {
+ for (int i = 0; i < fonts.length; i++) {
+ if (fonts[i].getFontName().equals(font.getFontName()))
+ return i;
+ }
+ return 0;
+ }
+
+ /**
+ * The amount of memory that the memory checker allows the application to consume.
+ * It's 0.9*{@link #getMaximumMemory()}} in bytes.
+ */
+ public static long getMemoryThreshold()
+ {
+ return (long) (0.9 * ((long) GlobalConfig.getMaximumMemory() * 1024L * 1024L));
+ }
+
+
+ /* * * * * * *
+ * Event Handling
+ * * * * * * */
+
+ // workspace list change
+
+ private transient ArrayList<ActionListener> workspaceListChangeListeners;
+
+ public void addWorkspaceListChangeListener(ActionListener listener)
+ {
+ if (workspaceListChangeListeners == null)
+ workspaceListChangeListeners = new ArrayList<ActionListener>();
+ workspaceListChangeListeners.add(listener);
+ }
+
+ public void removeWorkspaceListChangeListener(ActionListener listener)
+ {
+ workspaceListChangeListeners.remove(listener);
+ }
+
+ private void notifyWorkspaceListChanged()
+ {
+ ActionEvent event = new ActionEvent(this, 0, "workspaceListChanged");
+ for (ActionListener listener : workspaceListChangeListeners)
+ listener.actionPerformed(event);
+ }
+
+ // enter workspace event
+
+ private transient ArrayList<ActionListener> enterWorkspaceListeners;
+
+ public void addEnterWorkspaceListener(ActionListener listener)
+ {
+ if (enterWorkspaceListeners == null)
+ enterWorkspaceListeners = new ArrayList<ActionListener>();
+ enterWorkspaceListeners.add(listener);
+ }
+
+ public void removeEnterWorkspaceListener(ActionListener listener)
+ {
+ enterWorkspaceListeners.remove(listener);
+ }
+
+ private void notifyWorkspacEntered()
+ {
+ if (enterWorkspaceListeners == null)
+ return;
+ ActionEvent event = new ActionEvent(this, 0, "workspaceEntered");
+ for (ActionListener listener : enterWorkspaceListeners)
+ listener.actionPerformed(event);
+ }
+
+}
diff --git a/logo/src/xlogo/storage/user/DrawQuality.java b/logo/src/xlogo/storage/user/DrawQuality.java
new file mode 100644
index 0000000..565bb53
--- /dev/null
+++ b/logo/src/xlogo/storage/user/DrawQuality.java
@@ -0,0 +1,55 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.user;
+
+public enum DrawQuality {
+ NORMAL(0), HIGH(1), LOW(2);
+
+ int value;
+
+ private DrawQuality(int value)
+ {
+ this.value = value;
+ }
+
+ public int getValue()
+ {
+ return value;
+ }
+
+ public static DrawQuality getDrawQuality(int q)
+ {
+ switch(q)
+ {
+ case 2: return LOW;
+ case 1: return HIGH;
+ case 0:
+ default: return NORMAL;
+ }
+ }
+}
diff --git a/logo/src/xlogo/storage/user/LookAndFeel.java b/logo/src/xlogo/storage/user/LookAndFeel.java
new file mode 100644
index 0000000..b4a2187
--- /dev/null
+++ b/logo/src/xlogo/storage/user/LookAndFeel.java
@@ -0,0 +1,32 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.user;
+
+public enum LookAndFeel {
+ JAVA, MOTIF, NATIVE;
+}
diff --git a/logo/src/xlogo/storage/user/PenShape.java b/logo/src/xlogo/storage/user/PenShape.java
new file mode 100644
index 0000000..cce19ee
--- /dev/null
+++ b/logo/src/xlogo/storage/user/PenShape.java
@@ -0,0 +1,52 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.user;
+
+public enum PenShape {
+ OVAL(1), SQUARE(0);
+
+ private int value;
+
+ private PenShape(int value)
+ {
+ this.value = value;
+ }
+
+ public int getValue()
+ {
+ return value;
+ }
+
+ public static PenShape getPenShape(int value)
+ {
+ if (value == 1)
+ return OVAL;
+ else
+ return SQUARE;
+ }
+}
diff --git a/logo/src/xlogo/storage/user/UserConfig.java b/logo/src/xlogo/storage/user/UserConfig.java
new file mode 100644
index 0000000..c2f85c5
--- /dev/null
+++ b/logo/src/xlogo/storage/user/UserConfig.java
@@ -0,0 +1,918 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.user;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.GraphicsEnvironment;
+import java.awt.Toolkit;
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+
+import xlogo.Logo;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.StorableObject;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+
+/**
+ * This Config is user specific. It is stored in the {@link #workspaceLocation} in the user's folder.
+ * @author Marko Zivkovic
+ */
+public class UserConfig extends StorableObject implements Serializable
+{
+ private static final long serialVersionUID = 8897730869795295485L;
+
+ // This was initially in Panel_Font, which is not used anymore.
+ public static final Font[] fontes = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
+
+ static public int police_id(Font font)
+ {
+ for (int i = 0; i < fontes.length; i++)
+ {
+ if (fontes[i].getFontName().equals(font.getFontName()))
+ return i;
+ }
+ return 0;
+ }
+
+
+ /**
+ * Name of the virtual user
+ * @see #isVirtual()
+ * @see #createVirtualUser()
+ */
+ public static final String VIRTUAL_USER = "Guest";
+
+ private static final String SRC_DIR_NAME = "src";
+ private static final String BACKUPS_DIR_NAME = "backups";
+ private static final String CONTEST_DIR_NAME = "contest";
+ private static final String COMMAND_LINE_RECORD_NAME = "commandLineHistory";
+
+ protected UserConfig()
+ {
+ super();
+ }
+
+ /*
+ * Static constructors
+ */
+
+ /**
+ * A virtual user can enter the application in a virtual workspace without having an actual user account on the file system. Hence nothing will be stored.
+ * A regular user (not virtual) will have his own folder in a regular workspace on the file system and all his preferences and files are stored there.
+ * To create a regular user, use {@link #createNewUser(File, String)},
+ * to load a regular user from the file system, user {@link #loadUser(File, String)}}.
+ * @see #isVirtual()
+ * @return a virtual workspace
+ */
+ public static UserConfig createVirtualUser()
+ {
+ UserConfig vuc = new UserConfig();
+ vuc.makeVirtual();
+ return vuc;
+ }
+
+ /**
+ * Loads the UserConfig from the specified user, or creates it, if it's missing or corrupted.
+ * If workspace specifies a virtual workspace, a virtual user is created instead. In that case the error manager is informed.
+ * @param workspace
+ * @param username
+ * @return the loaded UserConfig
+ * @throws IOException
+ */
+ public static UserConfig loadUser(WorkspaceConfig workspace, String username)
+ {
+ if(workspace.isVirtual())
+ return createVirtualUser();
+
+ if (!WSManager.isWorkspaceDirectory(workspace.getLocation()))
+ throw new IllegalArgumentException(workspace.toString() + " is not a valid Workspace.");
+
+ File userDirectory = getDirectory(workspace.getLocation(), username);
+ File userConfigFile = getFile(userDirectory, UserConfig.class);
+
+ UserConfig userConfig = null;
+
+ if(userConfigFile.exists())
+ {
+ try
+ {
+ userConfig = (UserConfig) loadObject(userConfigFile);
+ userConfig.setLocation(userDirectory);
+ }catch(ClassNotFoundException e1) { } // this won't happen
+ catch(IOException e2) {
+ DialogMessenger.getInstance().dispatchError("Workspace Error", "Could not load user config file: " + e2.toString());
+ }
+ }
+
+ // If file is missing, it will be created again.
+ if (userConfig == null)
+ userConfig = createNewUser(workspace, username);
+
+ return userConfig;
+ }
+
+ /**
+ * If workspace is valid, then a new userSpace is created within that workspace.
+ * All necessary files and directories are created.
+ * <p>
+ * If a UserConfig file for the specified user already exists, it will be overwritten.
+ * If an error occurs while creating the file, the userConfig is just returned without saving to file.
+ * TODO In that case, the global ErrorManager is informed.
+ * @param workspace - must exist physically
+ * @param username
+ * @return the created UserConfig - or null, if one of the arguments was not legal
+ */
+ public static UserConfig createNewUser(WorkspaceConfig workspace, String username)
+ {
+ if(workspace.isVirtual())
+ return createVirtualUser();
+
+ if (!WSManager.isWorkspaceDirectory(workspace.getLocation()))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.WS_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.WS_DOES_NOT_EXIST));
+ return null;
+ }
+
+ if(!Storable.checkLegalName(username))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME));
+ return null;
+ }
+
+ File userDirectory = getDirectory(workspace.getLocation(), username);
+
+ if(!userDirectory.exists())
+ {
+ userDirectory.mkdirs();
+ }
+
+ // userDirectory exists
+
+ //File userFile = getFile(userDirectory, UserConfig.class);
+ UserConfig userConfig = new UserConfig();
+ try {
+ userConfig.setLocation(userDirectory);
+ userConfig.store();
+ } catch (IOException e) {
+ // Best effort. Continue without saving to disk.
+ System.out.print("Could not create UserConfig file: " + e.toString());
+ }
+
+ return userConfig;
+ }
+
+ public String getUserName()
+ {
+ if (isVirtual())
+ return VIRTUAL_USER;
+ String result = null;
+ if(getLocation() != null)
+ result = getLocation().getName();
+ return result;
+ }
+
+ /*
+ * File List
+ */
+
+ /**
+ * @return ../WorkspaceDir/UserDir/src/ or null if {@link #isVirtual()}
+ */
+ public File getSourceDirectory()
+ {
+ if (isVirtual())
+ return null;
+ return new File(getLocation().toString() + File.separator + SRC_DIR_NAME);
+ }
+
+ /**
+ * @param fileName - without extension
+ * @return
+ */
+ public File getLogoFilePath(String fileName)
+ {
+ if(isVirtual())
+ return null;
+
+ String path = getSourceDirectory().toString()
+ + File.separator + fileName
+ + GlobalConfig.LOGO_FILE_EXTENSION;
+
+ return new File(path);
+ }
+
+ /**
+ * @return ../WorkspaceDir/UserDir/backups/ or null if {@link #isVirtual()}
+ */
+ public File getBackupDirectory()
+ {
+ if (isVirtual())
+ return null;
+ return new File(getLocation().toString() + File.separator + BACKUPS_DIR_NAME);
+ }
+
+ /**
+ * @param fileName - without extension
+ * @return
+ */
+ public File getFileBackupDir(String fileName)
+ {
+ if (isVirtual())
+ return null;
+ String path = getBackupDirectory().toString()
+ + File.separator + fileName;
+ return new File(path);
+ }
+
+ public File getContestDir()
+ {
+ if (isVirtual())
+ return null;
+ return new File(getLocation().toString() + File.separator + CONTEST_DIR_NAME);
+ }
+
+ public File getContestFilePath(String fileName)
+ {
+ if (isVirtual())
+ return null;
+ String path = getContestDir().toString()
+ + File.separator + SRC_DIR_NAME
+ + File.separator + fileName
+ + GlobalConfig.LOGO_FILE_EXTENSION;
+ return new File(path);
+ }
+
+ public File getContestFileDir(String fileName)
+ {
+ if (isVirtual())
+ return null;
+ String path = getContestDir().toString()
+ + File.separator + fileName;
+ return new File(path);
+ }
+
+ /**
+ * @param fileName
+ * @return File descriptor for a backup file with the current timestamp
+ */
+ public File getBackupFilePath(String fileName)
+ {
+ String path = getFileBackupDir(fileName)
+ + File.separator + getTimeStamp()
+ + GlobalConfig.LOGO_FILE_EXTENSION;
+
+ return new File(path);
+ }
+
+ /**
+ * @param fileName
+ * @return File descriptor for a contest/record file with the current timestamp
+ */
+ public File getRecordFilePath(String fileName)
+ {
+ String path = getContestFileDir(fileName)
+ + File.separator + getTimeStamp()
+ + GlobalConfig.LOGO_FILE_EXTENSION;
+
+ return new File(path);
+ }
+
+ public File getCommandLineContestFile()
+ {
+ if (isVirtual())
+ return null;
+ String path = getContestDir().toString()
+ + File.separator + COMMAND_LINE_RECORD_NAME
+ + GlobalConfig.LOGO_FILE_EXTENSION;
+ return new File(path);
+ }
+
+ public static final String dateTimeFormat = "yyyy-MM-dd-HH-mm-ss";
+ public static final String timeFormat = "HH:mm:ss";
+ public static final String minSecFormat = "mm:ss";
+
+ public static String getTimeStamp()
+ {
+ SimpleDateFormat sdf = new SimpleDateFormat();
+ sdf.applyPattern(dateTimeFormat);
+ Calendar cal = Calendar.getInstance();
+ return sdf.format(cal.getTime());
+ }
+
+ /**
+ * @param millis
+ * @return yyyy-MM-dd-HH-mm-ss
+ */
+ public static String getDateTimeString(long millis)
+ {
+ SimpleDateFormat sdf = new SimpleDateFormat();
+ sdf.applyPattern(dateTimeFormat);
+ return sdf.format(new Date(millis));
+ }
+
+ /**
+ * @param millis
+ * @return HH:mm:ss
+ */
+ public static String getTimeString(long millis)
+ {
+ SimpleDateFormat sdf = new SimpleDateFormat();
+ sdf.applyPattern(timeFormat);
+ return sdf.format(new Date(millis));
+ }
+
+ public static String getMinSec(long millis)
+ {
+ SimpleDateFormat sdf = new SimpleDateFormat();
+ sdf.applyPattern(minSecFormat);
+ return sdf.format(new Date(millis));
+ }
+
+ /**
+ * The list of files UserSpace in the workspace.
+ */
+ private ArrayList<String> fileOrder = new ArrayList<String>();
+
+ public void addFile(String fileName)
+ {
+ if (!fileOrder.contains(fileName))
+ fileOrder.add(fileName);
+ }
+
+ public void removeFile(String fileName)
+ {
+ fileOrder.remove(fileName);
+ }
+
+ public void renameFile(String oldName, String newName)
+ {
+ int i = fileOrder.indexOf(oldName);
+ if (i < 0)
+ return;
+ fileOrder.remove(i);
+ fileOrder.add(i, newName);
+ }
+
+ public ArrayList<String> getFileOrder()
+ {
+ return fileOrder;
+ }
+
+ // The below properties are essentially copied from the old XLogo
+ // they might get (re)moved or changed
+
+ final String version="0.9.96pre 27/06/12"; // TODO not needed? new version?
+
+ /**
+ * Drawing Quality
+ */
+ private DrawQuality quality= DrawQuality.HIGH;
+
+ /**
+ * This integer represents the selected looknfeel for the appplication
+ */
+ private LookAndFeel looknfeel=LookAndFeel.JAVA;
+
+ /**
+ * This integer represents the drawing area width
+ */
+ private int imageWidth= Math.max(1000, Toolkit.getDefaultToolkit().getScreenSize().width);
+ /**
+ * This integer represents the drawing area height
+ */
+ private int imageHeight= Math.max(1000, Toolkit.getDefaultToolkit().getScreenSize().height);
+ /**
+ * Integer that represents the active turtle's shape
+ */
+ private int activeTurtle=0;
+ /**
+ * Maximum allowed pen size
+ */
+ private int maxPenWidth=-1;
+ /**
+ * This boolean indicates if the drawing area has to be cleaned when the editor is left.
+ */
+ private boolean eraseImage = false;
+ /**
+ * This boolean indicates if variables are deleted when closing the editor.
+ */
+ private boolean clearVariables = false;
+ /**
+ * Max value for the turtles number
+ */
+ private int maxTurtles = 16;
+ /**
+ * Default screen color: This color is used when the primitive "clearscreen" is used.
+ */
+ private Color screencolor=Color.WHITE;
+ /**
+ * Default pen color: This color is used when the primitive "clearscreen" is used.
+ */
+ private Color pencolor=Color.BLACK;
+
+
+ /**
+ * This represents the pen shape
+ */
+ private PenShape penShape = PenShape.SQUARE;
+
+ /**
+ * This integer represents the turtle's speed for drawing <br>
+ * Slow: 100
+ * Fast: 0
+ */
+ private int turtleSpeed=0;
+ /**
+ * This String contains the command to execute on Startup. <br>
+ * Configured in the dialog box "startup files"
+ */
+ private String a_executer="";
+ /** Marko Zivkovic : this is used for a few Logo Commands that operate with files <br>
+ * Loic:
+ * The default folder for the user when the application starts.<br>
+ * This folder corresponds to the last opened or saved file in format lgo // Marko : not true anymore
+ */
+ private String defaultFolder=Utils.rajoute_backslash(System.getProperty("user.home"));
+
+ /**
+ * This boolean indicates if the grid is enabled
+ */
+ private boolean drawGrid=false;
+ /**
+ * This integer represents the X distance for the grid
+ */
+ private int XGrid=20;
+ /**
+ * This integer represents the Y distance for the grid
+ */
+ private int YGrid=20;
+ /**
+ * This integer represents the grid Color
+ */
+ private int gridColor=Color.DARK_GRAY.getRGB();
+ /**
+ * This boolean indicates if the X axis is enabled
+ */
+ private boolean drawXAxis=false;
+ /**
+ * This boolean indicates if the Y axis is enabled
+ */
+ private boolean drawYAxis=false;
+ /**
+ * This integer represents the axis Color
+ */
+ private int axisColor=new Color(255,0,102).getRGB();
+ /**
+ * This integer represents the X distance between two divisions on the X Axis
+ */
+ private int XAxis=30;
+ /**
+ * This integer represents the X distance between two divisions on the Y Axis
+ */
+ private int YAxis=30;
+
+ /**
+ * This long represents the hour of XLogo starting
+ */
+ private long heure_demarrage;
+ /**
+ * Color for the border around drawing area
+ */
+ private Color borderColor=null;
+ /**
+ * The Image is for the border around drawing area
+ */
+ private String borderImageSelected="background.png";
+
+ /**
+ * This Vector contains all images added by the user for image Border
+ */
+ private ArrayList<String> borderExternalImage=new ArrayList<String>();
+ /**
+ * The default image defined by default that are included in XLogo
+ */
+ private String[] borderInternalImage={"background.png"};
+ /**
+ * This String represents the main command accessible with the button play in the toolbar
+ */
+ private String mainCommand="";
+ /**
+ * This boolean indicates if Xlogo must launch the main Command on XLogo startup
+ * It overrides the String a_executer
+ */
+ private boolean autoLaunch=false;
+
+
+ /**
+ * TCP Port for robotics and network flows
+ */
+ private int tcpPort = 1948;
+
+ public int searchInternalImage(String st) {
+ for (int i = 0; i < borderInternalImage.length; i++) {
+ if (st.equals(borderInternalImage[i]))
+ return i;
+ }
+ return -1;
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Generated Getters & Setters
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ public DrawQuality getQuality() {
+ return quality;
+ }
+
+
+ public void setQuality(DrawQuality quality) {
+ this.quality = quality;
+ makeDirty();
+ }
+
+ public LookAndFeel getLooknfeel() {
+ return looknfeel;
+ }
+
+ public void setLooknfeel(LookAndFeel looknfeel) {
+ this.looknfeel = looknfeel;
+ makeDirty();
+ }
+
+
+ public int getImageWidth() {
+ return imageWidth;
+ }
+
+
+ public void setImageWidth(int imageWidth) {
+ this.imageWidth = imageWidth;
+ makeDirty();
+ }
+
+
+ public int getImageHeight() {
+ return imageHeight;
+ }
+
+
+ public void setImageHeight(int imageHeight) {
+ this.imageHeight = imageHeight;
+ makeDirty();
+ }
+
+ public int getActiveTurtle() {
+ return activeTurtle;
+ }
+
+
+ public void setActiveTurtle(int activeTurtle) {
+ this.activeTurtle = activeTurtle;
+ makeDirty();
+ }
+
+
+ public int getMaxPenWidth() {
+ return maxPenWidth;
+ }
+
+
+ public void setMaxPenWidth(int maxPenWidth) {
+ this.maxPenWidth = maxPenWidth;
+ makeDirty();
+ }
+
+
+ public boolean isEraseImage() {
+ return eraseImage;
+ }
+
+
+ public void setEraseImage(boolean eraseImage) {
+ this.eraseImage = eraseImage;
+ makeDirty();
+ }
+
+
+ public boolean isClearVariables() {
+ return clearVariables;
+ }
+
+
+ public void setClearVariables(boolean clearVariables) {
+ this.clearVariables = clearVariables;
+ makeDirty();
+ }
+
+
+ public int getMaxTurtles() {
+ return maxTurtles;
+ }
+
+
+ public void setMaxTurtles(int maxTurtles) {
+ this.maxTurtles = maxTurtles;
+ makeDirty();
+ }
+
+
+ public Color getScreencolor() {
+ return screencolor;
+ }
+
+
+ public void setScreencolor(Color screencolor) {
+ this.screencolor = screencolor;
+ makeDirty();
+ }
+
+
+ public Color getPencolor() {
+ return pencolor;
+ }
+
+
+ public void setPencolor(Color pencolor) {
+ this.pencolor = pencolor;
+ makeDirty();
+ }
+
+
+ public PenShape getPenShape() {
+ return penShape;
+ }
+
+
+ public void setPenShape(PenShape penShape) {
+ this.penShape = penShape;
+ makeDirty();
+ }
+
+
+ public int getTurtleSpeed() {
+ return turtleSpeed;
+ }
+
+
+ public void setTurtleSpeed(int turtleSpeed) {
+ this.turtleSpeed = turtleSpeed;
+ makeDirty();
+ }
+
+
+ public String getA_executer() {
+ return a_executer;
+ }
+
+
+ public void setA_executer(String a_executer) {
+ this.a_executer = a_executer;
+ makeDirty();
+ }
+
+ /**
+ * Default : User source directory
+ * @return the current defaultDirectory
+ * @author Marko Zivkovic
+ */
+ public String getDefaultFolder() {
+ if (defaultFolder == null)
+ return getSourceDirectory().toString();
+ return defaultFolder;
+ }
+
+
+ public void setDefaultFolder(String defaultFolder) {
+ this.defaultFolder = defaultFolder;
+ makeDirty();
+ }
+
+
+
+
+ public boolean isDrawGrid() {
+ return drawGrid;
+ }
+
+
+ public void setDrawGrid(boolean drawGrid) {
+ this.drawGrid = drawGrid;
+ makeDirty();
+ }
+
+
+ public int getXGrid() {
+ return XGrid;
+ }
+
+
+ public void setXGrid(int xGrid) {
+ XGrid = xGrid;
+ makeDirty();
+ }
+
+
+ public int getYGrid() {
+ return YGrid;
+ }
+
+
+ public void setYGrid(int yGrid) {
+ YGrid = yGrid;
+ makeDirty();
+ }
+
+
+ public int getGridColor() {
+ return gridColor;
+ }
+
+
+ public void setGridColor(int gridColor) {
+ this.gridColor = gridColor;
+ makeDirty();
+ }
+
+
+ public boolean isDrawXAxis() {
+ return drawXAxis;
+ }
+
+
+ public void setDrawXAxis(boolean drawXAxis) {
+ this.drawXAxis = drawXAxis;
+ makeDirty();
+ }
+
+
+ public boolean isDrawYAxis() {
+ return drawYAxis;
+ }
+
+
+ public void setDrawYAxis(boolean drawYAxis) {
+ this.drawYAxis = drawYAxis;
+ makeDirty();
+ }
+
+
+ public int getAxisColor() {
+ return axisColor;
+ }
+
+
+ public void setAxisColor(int axisColor) {
+ this.axisColor = axisColor;
+ makeDirty();
+ }
+
+
+ public int getXAxis() {
+ return XAxis;
+ }
+
+
+ public void setXAxis(int xAxis) {
+ XAxis = xAxis;
+ makeDirty();
+ }
+
+
+ public int getYAxis() {
+ return YAxis;
+ }
+
+
+ public void setYAxis(int yAxis) {
+ YAxis = yAxis;
+ makeDirty();
+ }
+
+
+ public long getHeure_demarrage() {
+ return heure_demarrage;
+ }
+
+
+ public void setHeure_demarrage(long heure_demarrage) {
+ this.heure_demarrage = heure_demarrage;
+ makeDirty();
+ }
+
+
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ makeDirty();
+ }
+
+
+ public String getBorderImageSelected() {
+ return borderImageSelected;
+ }
+
+
+ public void setBorderImageSelected(String borderImageSelected) {
+ this.borderImageSelected = borderImageSelected;
+ makeDirty();
+ }
+
+
+ public ArrayList<String> getBorderExternalImage() {
+ return borderExternalImage;
+ }
+
+
+ public void setBorderExternalImage(ArrayList<String> borderExternalImage) {
+ this.borderExternalImage = borderExternalImage;
+ makeDirty();
+ }
+
+
+ public String[] getBorderInternalImage() {
+ return borderInternalImage;
+ }
+
+
+ public void setBorderInternalImage(String[] borderInternalImage) {
+ this.borderInternalImage = borderInternalImage;
+ makeDirty();
+ }
+
+
+ public String getMainCommand() {
+ return mainCommand;
+ }
+
+
+ public void setMainCommand(String mainCommand) {
+ this.mainCommand = mainCommand;
+ makeDirty();
+ }
+
+
+ public boolean isAutoLaunch() {
+ return autoLaunch;
+ }
+
+
+ public void setAutoLaunch(boolean autoLaunch) {
+ this.autoLaunch = autoLaunch;
+ makeDirty();
+ }
+
+ public int getTcpPort() {
+ return tcpPort;
+ }
+
+
+ public void setTcpPort(int tcpPort) {
+ this.tcpPort = tcpPort;
+ makeDirty();
+ }
+
+}
diff --git a/logo/src/xlogo/storage/workspace/ContestConfig.java b/logo/src/xlogo/storage/workspace/ContestConfig.java
new file mode 100644
index 0000000..197e4d5
--- /dev/null
+++ b/logo/src/xlogo/storage/workspace/ContestConfig.java
@@ -0,0 +1,59 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.workspace;
+
+import java.io.Serializable;
+
+public class ContestConfig implements Serializable {
+
+ private static final long serialVersionUID = 3323633026648762889L;
+
+ private int nOfContestFiles = 6;
+ private int nOfContestBonusFiles = 2;
+
+ public int getNOfContestFiles()
+ {
+ return nOfContestFiles;
+ }
+
+ public void setNOfContestFiles(int nOfContestFiles)
+ {
+ this.nOfContestFiles = nOfContestFiles;
+ }
+
+ public int getNOfContestBonusFiles()
+ {
+ return nOfContestBonusFiles;
+ }
+
+ public void setNOfContestBonusFiles(int nOfContestBonusFiles)
+ {
+ this.nOfContestBonusFiles = nOfContestBonusFiles;
+ }
+
+}
diff --git a/logo/src/xlogo/storage/workspace/Language.java b/logo/src/xlogo/storage/workspace/Language.java
new file mode 100644
index 0000000..f19b3ac
--- /dev/null
+++ b/logo/src/xlogo/storage/workspace/Language.java
@@ -0,0 +1,111 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.workspace;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+public enum Language {
+ LANGUAGE_FRENCH(0, "French", "fr", "FR"),
+ LANGUAGE_ENGLISH(1, "English", "en", "US"),
+ LANGUAGE_ARABIC(2, "Arabic", "ar", "MA"),
+ LANGUAGE_SPANISH(3, "Spanish", "es", "ES"),
+ LANGUAGE_PORTUGAL(4, "Portuguese", "pt", "BR"),
+ LANGUAGE_ESPERANTO(5, "Esperanto", "eo", "EO"),
+ LANGUAGE_GERMAN(6, "German", "de", "DE"),
+ LANGUAGE_GALICIAN(7, "Galician", "gl", "ES"),
+ LANGUAGE_ASTURIAN(8, "Asturian", "al", "ES"),
+ LANGUAGE_GREEK(9, "Greek", "el", "GR"),
+ LANGUAGE_ITALIAN(10, "Italian", "it", "IT"),
+ LANGUAGE_CATALAN(11, "Catalan", "ca", "ES"),
+ LANGUAGE_HUNGARIAN(12, "Hungarian", "hu", "HU"),
+ LANGUAGE_ENGLISH_GERMAN(13, "ABZ German/English", "en", "DE");
+
+ private int value;
+ private String englishName;
+ private String languageCode;
+ private String countryCode;
+
+ private static Map<Integer, Language> valueToLanguage;
+
+ private Language(int value, String englishName, String languageCode, String countryCode) {
+ this.value = value;
+ this.englishName = englishName;
+ this.languageCode = languageCode;
+ this.countryCode = countryCode;
+ }
+
+ public static Language getLanguage(int i)
+ {
+ if (valueToLanguage == null)
+ initMapping();
+ return valueToLanguage.get(i);
+ }
+
+ public static void initMapping()
+ {
+ valueToLanguage = new HashMap<Integer, Language>();
+ for (Language lang : values()) {
+ valueToLanguage.put(lang.value, lang);
+ }
+ }
+
+ public int getValue()
+ {
+ return value;
+ }
+
+ public String getEnglishName()
+ {
+ return englishName;
+ }
+
+ /**
+ * return English name
+ */
+ @Override
+ public String toString() {
+ return getEnglishName();
+ }
+
+ public Locale getLocale()
+ {
+ return new Locale(languageCode, countryCode);
+ }
+
+ public String getLanguageCode()
+ {
+ return languageCode;
+ }
+
+ public String getCountryCode()
+ {
+ return countryCode;
+ }
+}
diff --git a/logo/src/xlogo/storage/workspace/NumberOfBackups.java b/logo/src/xlogo/storage/workspace/NumberOfBackups.java
new file mode 100644
index 0000000..1c8d4b2
--- /dev/null
+++ b/logo/src/xlogo/storage/workspace/NumberOfBackups.java
@@ -0,0 +1,57 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.workspace;
+
+public enum NumberOfBackups {
+ NO_BACKUPS(0),
+ ONE(1),
+ THREE(3),
+ TEN(10),
+ FIFTY(50),
+ INFINITE(-1);
+
+ private int number;
+
+ private NumberOfBackups(int number) {
+ this.number = number;
+ }
+
+ public int getNumber()
+ {
+ return number;
+ }
+
+ public String toString()
+ {
+ if (number == -1)
+ {
+ return "infinite";
+ }
+ return Integer.toString(number);
+ }
+}
diff --git a/logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java b/logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java
new file mode 100644
index 0000000..ac89e64
--- /dev/null
+++ b/logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java
@@ -0,0 +1,151 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.workspace;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.io.Serializable;
+
+public class SyntaxHighlightConfig implements Serializable{
+
+ private static final long serialVersionUID = 7137977560063481685L;
+
+ /**
+ * syntax Highlighting: Color for primitives
+ */
+ private int primitiveColor=new Color(0,128,0).getRGB();
+ /**
+ * syntax Highlighting: Style for primitives
+ */
+ private int primitiveStyle=Font.PLAIN;
+ /**
+ * syntax Highlighting: Color for operands: numbers....
+ */
+ private int operatorColor=Color.BLUE.getRGB();
+ /**
+ * syntax Highlighting: Style for operands
+ */
+ private int operatorStyle=Font.PLAIN;
+ /**
+ * syntax Highlighting: Color for comments
+ */
+ private int commentColor=Color.GRAY.getRGB();
+ /**
+ * syntax Highlighting: Style for comments
+ */
+ private int commentStyle=Font.PLAIN;
+ /**
+ * syntax Highlighting: Color for parenthesis
+ */
+ private int braceColor=Color.RED.getRGB();
+ /**
+ * syntax Highlighting: Style for parenthesis
+ */
+ private int braceStyle=Font.BOLD;
+ /**
+ * boolean that indicates if syntax Highlighting is enabled
+ */
+ private boolean colorEnabled=true;
+
+ public SyntaxHighlightConfig() {
+
+ }
+
+ public int getPrimitiveColor() {
+ return primitiveColor;
+ }
+
+ public void setPrimitiveColor(int primitiveColor) {
+ this.primitiveColor = primitiveColor;
+ }
+
+ public int getPrimitiveStyle() {
+ return primitiveStyle;
+ }
+
+ public void setPrimitiveStyle(int primitiveStyle) {
+ this.primitiveStyle = primitiveStyle;
+ }
+
+ public int getOperatorColor() {
+ return operatorColor;
+ }
+
+ public void setOperatorColor(int operatorColor) {
+ this.operatorColor = operatorColor;
+ }
+
+ public int getOperatorStyle() {
+ return operatorStyle;
+ }
+
+ public void setOperatorStyle(int operatorStyle) {
+ this.operatorStyle = operatorStyle;
+ }
+
+ public int getCommentColor() {
+ return commentColor;
+ }
+
+ public void setCommentColor(int commentColor) {
+ this.commentColor = commentColor;
+ }
+
+ public int getCommentStyle() {
+ return commentStyle;
+ }
+
+ public void setCommentStyle(int commentStyle) {
+ this.commentStyle = commentStyle;
+ }
+
+ public int getBraceColor() {
+ return braceColor;
+ }
+
+ public void setBraceColor(int braceColor) {
+ this.braceColor = braceColor;
+ }
+
+ public int getBraceStyle() {
+ return braceStyle;
+ }
+
+ public void setBraceStyle(int braceStyle) {
+ this.braceStyle = braceStyle;
+ }
+
+ public boolean isColorEnabled() {
+ return colorEnabled;
+ }
+
+ public void setColorEnabled(boolean colorEnabled) {
+ this.colorEnabled = colorEnabled;
+ }
+
+}
diff --git a/logo/src/xlogo/storage/workspace/WorkspaceConfig.java b/logo/src/xlogo/storage/workspace/WorkspaceConfig.java
new file mode 100644
index 0000000..9a3b60a
--- /dev/null
+++ b/logo/src/xlogo/storage/workspace/WorkspaceConfig.java
@@ -0,0 +1,688 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.storage.workspace;
+
+import java.awt.Font;
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import xlogo.AppSettings;
+import xlogo.Logo;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.StorableObject;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.UserConfig;
+
+/**
+ * WorkspaceConfig maintains a workspace (i.e. a "class room") that consists of several projects or "Users".
+ * It defines a common language for the project (English, French, ...) and a master password for the workspace owner.
+ * The password is intended to allow certain settings only for the workspace owner. Therefore the corresponding methods will require a password.
+ * <br> A directory on the file system is an XLogo4Schools workspace, if it contains a file named "X4S_WorkspaceConfig.ser",
+ * no matter what it contains.
+ * <br> If a workspace is made virtual, it is not stored on the file system. ({@link #createVirtualWorkspace()})
+ * <p>
+ * A user belongs to the workspace, if it is contained logically in the user list.
+ * For a user to work correctly in its user space, a user directory {@code workspaceDir/username/}
+ * and the file {@code workspaceDir/username/X4S_UserConfig.ser} are required.
+ * As long as a user exists logically in the workspace, missing or corrupted files are recreated if needed.
+ * If a user is deleted, it is deleted only logically, so it can be reintegrated later again.
+ * @author Marko Zivkovic
+ */
+public class WorkspaceConfig extends StorableObject implements Serializable {
+
+ private static final long serialVersionUID = -3554871695113998509L;
+
+ /**
+ * Name of the virtual workspace
+ */
+ public static final String VIRTUAL_WORKSPACE = "Guest Workspace (no automatic save)";
+
+ protected WorkspaceConfig()
+ {
+ super();
+ userList = new ArrayList<String>();
+ language = Language.LANGUAGE_ENGLISH;
+ font = new Font("dialog",Font.PLAIN,14); // TODO on access check if it is null.
+ // TODO what if incompatible?
+ syntaxHighlightingStyles = new SyntaxHighlightConfig();
+ AppSettings.getInstance().setFont(font);
+ }
+
+ /**
+ * @return
+ * @throws IllegalStateException if this is not virtual and {@link #getLocation()} returns null
+ */
+ public String getWorkspaceName() throws IllegalStateException
+ {
+ if (isVirtual())
+ return VIRTUAL_WORKSPACE;
+
+ File wsDir = getLocation();
+ if (wsDir == null)
+ throw new IllegalStateException("Name is not available because location is null.");
+ return wsDir.getName();
+ }
+
+ /*
+ * Static constructors
+ */
+ private static WorkspaceConfig virtualWS;
+
+ /**
+ * A virtual user can enter the application in a virtual workspace without having an actual user account on the file system. Hence nothing will be stored.
+ * A regular user (not virtual) will have his own folder in a regular workspace on the file system and all his preferences and files are stored there.
+ * To create a regular workspace, use {@link #createNewWorkspace(File, String)},
+ * to load a regular workspace from the file system, user {@link #loadWorkspace(File)}}.
+ * @see #isVirtual()
+ * @return a virtual workspace
+ */
+ public static WorkspaceConfig createVirtualWorkspace()
+ {
+ if (virtualWS == null)
+ {
+ virtualWS = new WorkspaceConfig();
+ virtualWS.makeVirtual();
+ }
+ return virtualWS;
+ }
+
+ @Override
+ protected void makeVirtual()
+ {
+ super.makeVirtual();
+ userList = new ArrayList<String>();
+ userList.add(UserConfig.VIRTUAL_USER);
+ lastActiveUser = UserConfig.VIRTUAL_USER;
+ try { enterUserSpace(UserConfig.VIRTUAL_USER); } catch (IOException e) { /* Does not happen */ }
+ }
+
+ /**
+ * @param dir
+ * @param workspaceName
+ * @return
+ * @throws IOException
+ */
+ public static WorkspaceConfig createNewWorkspace(File dir, String workspaceName) throws IOException
+ {
+ if (!Storable.checkLegalName(workspaceName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME));
+ return null;
+ }
+
+ File wsd = getDirectory(dir, workspaceName);
+ boolean existed = wsd.exists();
+ if (!existed)
+ {
+ wsd.mkdirs();
+ }
+ // wsd exists and it is empty
+ WorkspaceConfig wsc = new WorkspaceConfig();
+ wsc.setLocation(wsd);
+ //if(existed && WSManager.isUserDirectory(wsd))
+ // wsc.refreshUserList();
+ wsc.store();
+ return wsc;
+ }
+
+ /**
+ * @see #loadWorkspace(File)
+ * @param dir - location of the workspace
+ * @param workspaceName
+ * @return
+ * @throws IOException
+ */
+ public static WorkspaceConfig loadWorkspace(File dir, String workspaceName) throws IOException
+ {
+ File wsc = getDirectory(dir, workspaceName);
+ return loadWorkspace(wsc);
+ }
+
+ /**
+ * @param workspaceDir
+ * @return Load an existing workspace from the file system.
+ * If workspaceDir specifies a {@link WorkspaceConfig#VIRTUAL_WORKSPACE}, the virtual workspace is returned instead.
+ * @throws IOException
+ */
+ public static WorkspaceConfig loadWorkspace(File workspaceDir) throws IOException
+ {
+ if(workspaceDir.getName().equals(WorkspaceConfig.VIRTUAL_WORKSPACE))
+ {
+ return createVirtualWorkspace();
+ }
+
+ File wsf = getFile(workspaceDir, WorkspaceConfig.class);
+
+ WorkspaceConfig wsc;
+ try {
+ wsc = (WorkspaceConfig) WorkspaceConfig.loadObject(wsf);
+ wsc.setLocation(workspaceDir);
+ return wsc;
+ } catch (ClassNotFoundException e) {
+ return null; // this won't happen
+ }
+ }
+
+ /*
+ * User list
+ */
+
+ /**
+ * @see #getUserList()
+ */
+ private ArrayList<String> userList;
+
+ /**
+ * The names of the logical users in the workspace
+ * @return
+ */
+ public String[] getUserList()
+ {
+ String[] users = new String[userList.size()];
+ return userList.toArray(users);
+ }
+
+ public File getUserDirectroy(String username)
+ {
+ if (!existsUserLogically(username) || isVirtual())
+ return null;
+
+ return getDirectory(getLocation(), username);
+ }
+
+ /**
+ * Create new user directory and UserConfig file in this workspace on the file system, and add it logically to the userList.
+ * If either existed, only the non-existing parts are created.
+ * To create a user in a virtual workspace will have no effect, but an error message is printed.
+ * <p> Has no effect if this is virtual.
+ * @param username
+ */
+ public void createUser(String username)
+ {
+ if (!Storable.checkLegalName(username))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME));
+ return;
+ }
+
+ if(isVirtual())
+ {
+ DialogMessenger.getInstance().dispatchError("Workspace Error", "Attempt to create new user to virtual workspace.");
+ return;
+ }
+
+ File userDir = getDirectory(getLocation(), username);
+
+ if(!userDir.mkdirs())
+ {
+ DialogMessenger.getInstance().dispatchError("Workspace Error","Could not make required directories: " + userDir.toString());
+ return;
+ }
+
+ if (!existsUserLogically(username))
+ {
+ userList.add(username);
+ makeDirty();
+ }
+
+ // Make new user logically existent in workspace config file
+ try { store(); } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchError("Workspace Error","Could not store workspace.");
+ }
+ if(!existsUserPhysically(username))
+ UserConfig.createNewUser(this, username);
+ }
+
+ /**
+ * Import a user directory from anywhere in the file system to this workspace.
+ * All files in the user directory are copied. Already existing files might get overwritten.
+ * <p> This has no effect if this is virtual.
+ * @param srcUserDir - a legal user directory anywhere on the file system
+ * @param destUsername - Existing files of targetUser are overwritten. If targetUser does not exist, it will be created first.
+ * @throws IllegalArgumentException
+ * @throws IOException
+ * @see WSManager#isUserDirectory(File)
+ */
+ public void importUser(File srcUserDir, String destUsername) throws IllegalArgumentException, IOException
+ {
+ if (isVirtual())
+ return;
+
+ if (!WSManager.isUserDirectory(srcUserDir))
+ throw new IllegalArgumentException();
+
+ createUser(destUsername);
+ File targetUserDir = getDirectory(getLocation(), destUsername);
+
+ WSManager.copyFullyRecursive(srcUserDir, targetUserDir);
+ }
+
+ /**
+ * @param userName will be removed logically only
+ */
+ public void removeUser(String userName)
+ {
+ if(existsUserLogically(userName))
+ makeDirty();
+
+ userList.remove(userName);
+ }
+
+ /**
+ * @param username - if this exists not logically in the workspace, null is returned.
+ * @return a {@link UserConfig} generated from the file system. If this is a virtual workspace, a virtual user is created instead.
+ * @throws IOException if the UserConfig could not be loaded
+ * @see UserConfig#loadUser(File, String)
+ */
+ public UserConfig loadUser(String username) throws IOException
+ {
+ if(!existsUserLogically(username))
+ {
+ DialogMessenger.getInstance().dispatchError("Workspace Error","Attempt to load inexistent user: " + username + ". Try to import this user.");
+ return null;
+ }
+
+ if(isVirtual())
+ return UserConfig.createVirtualUser();
+
+ // exists logically and is not virtual
+
+ if(!existsUserPhysically(username))
+ {
+ // but it does exist logically => it must have been corrupted externally.
+ // => restore it.
+ if(!getLocation().mkdirs())
+ {
+ DialogMessenger.getInstance().dispatchError("Workspace Error","Could not make required directories.");
+ return null;
+ }
+ // user creation requires existence of the workspace on file system
+ try { store(); } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchError("Workspace Error","Could not store workspace.");
+ }
+ return UserConfig.createNewUser(this, username);
+ }
+ // exists physically
+ return UserConfig.loadUser(this, username);
+ }
+
+ /**
+ * Produces a list of user names by reading the contents of the current workspace directory.
+ * The users in this list may contain users that have been deleted logically before.
+ * @return
+ */
+ public ArrayList<String> getPhysicalUserList()
+ {
+ if(isVirtual())
+ return new ArrayList<String>();
+
+ ArrayList<String> users = new ArrayList<String>();
+
+ if (WSManager.isWorkspaceDirectory(getLocation()))
+ {
+ DialogMessenger.getInstance().dispatchError("Workspace Error", "Current workspace was probably deleted. I will recreate it.");
+ try { store(); } catch (IOException e) {
+ DialogMessenger.getInstance().dispatchError("Workspace Error", "I could not recreate the Workspace. Try to delete the Workspace and recreate it manually.");
+ return users;
+ }
+ }
+
+
+ for(File dir : WSManager.listDirectories(getLocation()))
+ {
+ if(WSManager.isUserDirectory(dir))
+ {
+ users.add(dir.getName());
+ }
+ }
+ return users;
+ }
+
+ /**
+ * A user exists logically, if its name is known by the workspace.
+ * @param userName
+ * @return
+ */
+ public boolean existsUserLogically(String username)
+ {
+ return userList.contains(username);
+ }
+
+ /**
+ * A user exists physically, if a folder with the user's name exists in this workspace and if it contains a UserConfig file.
+ * @param username
+ * @return
+ * @see WSManager#isUserDirectory(File)
+ */
+ public boolean existsUserPhysically(String username)
+ {
+ File userDir = getDirectory(getLocation(), username);
+ return WSManager.isUserDirectory(userDir);
+ }
+
+ /*
+ * last active user
+ */
+
+ private String lastActiveUser;
+
+ /**
+ * @return name of the last active user
+ */
+ public String getLastActiveUser()
+ {
+ return lastActiveUser;
+ }
+
+ /**
+ * Succeeds if the user exists
+ * @param workspace
+ */
+ public void setLastActiveUser(String username)
+ {
+ if(existsUserLogically(username) && !username.equals(lastActiveUser))
+ {
+ lastActiveUser = new String(username);
+ makeDirty();
+ }
+ }
+
+ /*
+ * active user
+ */
+
+ private transient UserConfig activeUser;
+
+ public UserConfig getActiveUser()
+ {
+ return activeUser;
+ }
+
+ /**
+ * @throws IOException If the old userConfig could not be stored.
+ */
+ public void enterUserSpace(String username) throws IOException
+ {
+ if(activeUser != null)
+ {
+ leaveUserSpace();
+ }
+
+ if (isVirtual())
+ activeUser = UserConfig.createVirtualUser();
+ else
+ activeUser = UserConfig.loadUser(this, username);
+
+ setLastActiveUser(username);
+ }
+
+ /**
+ * @throws IOException If userConfig could not be stored.
+ */
+ public void leaveUserSpace() throws IOException
+ {
+ if (activeUser.isDirty())
+ activeUser.store();
+ activeUser = null;
+ }
+
+ /*
+ * Version control
+ */
+
+ /**
+ * How many old versions of a file should be kept, in addition to the most recent one?
+ * Default is infinite.
+ */
+ private NumberOfBackups numberOfBackups = NumberOfBackups.INFINITE;
+
+ /**
+ * @see #numberOfBackups
+ */
+ public NumberOfBackups getNumberOfBackups()
+ {
+ return numberOfBackups;
+ }
+
+ /**
+ * @see #numberOfBackups
+ */
+ public void setNumberOfBackups(NumberOfBackups n)
+ {
+ numberOfBackups = n;
+ makeDirty();
+ }
+
+ /*
+ * Workspace language
+ */
+
+ /**
+ * The language to be used within this workspace
+ */
+ public Language language;
+
+ public void setLanguage(Language language)
+ {
+ this.language = language;
+ AppSettings.getInstance().setLanguage(language);
+ makeDirty();
+ }
+
+ public Language getLanguage()
+ {
+ return language;
+ }
+
+ /*
+ * Allow users (children) to create new user accounts in workspaces?
+ */
+
+ private boolean allowUserCreation = true;
+
+ public void setAllowUserCreation(boolean allowed)
+ {
+ this.allowUserCreation = allowed;
+ makeDirty();
+ }
+
+ public boolean isUserCreationAllowed()
+ {
+ return allowUserCreation && !isVirtual();
+ }
+
+ /*
+ * Contest //TODO create options in workspace settings
+ */
+
+ private ContestConfig contestConfig;
+
+ protected ContestConfig getContestSettings()
+ {
+ return contestConfig;
+ }
+
+ public int getNOfContestFiles()
+ {
+ if (contestConfig == null)
+ contestConfig = new ContestConfig();
+ return getContestSettings().getNOfContestFiles();
+ }
+
+ public void setNOfContestFiles(int nOfContestFiles)
+ {
+ getContestSettings().setNOfContestFiles(nOfContestFiles);
+ }
+
+ public int getNOfContestBonusFiles()
+ {
+ return getContestSettings().getNOfContestBonusFiles();
+ }
+
+ public void setNOfContestBonusFiles(int nOfContestBonusFiles)
+ {
+ getContestSettings().setNOfContestBonusFiles(nOfContestBonusFiles);
+ }
+
+ /*
+ * Syntax Highlighting
+ */
+ private SyntaxHighlightConfig syntaxHighlightingStyles; // TODO = new SyntaxHighlightStyles();
+
+ /**
+ * This font is the default font for all menus ... in XLogo Application
+ */
+ private Font font;// TODO =new Font("dialog",Font.PLAIN,14);
+
+ public SyntaxHighlightConfig getSyntaxHighlightStyles()
+ {
+ if (syntaxHighlightingStyles == null)
+ {
+ syntaxHighlightingStyles = new SyntaxHighlightConfig();
+ makeDirty();
+ }
+ return syntaxHighlightingStyles;
+ }
+
+ public void setSyntaxHighlightConfig(SyntaxHighlightConfig syntaxHighlightingStyles)
+ {
+ this.syntaxHighlightingStyles = syntaxHighlightingStyles;
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(syntaxHighlightingStyles);
+ }
+
+ public int getPrimitiveColor() {
+ return getSyntaxHighlightStyles().getPrimitiveColor();
+ }
+
+
+ public void setPrimitiveColor(int primitiveColor) {
+ getSyntaxHighlightStyles().setPrimitiveColor(primitiveColor);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public int getPrimitiveStyle() {
+ return getSyntaxHighlightStyles().getPrimitiveStyle();
+ }
+
+ public void setPrimitiveStyle(int primitiveStyle) {
+ getSyntaxHighlightStyles().setPrimitiveStyle(primitiveStyle);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public int getOperatorColor() {
+ return getSyntaxHighlightStyles().getOperatorColor();
+ }
+
+ public void setOperandColor(int operatorColor) {
+ getSyntaxHighlightStyles().setOperatorColor(operatorColor);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public int getOperatorStyle() {
+ return getSyntaxHighlightStyles().getOperatorStyle();
+ }
+
+ public void setOperandStyle(int operatorStyle) {
+ getSyntaxHighlightStyles().setOperatorStyle(operatorStyle);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public int getCommentColor() {
+ return getSyntaxHighlightStyles().getCommentColor();
+ }
+
+ public void setCommentColor(int commentColor) {
+ getSyntaxHighlightStyles().setCommentColor(commentColor);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public int getCommentStyle() {
+ return getSyntaxHighlightStyles().getCommentStyle();
+ }
+
+ public void setCommentStyle(int commentStyle) {
+ getSyntaxHighlightStyles().setCommentStyle(commentStyle);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public int getBraceColor() {
+ return getSyntaxHighlightStyles().getBraceColor();
+ }
+
+ public void setBraceColor(int braceColor) {
+ getSyntaxHighlightStyles().setBraceColor(braceColor);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public int getBraceStyle() {
+ return getSyntaxHighlightStyles().getBraceStyle();
+ }
+
+ public void setBraceStyle(int braceStyle) {
+ getSyntaxHighlightStyles().setBraceStyle(braceStyle);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public boolean isSyntaxHighlightingEnabled() {
+ return getSyntaxHighlightStyles().isColorEnabled();
+ }
+
+ public void setSyntaxHighlightingEnabled(boolean colorEnabled) {
+ getSyntaxHighlightStyles().setColorEnabled(colorEnabled);
+ makeDirty();
+ AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles());
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public void setFont(Font font) {
+ this.font = font;
+ makeDirty();
+ AppSettings.getInstance().setFont(font);
+ }
+
+}
diff --git a/logo/src/xlogo/utils/ExtensionFichier.java b/logo/src/xlogo/utils/ExtensionFichier.java
new file mode 100644
index 0000000..4fcf708
--- /dev/null
+++ b/logo/src/xlogo/utils/ExtensionFichier.java
@@ -0,0 +1,75 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */package xlogo.utils;
+import javax.swing.filechooser.*;
+import java.io.File;
+//Permet de filtrer les fichiers dans un FileChooser
+// You can add a filter in a FileChooser
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+public class ExtensionFichier extends FileFilter {
+/**
+ * @uml.property name="description"
+ */
+private String description; //Description du type de fichiers (Ex: "Fichiers JPEG")
+/**
+ * @uml.property name="extension" multiplicity="(0 -1)" dimension="1"
+ */
+private String[] extension; //Extension (incluant le '.') Ex: .jpg .java
+ public ExtensionFichier() {
+ }
+ public ExtensionFichier(String description,String[] extension){
+ this.description=description;
+ this.extension=extension;
+ }
+ public boolean accept(File f) {
+ if (f.isDirectory()) return true;
+ String nomFichier = f.getPath().toLowerCase();
+ for (int i=0;i<extension.length;i++){
+ if (nomFichier.endsWith(extension[i])) return true;
+
+ }
+ return false;
+ }
+ public String getDescription() {
+ StringBuffer sb=new StringBuffer();
+ sb.append(description);
+ sb.append(" (");
+ for (int i=0;i<extension.length;i++){
+ sb.append("*");
+ sb.append(extension[i]);
+ if (i!=extension.length-1) sb.append(", ");
+ }
+ sb.append(")");
+ return new String(sb);
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/utils/Icon_x4s.png b/logo/src/xlogo/utils/Icon_x4s.png
new file mode 100644
index 0000000..1cbd384
--- /dev/null
+++ b/logo/src/xlogo/utils/Icon_x4s.png
Binary files differ
diff --git a/logo/src/xlogo/utils/Logo_xlogo4schools.png b/logo/src/xlogo/utils/Logo_xlogo4schools.png
new file mode 100644
index 0000000..3898176
--- /dev/null
+++ b/logo/src/xlogo/utils/Logo_xlogo4schools.png
Binary files differ
diff --git a/logo/src/xlogo/utils/Utils.java b/logo/src/xlogo/utils/Utils.java
new file mode 100644
index 0000000..3c2e703
--- /dev/null
+++ b/logo/src/xlogo/utils/Utils.java
@@ -0,0 +1,481 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+package xlogo.utils;
+import java.awt.Container;
+import java.awt.Font;
+import java.awt.Image;
+import java.awt.Component;
+import java.awt.Toolkit;
+import java.io.BufferedReader;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+import javax.swing.ImageIcon;
+
+import xlogo.kernel.MyCalculator;
+import xlogo.kernel.Affichage;
+import xlogo.storage.WSManager;
+import xlogo.storage.workspace.Language;
+import xlogo.Logo;
+
+public class Utils {
+
+ /**
+ * Marko : Here a technique was used for loading images, that happens to be from the last milenium.
+ * https://weblogs.java.net/blog/chet/archive/2004/07/imageio_just_an.html
+ *
+ * The way of the 3rs millenium is ImageIO
+ *
+ * @param nom
+ * @param jf
+ * @return
+ */
+ public static ImageIcon dimensionne_image(String name,Component jf){
+
+ Image img = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(name));
+
+ return new ImageIcon(img.getScaledInstance(20, 20, Image.SCALE_SMOOTH));
+
+ /*
+ Image image;
+ try
+ {
+ image = ImageIO.read(new File("xlogo/utils/"+nom));
+ return image.getScaledInstance(22, 22, Image.SCALE_SMOOTH);
+ }
+ catch (IOException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ }
+ / *
+ Image image=null;
+ image= Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(nom));
+ MediaTracker tracker=new MediaTracker(jf);
+ tracker.addImage(image,0);
+ try{tracker.waitForID(0);}
+ catch(InterruptedException e1){}
+ double largeur_ecran=Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+ int largeur=image.getWidth(jf);
+ int hauteur=image.getHeight(jf);
+ // On fait attention à la résolution de l'utilisateur
+ double facteur = largeur_ecran/1024.0; //les images sont prévues pour 1024x768
+ if ((int)(facteur+0.001)!=1){
+ image=image.getScaledInstance((int)(facteur*largeur),(int)(facteur*hauteur),Image.SCALE_SMOOTH);
+ tracker=new MediaTracker(jf);
+ tracker.addImage(image,0);
+ try{tracker.waitForID(0);}
+ catch(InterruptedException e1){}
+ }
+ return image;
+ */
+ }
+ public static void recursivelySetFonts(Component comp, Font font) {
+ comp.setFont(font);
+ if (comp instanceof Container) {
+ Container cont = (Container) comp;
+ for(int j=0, ub=cont.getComponentCount(); j<ub; ++j)
+ recursivelySetFonts(cont.getComponent(j), font);
+ }
+ }
+ public static String rajoute_backslash(String st){
+ StringBuffer tampon=new StringBuffer();
+ for(int j=0;j<st.length();j++){
+ char c=st.charAt(j);
+ if (c=='\\') tampon.append("\\\\");
+ else if (c==' ') tampon.append("\\e");
+ else if ("()[]#".indexOf(c)!=-1) tampon.append("\\"+c);
+ else tampon.append(c);
+ }
+ return(new String(tampon));
+ }
+
+ /**
+ * Escape string, backslash ...
+ * @param chaine
+ * @return
+ */
+ public static String SortieTexte(String chaine){ // Enlève les backslash
+ StringBuffer buffer=new StringBuffer();
+ boolean backslash=false;
+ boolean ignore=false;
+ for (int j=0;j<chaine.length();j++){
+ char c=chaine.charAt(j);
+ if (backslash) {
+ if (c=='e') buffer.append(' ');
+// else if (c=='\\') buffer.append('\\');
+ else if (c=='n') buffer.append("\n");
+ else if(c=='v') buffer.append("");
+ else if(c=='l') {
+ ignore=true;
+ }
+ else if("[]()#\\".indexOf(c)>-1) buffer.append(c);
+ backslash=false;
+ }
+ else {
+ if (c=='\\') backslash=true;
+ else if (!ignore) buffer.append(c);
+ else if (c==' ') ignore=false;
+ }
+ }
+ return MyCalculator.getOutputNumber(new String(buffer));
+ }
+ /**
+ * This method is formatting the String st.<br>
+ * - Unused white spaces are deleted.<br>
+ * - The character \ is modified to \\ <br>
+ * - The sequence "\ " is modified to "\e"<br>
+ * - The sequence "\ " is modified to "\e"<br>
+ * - The sequence "\ " is modified to "\e"<br>
+ * - The sequence "\ " is modified to "\e"<br>
+ * @param st The String instruction to format
+ * @return The formatted instructions
+ */
+ public static StringBuffer decoupe(String st) {
+ StringBuffer buffer = new StringBuffer();
+ // If last character is a white space
+ boolean espace=false;
+ // If last character is a backslash
+ boolean backslash=false;
+ // If last character is a word
+ boolean mot=false;
+
+ int crochet_liste=0;
+// boolean variable=false;
+ // If XLogo is running a program
+ boolean execution_lancee=Affichage.execution_lancee;
+ for(int i=0;i<st.length();i++){
+ char c=st.charAt(i);
+ if (c==' ') {
+ if (!espace&&buffer.length()!=0) {
+ if (backslash) buffer.append("\\e");
+ else {
+ buffer.append(c);
+ espace=true;
+ mot=false;
+ // variable=false;
+ }
+ backslash=false;
+ }
+ }
+ else if(c=='\\'&&!backslash) {
+ espace=false;
+ backslash=true;
+ }
+ else if(c=='\"'){
+ if (espace&&crochet_liste<=0){
+ mot=true;
+ }
+ buffer.append(c);
+ espace=false;
+ backslash=false;
+ }
+ else if (c==':'){
+ /* if (espace&&crochet_liste<=0){
+ variable=true;
+ }*/
+ buffer.append(c);
+ espace=false;
+ backslash=false;
+ }
+ else if (c=='['||c==']'||c=='('||c==')'){
+ //Modifications apportées
+ if (backslash) {
+ buffer.append("\\"+c);
+ backslash=false;
+ }
+ else {
+ if (c=='[') crochet_liste++;
+ else if (c==']') crochet_liste--;
+ if (espace||buffer.length()==0) {buffer.append(c+" ");espace=true;}
+ else {
+ buffer.append(" "+c+" ");
+ mot=false;
+ espace=true;
+ }
+ }
+ }
+ else if (c=='+'||c=='-'||c=='*'||c=='/'||c=='='||c=='<'||c=='>'||c=='&'||c=='|'){
+ //System.out.println(mot+" "+espace);
+ // à modifier (test + fin)
+ if (mot||crochet_liste>0) {
+ buffer.append(c);
+ if (espace) espace=false;
+ }
+ else {
+ String op=String.valueOf(c);
+ // Looking for operator <= or >=
+ if (c=='<'||c=='>'){
+ if (i+1<st.length()){
+ if (st.charAt(i+1)=='='){
+ op+="=";
+ i++;
+ }
+ }
+ }
+ if (espace) buffer.append(op+" ");
+ else {
+ espace=true;
+ if (buffer.length()!=0) buffer.append(" "+op+" ");
+ // If buffer is empty no white space before
+ else buffer.append(op+" ");
+ }
+ }
+ }
+ else{
+ if (backslash){
+ if (c=='n') buffer.append("\\n");
+ else if (c=='\\') buffer.append("\\\\");
+ else if (c=='v'&& execution_lancee) buffer.append("\"");
+ else if(c=='e'&& execution_lancee) buffer.append("\\e");
+ else if (c=='#') buffer.append("\\#");
+ else if (c=='l'&&execution_lancee) buffer.append("\\l");
+ else {
+ buffer.append(c);
+ }
+ }
+ else {
+ buffer.append(c);
+ }
+ backslash=false;
+ espace=false;
+ }
+ }
+// System.out.println(buffer);
+ // Remove the space when the user write only "*" or "+" in the command line
+ //if (buffer.length()>0&&buffer.charAt(0)==' ') buffer.deleteCharAt(0);
+ return (buffer);
+ }
+
+
+
+ public static String specialCharacterXML(String st){
+ st=st.replaceAll("&","&amp;");
+ st=st.replaceAll("<","&lt;");
+ st=st.replaceAll("\"","&quot;");
+ st=st.replaceAll(">","&gt;");
+ st=st.replaceAll("'","&apos;");
+
+ return st;
+ }
+ public static String readLogoFile(String path) throws IOException{ // ADAPT READ LOGO FILE
+ String txt="";
+ // The old format before XLogo 0.9.23 is no longer supported from version 0.9.30
+ try{
+ // New format for XLogo >=0.923
+ // Encoded with UTF-8
+ StringBuffer sb=new StringBuffer();
+ FileInputStream fr = new FileInputStream(path);
+ InputStreamReader isr = new InputStreamReader(fr, "UTF8");
+ BufferedReader brd=new BufferedReader(isr);
+ while (brd.ready()){
+ sb.append(brd.readLine());
+ sb.append("\n");
+ }
+ txt=new String(sb);
+ }
+ catch(FileNotFoundException e1){
+ // tentative fichier réseau
+ try{
+ URL url =new java.net.URL(path);
+ StringBuffer sb=new StringBuffer();
+ java.io.InputStream fr = url.openStream();
+ InputStreamReader isr = new InputStreamReader(fr, "UTF8");
+ BufferedReader brd=new BufferedReader(isr);
+ while (brd.ready()){
+ String st=brd.readLine();
+ sb.append(st);
+ sb.append("\n");
+ }
+ txt=new String(sb);
+ }
+ catch( java.net.MalformedURLException e){
+ System.out.println("File not found: "+path.toString());
+ }
+ }
+ catch(Exception e){e.printStackTrace();}
+ if (txt.startsWith("# "+Logo.messages.getString("mainCommand"))){
+ int id=txt.indexOf("\n");
+ if (id!=-1){
+ WSManager.getUserConfig().setMainCommand(txt.substring(("# "+Logo.messages.getString("mainCommand")).length(),id).trim());
+ txt=txt.substring(id+1);
+ }
+ };
+ return txt;
+ }
+
+ /**
+ * Store a string to a logo file path, UTF9 encoding,
+ * Write the main command to the head of the file with a #
+ * @param path
+ * @param txt
+ * @throws IOException
+ */
+ public static void writeLogoFile(String path,String txt) throws IOException{ // ADAPT write logo file
+ try{
+ if (!WSManager.getUserConfig().getMainCommand().trim().equals("")) {
+ String heading="# "+Logo.messages.getString("mainCommand")+" "+WSManager.getUserConfig().getMainCommand()+"\n";
+ txt=heading+txt;
+ }
+ FileOutputStream f = new FileOutputStream(path);
+ BufferedOutputStream b = new BufferedOutputStream(f);
+ OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8");
+ osw.write(txt);
+ osw.close();
+ b.close();
+ f.close();
+
+ }
+ catch(FileNotFoundException e1){e1.printStackTrace();}
+ }
+ public static boolean fileExists(String name){
+ File f=new File(name);
+ return f.exists();
+ }
+
+ /**
+ * @param name
+ * @return
+ * @author Marko Zivkovic
+ */
+ public static boolean isFile(String path){
+ File f = new File(path);
+ return f.isFile();
+ }
+ /**
+ * @param path
+ * @return
+ * @author Marko Zivkovic
+ */
+ public static boolean isDirectory(String path){
+ File f = new File(path);
+ return f.isDirectory();
+ }
+
+ /**
+ * Implementation inspired by "JAVA ist auch eine Insel" - Christian Ullenboom, Galileo Computing
+ * <p> If destination exists, it will be replaced
+ * @param src
+ * @param dest
+ * @throws IOException If there is a problem with either src or dest
+ * @author Marko Zivkovic
+ */
+ public static void copyFile(String src, String dest) throws IOException
+ {
+ copyFile(new File(src), new File(dest));
+ }
+ /**
+ * Implementation inspired by "JAVA ist auch eine Insel" - Christian Ullenboom, Galileo Computing
+ * <p> If destination exists, it will be replaced
+ * @param src
+ * @param dest
+ * @throws IOException If there is a problem with either src or dest
+ * @author Marko Zivkovic
+ */
+ public static void copyFile(File src, File dest) throws IOException
+ {
+ FileInputStream fis = null;
+ FileOutputStream fos = null;
+
+ try
+ {
+ fis = new FileInputStream(src);
+ fos = new FileOutputStream(dest);
+ byte[] buffer = new byte[0xFFFF];
+ for (int len; (len = fis.read(buffer)) != -1;)
+ {
+ fos.write(buffer, 0, len);
+ }
+ }catch (IOException e) {
+ throw e;
+ }
+ finally {
+ if (fis != null)
+ try { fis.close(); } catch (IOException e) {}
+ if (fos != null)
+ try { fos.close(); } catch (IOException e) {}
+ }
+ }
+
+ /**
+ * First copy file to dest and then delete file.
+ * @param file
+ * @param dest
+ * @throws IOException If there is a problem with either file or dest
+ * @author Marko Zivkovic
+ */
+ public static void moveFile(String file, String dest) throws IOException
+ {
+ moveFile(new File(file), new File(dest));
+ }
+
+ /**
+ * First copy file to dest and then delete file.
+ * @param file
+ * @param dest
+ * @throws IOException If there is a problem with either file or dest
+ * @author Marko Zivkovic
+ */
+ public static void moveFile(File file, File dest) throws IOException
+ {
+ copyFile(file, dest);
+ file.delete();
+ }
+
+
+ public static String primitiveName(String generic){
+ Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage();
+ Locale locale = lang.getLocale();
+ ResourceBundle prim = ResourceBundle.getBundle(
+ "primitives", locale);
+ String st = prim.getString(generic);
+ StringTokenizer str = new StringTokenizer(st);
+ while (str.hasMoreTokens()) {
+ st = str.nextToken();
+ }
+ return st;
+ }
+}
diff --git a/logo/src/xlogo/utils/WebPage.java b/logo/src/xlogo/utils/WebPage.java
new file mode 100644
index 0000000..56f1528
--- /dev/null
+++ b/logo/src/xlogo/utils/WebPage.java
@@ -0,0 +1,66 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+/**
+ * Title : XLogo
+ * Description : XLogo is an interpreter for the Logo
+ * programming language
+ * @author Loïc Le Coq
+ */
+package xlogo.utils;
+
+import javax.swing.event.*;
+import javax.swing.text.html.*;
+import javax.swing.JEditorPane;
+import javax.swing.event.HyperlinkListener;
+
+
+
+public class WebPage extends JEditorPane implements HyperlinkListener {
+ private static final long serialVersionUID = 1L;
+ public WebPage() {
+ super();
+ addHyperlinkListener(this);
+ }
+ public void hyperlinkUpdate(HyperlinkEvent e) {
+ if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ JEditorPane pane = (JEditorPane) e.getSource();
+ if (e instanceof HTMLFrameHyperlinkEvent) {
+ HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e;
+ HTMLDocument doc = (HTMLDocument)pane.getDocument();
+ doc.processHTMLFrameHyperlinkEvent(evt);
+ } else {
+ try {
+ pane.setPage(e.getURL());
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/logo/src/xlogo/utils/WriteImage.java b/logo/src/xlogo/utils/WriteImage.java
new file mode 100644
index 0000000..13426f9
--- /dev/null
+++ b/logo/src/xlogo/utils/WriteImage.java
@@ -0,0 +1,122 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. 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.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.utils;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import xlogo.Logo;
+import xlogo.storage.WSManager;
+
+import java.awt.image.BufferedImage;
+import java.awt.Dimension;
+
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JProgressBar;
+import javax.swing.JDialog;
+
+public class WriteImage extends Thread{
+ private BufferedImage image;
+ private JFrame owner;
+ private String path;
+ public WriteImage(JFrame owner,BufferedImage image){
+ this.image=image;
+ this.owner=owner;
+ }
+ public void setImage(BufferedImage img){
+ image=img;
+ }
+ public int chooseFile(){
+ JFileChooser jf = new JFileChooser();
+ String[] ext={".jpg",".png"};
+ jf.addChoosableFileFilter(new ExtensionFichier(Logo.messages.getString("imagefile"),
+ ext ));
+ int retval = jf.showDialog(owner, Logo.messages.getString("menu.file.save"));
+ // Si l'utilisateur appuie sur enregistrer du JFileChooser
+ if (retval == JFileChooser.APPROVE_OPTION) {
+ // On rajoute l'extension convenable au fichier
+ path=jf.getSelectedFile().getPath();
+ String copie_path=path.toLowerCase(); //
+ if (!copie_path.endsWith(".jpg") && !copie_path.endsWith(".png")) {
+ String st = jf.getFileFilter().getDescription().toLowerCase();
+ if (st.endsWith("jpg)"))
+ path += ".jpg";
+ else if (st.endsWith("png)"))
+ path += ".png";
+ else
+ path += ".jpg";
+ }
+ }
+ return retval;
+ }
+
+ public void run(){
+
+ ProgressDialog progress=new ProgressDialog(owner);
+
+
+ Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
+ // On écrit le fichier
+ try {
+ if (path.endsWith(".jpg")) {
+ File f = new File(path);
+ ImageIO.write(image, "jpg", f);
+ }
+ else if (path.endsWith(".png")) {
+ File f = new File(path);
+ ImageIO.write(image, "png", f);
+ }
+ }
+ catch (IOException ex) {System.out.println(ex.toString());}
+ progress.dispose();
+ }
+ private class ProgressDialog extends JDialog{
+
+ private static final long serialVersionUID = 1L;
+ private JProgressBar prog=new JProgressBar();
+ ProgressDialog(JFrame owner){
+ super(owner);
+ initGui();
+ }
+ private void initGui(){
+ setFont(WSManager.getWorkspaceConfig().getFont());
+ setTitle(Logo.messages.getString("titredialogue2"));
+ prog.setIndeterminate(true);
+ java.awt.FontMetrics fm = owner.getGraphics()
+ .getFontMetrics(WSManager.getWorkspaceConfig().getFont());
+ int width = fm.stringWidth(Logo.messages.getString("titredialogue2"));
+ setSize(new Dimension(width+150,100));
+ getContentPane().add(prog);
+ setVisible(true);
+ }
+ }
+} \ No newline at end of file
diff --git a/logo/src/xlogo/utils/animation.png b/logo/src/xlogo/utils/animation.png
new file mode 100644
index 0000000..ec96348
--- /dev/null
+++ b/logo/src/xlogo/utils/animation.png
Binary files differ
diff --git a/logo/src/xlogo/utils/background.png b/logo/src/xlogo/utils/background.png
new file mode 100644
index 0000000..fa16b1b
--- /dev/null
+++ b/logo/src/xlogo/utils/background.png
Binary files differ
diff --git a/logo/src/xlogo/utils/chercher.png b/logo/src/xlogo/utils/chercher.png
new file mode 100644
index 0000000..f3d0e70
--- /dev/null
+++ b/logo/src/xlogo/utils/chercher.png
Binary files differ
diff --git a/logo/src/xlogo/utils/close_icon.png b/logo/src/xlogo/utils/close_icon.png
new file mode 100644
index 0000000..003e5af
--- /dev/null
+++ b/logo/src/xlogo/utils/close_icon.png
Binary files differ
diff --git a/logo/src/xlogo/utils/down_arrow.png b/logo/src/xlogo/utils/down_arrow.png
new file mode 100644
index 0000000..4cfbf80
--- /dev/null
+++ b/logo/src/xlogo/utils/down_arrow.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau0.png b/logo/src/xlogo/utils/drapeau0.png
new file mode 100644
index 0000000..1b03ba5
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau0.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau1.png b/logo/src/xlogo/utils/drapeau1.png
new file mode 100644
index 0000000..f7c5c02
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau1.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau10.png b/logo/src/xlogo/utils/drapeau10.png
new file mode 100644
index 0000000..41cfe91
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau10.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau11.png b/logo/src/xlogo/utils/drapeau11.png
new file mode 100644
index 0000000..0d8ceab
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau11.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau12.png b/logo/src/xlogo/utils/drapeau12.png
new file mode 100644
index 0000000..bcddaf4
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau12.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau13.png b/logo/src/xlogo/utils/drapeau13.png
new file mode 100644
index 0000000..2b8e820
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau13.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau2.png b/logo/src/xlogo/utils/drapeau2.png
new file mode 100644
index 0000000..22af90c
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau2.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau3.png b/logo/src/xlogo/utils/drapeau3.png
new file mode 100644
index 0000000..1444d86
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau3.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau4.png b/logo/src/xlogo/utils/drapeau4.png
new file mode 100644
index 0000000..e16dcb1
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau4.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau5.png b/logo/src/xlogo/utils/drapeau5.png
new file mode 100644
index 0000000..31378fd
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau5.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau6.png b/logo/src/xlogo/utils/drapeau6.png
new file mode 100644
index 0000000..68d1cbb
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau6.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau7.png b/logo/src/xlogo/utils/drapeau7.png
new file mode 100644
index 0000000..9f9f9b1
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau7.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau8.png b/logo/src/xlogo/utils/drapeau8.png
new file mode 100644
index 0000000..1a5a07c
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau8.png
Binary files differ
diff --git a/logo/src/xlogo/utils/drapeau9.png b/logo/src/xlogo/utils/drapeau9.png
new file mode 100644
index 0000000..81001a3
--- /dev/null
+++ b/logo/src/xlogo/utils/drapeau9.png
Binary files differ
diff --git a/logo/src/xlogo/utils/editcopy.png b/logo/src/xlogo/utils/editcopy.png
new file mode 100644
index 0000000..28b5162
--- /dev/null
+++ b/logo/src/xlogo/utils/editcopy.png
Binary files differ
diff --git a/logo/src/xlogo/utils/editcut.png b/logo/src/xlogo/utils/editcut.png
new file mode 100644
index 0000000..275571d
--- /dev/null
+++ b/logo/src/xlogo/utils/editcut.png
Binary files differ
diff --git a/logo/src/xlogo/utils/editpaste.png b/logo/src/xlogo/utils/editpaste.png
new file mode 100644
index 0000000..42c2122
--- /dev/null
+++ b/logo/src/xlogo/utils/editpaste.png
Binary files differ
diff --git a/logo/src/xlogo/utils/error.png b/logo/src/xlogo/utils/error.png
new file mode 100644
index 0000000..3dd3b55
--- /dev/null
+++ b/logo/src/xlogo/utils/error.png
Binary files differ
diff --git a/logo/src/xlogo/utils/fileprint.png b/logo/src/xlogo/utils/fileprint.png
new file mode 100644
index 0000000..669b738
--- /dev/null
+++ b/logo/src/xlogo/utils/fileprint.png
Binary files differ
diff --git a/logo/src/xlogo/utils/fog.png b/logo/src/xlogo/utils/fog.png
new file mode 100644
index 0000000..8cd7bfd
--- /dev/null
+++ b/logo/src/xlogo/utils/fog.png
Binary files differ
diff --git a/logo/src/xlogo/utils/gnu_gpl.png b/logo/src/xlogo/utils/gnu_gpl.png
new file mode 100644
index 0000000..8ac8564
--- /dev/null
+++ b/logo/src/xlogo/utils/gnu_gpl.png
Binary files differ
diff --git a/logo/src/xlogo/utils/icone.png b/logo/src/xlogo/utils/icone.png
new file mode 100644
index 0000000..20484b8
--- /dev/null
+++ b/logo/src/xlogo/utils/icone.png
Binary files differ
diff --git a/logo/src/xlogo/utils/info_icon.png b/logo/src/xlogo/utils/info_icon.png
new file mode 100644
index 0000000..4d7fe39
--- /dev/null
+++ b/logo/src/xlogo/utils/info_icon.png
Binary files differ
diff --git a/logo/src/xlogo/utils/light0.png b/logo/src/xlogo/utils/light0.png
new file mode 100644
index 0000000..2ce6bd6
--- /dev/null
+++ b/logo/src/xlogo/utils/light0.png
Binary files differ
diff --git a/logo/src/xlogo/utils/light1.png b/logo/src/xlogo/utils/light1.png
new file mode 100644
index 0000000..70e5149
--- /dev/null
+++ b/logo/src/xlogo/utils/light1.png
Binary files differ
diff --git a/logo/src/xlogo/utils/light2.png b/logo/src/xlogo/utils/light2.png
new file mode 100644
index 0000000..a5d6c42
--- /dev/null
+++ b/logo/src/xlogo/utils/light2.png
Binary files differ
diff --git a/logo/src/xlogo/utils/light3.png b/logo/src/xlogo/utils/light3.png
new file mode 100644
index 0000000..4dc827e
--- /dev/null
+++ b/logo/src/xlogo/utils/light3.png
Binary files differ
diff --git a/logo/src/xlogo/utils/menubtn.png b/logo/src/xlogo/utils/menubtn.png
new file mode 100644
index 0000000..ec6a2bf
--- /dev/null
+++ b/logo/src/xlogo/utils/menubtn.png
Binary files differ
diff --git a/logo/src/xlogo/utils/play.png b/logo/src/xlogo/utils/play.png
new file mode 100644
index 0000000..ea86208
--- /dev/null
+++ b/logo/src/xlogo/utils/play.png
Binary files differ
diff --git a/logo/src/xlogo/utils/preview0.png b/logo/src/xlogo/utils/preview0.png
new file mode 100644
index 0000000..4e428f5
--- /dev/null
+++ b/logo/src/xlogo/utils/preview0.png
Binary files differ
diff --git a/logo/src/xlogo/utils/preview1.png b/logo/src/xlogo/utils/preview1.png
new file mode 100644
index 0000000..632ffa7
--- /dev/null
+++ b/logo/src/xlogo/utils/preview1.png
Binary files differ
diff --git a/logo/src/xlogo/utils/preview2.png b/logo/src/xlogo/utils/preview2.png
new file mode 100644
index 0000000..69468cf
--- /dev/null
+++ b/logo/src/xlogo/utils/preview2.png
Binary files differ
diff --git a/logo/src/xlogo/utils/preview3.png b/logo/src/xlogo/utils/preview3.png
new file mode 100644
index 0000000..5b34a32
--- /dev/null
+++ b/logo/src/xlogo/utils/preview3.png
Binary files differ
diff --git a/logo/src/xlogo/utils/preview4.png b/logo/src/xlogo/utils/preview4.png
new file mode 100644
index 0000000..1173235
--- /dev/null
+++ b/logo/src/xlogo/utils/preview4.png
Binary files differ
diff --git a/logo/src/xlogo/utils/preview5.png b/logo/src/xlogo/utils/preview5.png
new file mode 100644
index 0000000..b6f276a
--- /dev/null
+++ b/logo/src/xlogo/utils/preview5.png
Binary files differ
diff --git a/logo/src/xlogo/utils/preview6.png b/logo/src/xlogo/utils/preview6.png
new file mode 100644
index 0000000..d7d1d2c
--- /dev/null
+++ b/logo/src/xlogo/utils/preview6.png
Binary files differ
diff --git a/logo/src/xlogo/utils/quit.png b/logo/src/xlogo/utils/quit.png
new file mode 100644
index 0000000..54be95d
--- /dev/null
+++ b/logo/src/xlogo/utils/quit.png
Binary files differ
diff --git a/logo/src/xlogo/utils/redo.png b/logo/src/xlogo/utils/redo.png
new file mode 100644
index 0000000..4766577
--- /dev/null
+++ b/logo/src/xlogo/utils/redo.png
Binary files differ
diff --git a/logo/src/xlogo/utils/remove_256.png b/logo/src/xlogo/utils/remove_256.png
new file mode 100644
index 0000000..51e436d
--- /dev/null
+++ b/logo/src/xlogo/utils/remove_256.png
Binary files differ
diff --git a/logo/src/xlogo/utils/screenshot.png b/logo/src/xlogo/utils/screenshot.png
new file mode 100644
index 0000000..792558e
--- /dev/null
+++ b/logo/src/xlogo/utils/screenshot.png
Binary files differ
diff --git a/logo/src/xlogo/utils/stop.png b/logo/src/xlogo/utils/stop.png
new file mode 100644
index 0000000..0c1a0a9
--- /dev/null
+++ b/logo/src/xlogo/utils/stop.png
Binary files differ
diff --git a/logo/src/xlogo/utils/tortue0.png b/logo/src/xlogo/utils/tortue0.png
new file mode 100644
index 0000000..ba7057b
--- /dev/null
+++ b/logo/src/xlogo/utils/tortue0.png
Binary files differ
diff --git a/logo/src/xlogo/utils/tortue1.png b/logo/src/xlogo/utils/tortue1.png
new file mode 100644
index 0000000..dd97517
--- /dev/null
+++ b/logo/src/xlogo/utils/tortue1.png
Binary files differ
diff --git a/logo/src/xlogo/utils/tortue2.png b/logo/src/xlogo/utils/tortue2.png
new file mode 100644
index 0000000..5ba638f
--- /dev/null
+++ b/logo/src/xlogo/utils/tortue2.png
Binary files differ
diff --git a/logo/src/xlogo/utils/tortue3.png b/logo/src/xlogo/utils/tortue3.png
new file mode 100644
index 0000000..1234947
--- /dev/null
+++ b/logo/src/xlogo/utils/tortue3.png
Binary files differ
diff --git a/logo/src/xlogo/utils/tortue4.png b/logo/src/xlogo/utils/tortue4.png
new file mode 100644
index 0000000..499fbef
--- /dev/null
+++ b/logo/src/xlogo/utils/tortue4.png
Binary files differ
diff --git a/logo/src/xlogo/utils/tortue5.png b/logo/src/xlogo/utils/tortue5.png
new file mode 100644
index 0000000..bf622f2
--- /dev/null
+++ b/logo/src/xlogo/utils/tortue5.png
Binary files differ
diff --git a/logo/src/xlogo/utils/tortue6.png b/logo/src/xlogo/utils/tortue6.png
new file mode 100644
index 0000000..2be5e71
--- /dev/null
+++ b/logo/src/xlogo/utils/tortue6.png
Binary files differ
diff --git a/logo/src/xlogo/utils/undo.png b/logo/src/xlogo/utils/undo.png
new file mode 100644
index 0000000..e7bc8ec
--- /dev/null
+++ b/logo/src/xlogo/utils/undo.png
Binary files differ
diff --git a/logo/src/xlogo/utils/up_arrow.png b/logo/src/xlogo/utils/up_arrow.png
new file mode 100644
index 0000000..06d5aee
--- /dev/null
+++ b/logo/src/xlogo/utils/up_arrow.png
Binary files differ
diff --git a/logo/src/xlogo/utils/zoomin.png b/logo/src/xlogo/utils/zoomin.png
new file mode 100644
index 0000000..c896f8f
--- /dev/null
+++ b/logo/src/xlogo/utils/zoomin.png
Binary files differ
diff --git a/logo/src/xlogo/utils/zoomout.png b/logo/src/xlogo/utils/zoomout.png
new file mode 100644
index 0000000..a3da47d
--- /dev/null
+++ b/logo/src/xlogo/utils/zoomout.png
Binary files differ