├── src ├── history.md ├── .gitignore ├── images │ ├── fpgtdLogo.png │ ├── fpgtdLogo.xcf │ ├── freeplaneGTD.png │ ├── freeplaneGTD-icon.png │ └── freeplaneGTD-icon.xcf ├── zips │ ├── icons │ │ └── fpgtdIcon.png │ ├── scripts │ │ └── init │ │ │ └── AddGTDChangeListener.groovy │ ├── lib │ │ └── freeplaneGTD │ │ │ ├── mover │ │ │ ├── ReviewTask.groovy │ │ │ ├── ArchiveTask.groovy │ │ │ └── DoneMover.groovy │ │ │ ├── listener │ │ │ ├── GTDMapSelectionListener.groovy │ │ │ ├── GTDMapChangeListener.groovy │ │ │ └── GTDNodeChangeListener.groovy │ │ │ ├── util │ │ │ ├── IconUtil.groovy │ │ │ ├── Tag.groovy │ │ │ ├── ClipBoardUtil.groovy │ │ │ └── DateUtil.groovy │ │ │ ├── GtdReportController.groovy │ │ │ ├── editor │ │ │ └── ActionEditor.groovy │ │ │ ├── ReportModel.groovy │ │ │ └── GtdReportViewController.groovy │ └── templates │ │ └── GTD_template.mm ├── scripts │ ├── ReviewTask.groovy │ ├── ArchiveTask.groovy │ ├── ParseShorthand.groovy │ └── EditTask.groovy └── translations │ ├── nl.properties │ ├── es.properties │ ├── ru.properties │ ├── de.properties │ ├── en.properties │ ├── fr.properties │ └── hu.properties ├── doc ├── list.png ├── yes.png ├── full-2.png ├── button_ok.png ├── GTD_template.mm └── GTD with Freeplane.html ├── .gitignore ├── harness ├── resources │ └── images │ │ └── freeplaneGTD-icon.png ├── keys.properties └── src │ ├── FreePlaneGTD │ └── Harness.groovy │ └── freeeplaneHarness │ ├── ReportWindowCreator.groovy │ └── MyController.groovy ├── changelog.txt ├── test ├── lib │ └── freeplaneGTD │ │ ├── TagTest.groovy │ │ ├── DateUtilTest.groovy │ │ ├── ReportModelTest.groovy │ │ ├── ClipBoardUtilTest.groovy │ │ └── GTDMapReaderTest.groovy └── resources │ ├── Archive test.mm │ └── freeplaneGTD-test-gtd.mm └── README.md /src/history.md: -------------------------------------------------------------------------------- 1 | # History 2 | -------------------------------------------------------------------------------- /doc/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/doc/list.png -------------------------------------------------------------------------------- /doc/yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/doc/yes.png -------------------------------------------------------------------------------- /doc/full-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/doc/full-2.png -------------------------------------------------------------------------------- /doc/button_ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/doc/button_ok.png -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | freeplaneGTD.addon.mm.bak 2 | freeplaneGTD-*.addon.mm 3 | version.properties -------------------------------------------------------------------------------- /src/images/fpgtdLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/src/images/fpgtdLogo.png -------------------------------------------------------------------------------- /src/images/fpgtdLogo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/src/images/fpgtdLogo.xcf -------------------------------------------------------------------------------- /src/images/freeplaneGTD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/src/images/freeplaneGTD.png -------------------------------------------------------------------------------- /src/zips/icons/fpgtdIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/src/zips/icons/fpgtdIcon.png -------------------------------------------------------------------------------- /src/images/freeplaneGTD-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/src/images/freeplaneGTD-icon.png -------------------------------------------------------------------------------- /src/images/freeplaneGTD-icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/src/images/freeplaneGTD-icon.xcf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | .project 3 | .classpath 4 | .settings 5 | .idea 6 | .groovy 7 | /update.sh 8 | out 9 | *~ 10 | /.vs 11 | -------------------------------------------------------------------------------- /harness/resources/images/freeplaneGTD-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpapp/FreePlaneGTD/HEAD/harness/resources/images/freeplaneGTD-icon.png -------------------------------------------------------------------------------- /harness/keys.properties: -------------------------------------------------------------------------------- 1 | language=hu 2 | format_locale=automatic 3 | freeplaneGTD_default_view=missing 4 | freeplaneGTD_auto_fold_map=missing 5 | freeplaneGTD_remember_last_position=missing 6 | freeplaneGTD_filter_done=missing 7 | link_icon=back.png 8 | link_local_icon=back.png -------------------------------------------------------------------------------- /src/zips/scripts/init/AddGTDChangeListener.groovy: -------------------------------------------------------------------------------- 1 | import freeplaneGTD.GtdReportController 2 | import org.freeplane.features.mode.Controller 3 | import org.freeplane.features.mode.ModeController 4 | import org.freeplane.main.addons.AddOnsController 5 | 6 | def isActive = AddOnsController.getController().getInstalledAddOns().find{it.name == 'freeplaneGTD'}?.active 7 | 8 | if (isActive){ 9 | ModeController modeController = Controller.currentModeController 10 | GtdReportController.install(modeController) 11 | } 12 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/mover/ReviewTask.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.mover 2 | 3 | import org.freeplane.api.Node 4 | import org.freeplane.core.util.TextUtils 5 | 6 | class ReviewTask extends DoneMover { 7 | 8 | static Node findOrCreateReviewDir(Node node) { 9 | String reviewDirName = TextUtils.getText("freeplaneGTD.config.reviewDirName") 10 | Node rootNode = node.map.root 11 | 12 | Node archiveNode = rootNode.children.find { 13 | it.transformedText == reviewDirName 14 | } 15 | if (!archiveNode) { 16 | archiveNode = rootNode.createChild() 17 | archiveNode.text = reviewDirName 18 | } 19 | return archiveNode 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/mover/ArchiveTask.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.mover 2 | 3 | import org.freeplane.api.Node 4 | import org.freeplane.core.util.TextUtils 5 | 6 | class ArchiveTask extends DoneMover { 7 | 8 | static Node findOrCreateArchiveDir(Node node) { 9 | final Node rootNode = node.map.root 10 | final String archiveDirName = TextUtils.getText("freeplaneGTD.config.archiveDirName") 11 | 12 | Node archiveNode = rootNode.children.find { 13 | it.transformedText == archiveDirName 14 | } 15 | if (!archiveNode) { 16 | archiveNode = rootNode.createChild() 17 | archiveNode.text = archiveDirName 18 | } 19 | return archiveNode 20 | } 21 | } -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | v0.9 2 | Added support to close the GTD window with ESC key 3 | Added Done icon marking 4 | Showing done items crossed out in project view 5 | Added support to filter done items 6 | Parsed and reformatted set dates to use uniform, locale specific date format (NB. truncates time from items) 7 | v1.0 8 | Added global preference to filter completed tasks 9 | Added global preference to select default task view 10 | Added action to convert shortcuts to tasks (alt-h) 11 | Added task freeplaneGTD.editor (F4) 12 | Rewritten copy to clipboard function of action view, to support text and formatted content 13 | Added context icon support (convert icons to context) 14 | Added Hungarian translation 15 | Updated package to follow developer guidelines 16 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/listener/GTDMapSelectionListener.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.listener 2 | 3 | import freeplaneGTD.GtdReportController 4 | import org.freeplane.features.map.IMapSelectionListener 5 | import org.freeplane.features.map.MapModel 6 | import org.freeplane.features.mode.Controller 7 | 8 | 9 | /** 10 | * On changing the map make sure the task list is updated to the newly selected map 11 | * 12 | * @author gergely.papp@itworks.hu 13 | */ 14 | class GTDMapSelectionListener implements IMapSelectionListener { 15 | @Override 16 | void afterMapChange(MapModel oldMap, MapModel newMap) { 17 | if (newMap) { 18 | Controller.currentModeController.getExtension(GtdReportController.class).gtdReportViewController.refreshContent() 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/util/IconUtil.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.util 2 | 3 | import groovy.util.logging.Log 4 | 5 | import org.freeplane.core.resources.ResourceController 6 | import org.freeplane.features.icon.UIIcon 7 | import org.freeplane.features.icon.factory.IconFactory 8 | import org.freeplane.features.icon.factory.IconStoreFactory 9 | 10 | import javax.swing.* 11 | 12 | @Log 13 | class IconUtil { 14 | static Icon getIcon(String s) { 15 | return IconFactory.getInstance().getIcon(getUIIcon(s)) 16 | } 17 | 18 | static UIIcon getUIIcon(String s) { 19 | try { 20 | return IconStoreFactory.ICON_STORE.getUIIcon(s) 21 | } catch (Exception error) { 22 | log.info("Error looking up icon:"+s) 23 | } 24 | return new UIIcon(s, "icons/" + s + ".svg", "", "?", -1) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/lib/freeplaneGTD/TagTest.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | /** 3 | * Created by gpapp on 2015.03.02.. 4 | */ 5 | class TagTest extends GroovyTestCase { 6 | void testConstructors() { 7 | assert new Tag('A').toString() == '' 8 | assert new Tag('A', 'content').toString() == 'content' 9 | assert new Tag('A', 'content', [comes: 'here']).toString() == 'content' 10 | assert new Tag('A', 'content', [comes: 'here', because: 'content', is: 'God']).toString() == 'content' 11 | } 12 | 13 | void testToString() { 14 | assert new Tag('A', 'content').toString() == 'content' 15 | assert new Tag('A', '').toString() == '<brackets & ampersand>' 16 | assert new Tag('A').addContent('B', '').toString() == '<BOLD>' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/scripts/ReviewTask.groovy: -------------------------------------------------------------------------------- 1 | // @ExecutionModes({on_single_node="main_menu_scripting/freeplaneGTD[addons.archiveTask]"}) 2 | /* 3 | ========================================================= 4 | Freeplane GTD+ 5 | 6 | Copyright (c)2016 Gergely Papp 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | 21 | ========================================================= 22 | */ 23 | freeplaneGTD.mover.ReviewTask reviewTask = new freeplaneGTD.mover.ReviewTask() 24 | reviewTask.execute(freeplaneGTD.mover.ReviewTask.findOrCreateReviewDir(node), node) 25 | 26 | -------------------------------------------------------------------------------- /src/scripts/ArchiveTask.groovy: -------------------------------------------------------------------------------- 1 | // @ExecutionModes({on_single_node="main_menu_scripting/freeplaneGTD[addons.archiveTask]"}) 2 | /* 3 | ========================================================= 4 | Freeplane GTD+ 5 | 6 | Copyright (c)2016 Gergely Papp 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | 21 | ========================================================= 22 | */ 23 | freeplaneGTD.mover.ArchiveTask archiveTask = new freeplaneGTD.mover.ArchiveTask() 24 | archiveTask.execute(freeplaneGTD.mover.ArchiveTask.findOrCreateArchiveDir(node), node) 25 | 26 | -------------------------------------------------------------------------------- /src/scripts/ParseShorthand.groovy: -------------------------------------------------------------------------------- 1 | // @ExecutionModes({on_single_node="main_menu_scripting/freeplaneGTD[addons.parseShorthand]"}) 2 | //========================================================= 3 | // Freeplane GTD+ 4 | // 5 | // Copyright (c)2016 Gergely Papp 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | // 20 | //========================================================= 21 | import freeplaneGTD.GtdReportController 22 | import org.freeplane.features.mode.Controller 23 | 24 | Controller.currentModeController.getExtension(GtdReportController.getGtdReportControllerClass(Controller.currentModeController)). 25 | gtdReportViewController.parseAndRefresh() 26 | -------------------------------------------------------------------------------- /src/scripts/EditTask.groovy: -------------------------------------------------------------------------------- 1 | // @ExecutionModes({on_single_node="main_menu_scripting/freeplaneGTD[addons.editAction]"}) 2 | //========================================================= 3 | // Freeplane GTD+ 4 | // 5 | // Copyright (c)2014 Gergely Papp 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | // 20 | //========================================================= 21 | import freeplaneGTD.editor.ActionEditor 22 | import org.freeplane.api.Node 23 | import org.freeplane.core.ui.components.UITools 24 | 25 | List selecteds = c.getSelecteds() 26 | if (selecteds.size()==1) { 27 | ActionEditor editor = new ActionEditor() 28 | editor.editNode(selecteds[0]) 29 | } else if(selecteds.size()>1) { 30 | UITools.informationMessage("Cannot edit multiple tasks at once") 31 | } else { 32 | UITools.informationMessage("Nothing selected") 33 | } 34 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/listener/GTDMapChangeListener.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.listener 2 | 3 | import freeplaneGTD.GtdReportController 4 | import groovy.util.logging.Log 5 | import org.freeplane.features.map.IMapChangeListener 6 | import org.freeplane.features.map.NodeDeletionEvent 7 | import org.freeplane.features.map.NodeMoveEvent 8 | import org.freeplane.features.mode.Controller 9 | 10 | import java.util.logging.Level 11 | 12 | @Log 13 | class GTDMapChangeListener implements IMapChangeListener { 14 | private boolean enabled 15 | private final Class gtdReportControllerClass 16 | 17 | GTDMapChangeListener(Class gtdReportControllerClass) { 18 | this.gtdReportControllerClass = gtdReportControllerClass 19 | enabled = true 20 | } 21 | 22 | Class getGtdReportControllerClass() { return gtdReportControllerClass } 23 | 24 | @Override 25 | void onNodeDeleted(NodeDeletionEvent nodeDeletionEvent) { 26 | if (!enabled) return 27 | try { 28 | Controller.currentModeController.getExtension(GtdReportController.getGtdReportControllerClass(Controller.currentModeController)). 29 | gtdReportViewController.refreshContent() 30 | } catch (Exception e) { 31 | log.log(Level.SEVERE, e.message, e) 32 | } 33 | 34 | } 35 | 36 | @Override 37 | void onNodeMoved(NodeMoveEvent nodeMoveEvent) { 38 | if (!enabled) return 39 | try { 40 | Controller.currentModeController.getExtension(GtdReportController.getGtdReportControllerClass(Controller.currentModeController)). 41 | gtdReportViewController.refreshContent() 42 | } catch (Exception e) { 43 | log.log(Level.SEVERE, e.message, e) 44 | } 45 | } 46 | 47 | void setEnabled(boolean b) { 48 | enabled = b 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/translations/nl.properties: -------------------------------------------------------------------------------- 1 | main_menu_scripting/freeplaneGTD=FreeplaneGTD 2 | addons.${name}=FreeplaneGTD 3 | addons.${name}.listActions=Lijst met eerstvolgende acties 4 | addons.${name}.parseShorthand=Converteer actie vanuit omschrijving 5 | addons.${name}.editAction=Wijzig actie 6 | addons.${name}.archiveTask=Verplaats afgeronden naar archief 7 | addons.${name}.reviewTask=Verplaats afgeronden naar review 8 | OptionPanel.separator.freeplaneGTD=freeplaneGTD addon 9 | OptionPanel.freeplaneGTD_filter_done=Filter de afgewerkte acties er standaard uit. 10 | OptionPanel.freeplaneGTD_default_view=Standaard actie overzicht 11 | OptionPanel.freeplaneGTD_auto_fold_map=Vouw de map dicht zodat alleen relevante taken getoond worden 12 | freeplaneGTD_view_project=Project 13 | freeplaneGTD_view_who=Wie 14 | freeplaneGTD_view_context=Context 15 | freeplaneGTD_view_when=Tijdslijn 16 | freeplaneGTD.tab.project.title=Volgens Project 17 | freeplaneGTD.tab.project.tooltip=Lijst van projectacties 18 | freeplaneGTD.tab.who.title=Volgens Wie 19 | freeplaneGTD.tab.who.tooltip=Lijst van gedelegeerde acties 20 | freeplaneGTD.tab.context.title=Volgens Context 21 | freeplaneGTD.tab.context.tooltip=Lijst van contextuele acties 22 | freeplaneGTD.tab.when.title=Tijdslijn 23 | freeplaneGTD.tab.when.tooltip=Volgens Tijdslijn 24 | freeplaneGTD.tab.about.title=Over 25 | freeplaneGTD.tab.about.tooltip=Over Freeplane|GTD+ 26 | freeplaneGTD.button.refresh=Ververs 27 | freeplaneGTD.button.print=Print 28 | freeplaneGTD.button.copy=Kopieer 29 | freeplaneGTD.button.cancel=Sluit 30 | freeplaneGTD.button.filter_done=Filter klaar 31 | freeplaneGTD.button.show_notes=Toon notities 32 | freeplaneGTD.button.done=Klaar 33 | freeplaneGTD.button.select=Selecteer nodes 34 | freeplaneGTD.view.context.unassigned=Niet toegekend 35 | freeplaneGTD.view.when.today=Vandaag 36 | freeplaneGTD.view.when.this_week=Deze week 37 | freeplaneGTD.actioneditor.title=Pas actie aan 38 | freeplaneGTD.actioneditor.action=Actie 39 | freeplaneGTD.actioneditor.delegate=Wie 40 | freeplaneGTD.actioneditor.context=Context 41 | freeplaneGTD.actioneditor.when=Wanneer 42 | freeplaneGTD.actioneditor.today=Vandaag 43 | freeplaneGTD.actioneditor.priority=Prioriteit 44 | freeplaneGTD.actioneditor.done=Klaar 45 | freeplaneGTD.message.copy_ok=Selectie gekopieerd naar het clipboard 46 | freeplaneGTD.config.archiveDirName=Archief 47 | freeplaneGTD.config.reviewDirName=Review 48 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/mover/DoneMover.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.mover 2 | 3 | import freeplaneGTD.GTDMapReader 4 | import org.freeplane.api.Node 5 | 6 | /** 7 | * Created by gpapp on 2016.01.24.. 8 | */ 9 | abstract class DoneMover { 10 | protected final GTDMapReader mapReader = GTDMapReader.instance 11 | 12 | /** 13 | * Walk up the node structure to find the project path. 14 | * @param targetDir where to create the new directory 15 | * @param projectDir which node we currently try to look at 16 | * @return 17 | */ 18 | Node findProjectRoot(final Node targetDir, final Node projectDir) { 19 | if (projectDir == targetDir.map.root) { 20 | return targetDir 21 | } 22 | Node targetParentDir = findProjectRoot(targetDir, projectDir.parent) 23 | if (projectDir.icons.contains(mapReader.iconProject)) { 24 | Node targetProjectDir = targetParentDir.children.find { it.text == projectDir.text } 25 | if (!targetProjectDir) { 26 | targetProjectDir = targetParentDir.appendChild(projectDir) 27 | } 28 | return targetProjectDir 29 | } 30 | return targetParentDir 31 | } 32 | 33 | void execute(final Node targetDir, final Node node) { 34 | // Must reread it every time in case the configuration nodes were changed 35 | mapReader.findIcons() 36 | mapReader.internalConvertShorthand() 37 | if (node.text.equals("Archives")) { 38 | return 39 | } 40 | 41 | if (!node.icons.contains(mapReader.iconNextAction) || !mapReader.isDone(node)) { 42 | node.children.each { 43 | Node it -> 44 | if (it == targetDir) { 45 | return 46 | } 47 | this.execute(targetDir, it) 48 | } 49 | return 50 | } 51 | 52 | Node targetNode = findProjectRoot(targetDir, node.parent) 53 | Node oldParentPtr = node.parent 54 | node.moveTo(targetNode) 55 | node.left = targetNode.left 56 | 57 | while (!oldParentPtr.children && oldParentPtr.icons.contains(mapReader.iconProject) && oldParentPtr.parent) { 58 | Node toDelete = oldParentPtr 59 | oldParentPtr = oldParentPtr.parent 60 | toDelete.delete() 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/translations/es.properties: -------------------------------------------------------------------------------- 1 | main_menu_scripting/freeplaneGTD=FreeplaneGTD 2 | addons.${name}=FreeplaneGTD 3 | addons.${name}.listActions=Lista de acciones 4 | addons.${name}.parseShorthand=Convertir acciones desde notación 5 | addons.${name}.editAction=Editar acción 6 | addons.${name}.archiveTask=Pasar completadas a Archivo 7 | addons.${name}.reviewTask=Pasar completadas a Revisar 8 | OptionPanel.separator.freeplaneGTD=freeplaneGTD addon 9 | OptionPanel.freeplaneGTD_filter_done=Filtrar completadas por defecto 10 | OptionPanel.freeplaneGTD_default_view=Vista por defecto 11 | OptionPanel.freeplaneGTD_auto_fold_map=Colapsar mapa para ver solo acciones relevantes 12 | freeplaneGTD_view_project=Proyecto 13 | freeplaneGTD_view_who=Quien 14 | freeplaneGTD_view_context=Contexto 15 | freeplaneGTD_view_when=Orden temporal 16 | freeplaneGTD.tab.project.title=Por proyecto 17 | freeplaneGTD.tab.project.tooltip=Listar acciones por proyecto 18 | freeplaneGTD.tab.who.title=por Quien 19 | freeplaneGTD.tab.who.tooltip=Listar acciones por persona 20 | freeplaneGTD.tab.context.title=por Contexto 21 | freeplaneGTD.tab.context.tooltip=Lista acciones por contexto 22 | freeplaneGTD.tab.when.title=Orden temporal 23 | freeplaneGTD.tab.when.tooltip=Listar acciones por orden temporal 24 | freeplaneGTD.tab.about.title=Sobre 25 | freeplaneGTD.tab.about.tooltip=Sobre Freeplane|GTD+ 26 | freeplaneGTD.button.refresh=Refrescar 27 | freeplaneGTD.button.print=Imprimir 28 | freeplaneGTD.button.copy=Copiar 29 | freeplaneGTD.button.cancel=Cerrar 30 | freeplaneGTD.button.filter_done=Filtrar hechos 31 | freeplaneGTD.button.show_notes=Mostrar notas 32 | freeplaneGTD.button.done=Hecho 33 | freeplaneGTD.button.select=Seleccionar nodos 34 | freeplaneGTD.view.context.unassigned=Sin asignar 35 | freeplaneGTD.view.when.today=Hoy 36 | freeplaneGTD.view.when.this_week=Esta semana 37 | freeplaneGTD.actioneditor.title=Editar acción 38 | freeplaneGTD.actioneditor.action=Acción 39 | freeplaneGTD.actioneditor.delegate=Quien 40 | freeplaneGTD.actioneditor.context=Contexto 41 | freeplaneGTD.actioneditor.when=Cuando 42 | freeplaneGTD.actioneditor.today=Hoy 43 | freeplaneGTD.actioneditor.priority=Prioridad 44 | freeplaneGTD.actioneditor.done=Hecho 45 | freeplaneGTD.actioneditor.waitFor=Esperar a 46 | freeplaneGTD.actioneditor.waitUntil=Esperar hasta 47 | freeplaneGTD.message.copy_ok=Selección copiada al portapapeles 48 | freeplaneGTD.config.archiveDirName=Archivo 49 | freeplaneGTD.config.reviewDirName=Revisar 50 | -------------------------------------------------------------------------------- /src/translations/ru.properties: -------------------------------------------------------------------------------- 1 | main_menu_scripting/freeplaneGTD=FreeplaneGTD 2 | addons.${name}=FreeplaneGTD 3 | addons.${name}.listActions=Следующий список действий 4 | addons.${name}.parseShorthand=Конвертировать задачи из стенограммы 5 | addons.${name}.editAction=Изменить действие 6 | addons.${name}.archiveTask=Переместить завершенное в архив 7 | addons.${name}.reviewTask=Переместить завершенное в пересмотр 8 | OptionPanel.separator.freeplaneGTD=дополнение freeplaneGTD 9 | OptionPanel.freeplaneGTD_filter_done=Фильтровать завершенные задачи по умолчанию 10 | OptionPanel.freeplaneGTD_default_view=Обычный вид действий 11 | OptionPanel.freeplaneGTD_auto_fold_map=Свернуть карту и показать только значимые задачи 12 | freeplaneGTD_view_project=Проект 13 | freeplaneGTD_view_who=Кто 14 | freeplaneGTD_view_context=Контекст 15 | freeplaneGTD_view_when=Когда 16 | freeplaneGTD.tab.project.title=По проекту 17 | freeplaneGTD.tab.project.tooltip=Список действий по проекту 18 | freeplaneGTD.tab.who.title=Исполнители 19 | freeplaneGTD.tab.who.tooltip=Список действий по исполнителям 20 | freeplaneGTD.tab.context.title=По контексту 21 | freeplaneGTD.tab.context.tooltip=Список действий по контексту 22 | freeplaneGTD.tab.when.title=Сроки 23 | freeplaneGTD.tab.when.tooltip=Список действий по времени 24 | freeplaneGTD.tab.about.title=О программе 25 | freeplaneGTD.tab.about.tooltip=О Freeplane|GTD+ 26 | freeplaneGTD.button.refresh=Обновить 27 | freeplaneGTD.button.print=Печать 28 | freeplaneGTD.button.copy=Копировать 29 | freeplaneGTD.button.cancel=Закрыть 30 | freeplaneGTD.button.filter_done=Фильтровать завершенные 31 | freeplaneGTD.button.show_notes=Показать заметки 32 | freeplaneGTD.button.done=Завершено 33 | freeplaneGTD.button.select=Выбрать узлы 34 | freeplaneGTD.view.context.unassigned=Неназначено 35 | freeplaneGTD.view.when.today=Сегодня 36 | freeplaneGTD.view.when.this_week=На этой неделе 37 | freeplaneGTD.actioneditor.title=Изменить действие 38 | freeplaneGTD.actioneditor.action=Действие 39 | freeplaneGTD.actioneditor.delegate=Исполнитель 40 | freeplaneGTD.actioneditor.context=Контекст 41 | freeplaneGTD.actioneditor.when=Когда 42 | freeplaneGTD.actioneditor.today=Сегодня 43 | freeplaneGTD.actioneditor.priority=Приоритет 44 | freeplaneGTD.actioneditor.done=Завершено 45 | freeplaneGTD.actioneditor.waitFor=Ждать 46 | freeplaneGTD.actioneditor.waitUntil=Ждать пока 47 | freeplaneGTD.message.copy_ok=Выбранное скопировано в буфер обмена 48 | freeplaneGTD.config.archiveDirName=Архивы 49 | freeplaneGTD.config.reviewDirName=Пересмотр -------------------------------------------------------------------------------- /test/lib/freeplaneGTD/DateUtilTest.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | 3 | import org.junit.Test 4 | 5 | import java.text.SimpleDateFormat 6 | 7 | class DateUtilTest { 8 | 9 | int currentYear = Calendar.instance.get(Calendar.YEAR) 10 | Map goodresults = [ 11 | ['v1.1', 'v1.1'] : null, 12 | ['11 23', currentYear+'-11-23'] : 'MM dd', 13 | ['11/23', currentYear+'-11-23'] : 'MM/dd', 14 | ['11.23', currentYear+'-11-23'] : 'MM.dd', 15 | ['11.23.', currentYear+'-11-23']: 'MM.dd.', 16 | 17 | ['14-11-23', '2014-11-23'] : 'yy-MM-dd', 18 | ['14 11 23', '2014-11-23'] : 'yy MM dd', 19 | ['1/2/3', '2003-01-02'] : 'MM/dd/yy', 20 | ['11/23/14', '2014-11-23'] : 'MM/dd/yy', 21 | ['14.11.23', '2014-11-23'] : 'yy.MM.dd', 22 | ['14.11.23.', '2014-11-23'] : 'yy.MM.dd.', 23 | 24 | ['23-11-2014', '2014-11-23'] : 'dd-MM-yyyy', 25 | ['2014-11-23', '2014-11-23'] : 'yyyy-MM-dd', 26 | ['11/23/2014', '2014-11-23'] : 'MM/dd/yyyy', 27 | ['2014/11/23', '2014-11-23'] : 'yyyy/MM/dd', 28 | ['2014.11.23', '2014-11-23'] : 'yyyy.MM.dd', 29 | ['2014.11.23.', '2014-11-23'] : 'yyyy.MM.dd.', 30 | // '23 NOV 2014': 'dd MMM yyyy', 31 | ['23 November 2014', '2014-11-23']: 'dd MMMM yyyy', 32 | ] 33 | 34 | @Test 35 | void testDetermineDateFormat() { 36 | goodresults.each { value, result -> 37 | if (result == null) { 38 | assert (result == DateUtil.determineDateFormat(value[0])) 39 | } else { 40 | SimpleDateFormat determineDateFormat = DateUtil.determineDateFormat(value[0]) 41 | assert (null != determineDateFormat): 'Error parsing: ' + value[0] 42 | assert (result == determineDateFormat.toPattern()) 43 | } 44 | } 45 | } 46 | 47 | @Test 48 | void testNormalizeDate() { 49 | 50 | goodresults.each { value, result -> 51 | if (result == null) { 52 | assert (value[1] == DateUtil.normalizeDate(value[0])) 53 | } else { 54 | Date tocheck = DateUtil.stdFormat.parse(value[1]) 55 | assert (tocheck.equals(DateUtil.normalizeDate(value[0]))): 'Error parsing: ' + value[1] + ' normalizedDate: ' + DateUtil.normalizeDate(value[0]) 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/lib/freeplaneGTD/ReportModelTest.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | 3 | import groovy.mock.interceptor.MockFor 4 | import org.freeplane.core.util.TextUtils 5 | 6 | /** 7 | * Created by gpapp on 2015.03.05.. 8 | */ 9 | class ReportModelTest extends GroovyTestCase { 10 | void testGetTaskDateComparator() { 11 | def mockReportModel = new MockFor(TextUtils) 12 | mockReportModel.demand.getText { 'TODAY' } 13 | mockReportModel.demand.getText { 'THIS WEEK' } 14 | mockReportModel.use { 15 | ReportModel reportModel = new ReportModel() 16 | assert reportModel.taskDateComparator([], []) == 0 17 | assert reportModel.taskDateComparator([when: '0'], [when: '0']) == 0 18 | assert reportModel.taskDateComparator([when: '1'], [when: '0']) == 1 19 | assert reportModel.taskDateComparator([when: '0'], [when: '1']) == -1 20 | assert reportModel.taskDateComparator([when: new Date()], [when: '0']) == -1 21 | assert reportModel.taskDateComparator([when: 'TODAY'], [when: new Date().clearTime()]) == 0 22 | assert reportModel.taskDateComparator([when: 'THIS WEEK'], [when: new Date().clearTime() + 7]) == 0 23 | assert reportModel.taskDateComparator([when: 'TODAY'], [when: 'THIS WEEK']) == -1 24 | assert reportModel.taskDateComparator([when: 'TODAY'], [when: new Date().clearTime() + 1]) == -1 25 | assert reportModel.taskDateComparator([when: 'THIS WEEK'], [when: new Date().clearTime() + 8]) == -1 26 | } 27 | } 28 | 29 | void testGetTaskSortComparator() { 30 | def mockReportModel = new MockFor(TextUtils) 31 | mockReportModel.demand.getText { 'TODAY' } 32 | mockReportModel.demand.getText { 'THIS WEEK' } 33 | mockReportModel.use { 34 | ReportModel reportModel = new ReportModel() 35 | assert reportModel.taskSortComparator([priority: null], [priority: '0']) == 1 36 | assert reportModel.taskSortComparator([priority: '1'], [priority: '1']) == 0 37 | assert reportModel.taskSortComparator([priority: '0'], [priority: '1']) == -1 38 | assert reportModel.taskSortComparator([priority: '1', when: 'TODAY'], [priority: '2', when: 'TODAY']) == -1 39 | assert reportModel.taskSortComparator([priority: '1', when: 'TODAY'], [priority: '1', when: 'TODAY']) == 0 40 | assert reportModel.taskSortComparator([priority: '1', when: 'TODAY'], [priority: '1', when: 'THIS WEEK']) == -1 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/translations/de.properties: -------------------------------------------------------------------------------- 1 | main_menu_scripting/freeplaneGTD=FreeplaneGTD 2 | addons.${name}=FreeplaneGTD 3 | addons.${name}.listActions=Offene-Punkte-Liste, OPL 4 | addons.${name}.parseShorthand=Kurzschreibweise für Aktivität verarbeiten 5 | addons.${name}.editAction=Aktivität bearbeiten 6 | addons.${name}.archiveTask=verschiebe Aktivität ins Archiv 7 | addons.${name}.reviewTask=verschiebe Aktivität zum Review 8 | OptionPanel.separator.freeplaneGTD=freeplaneGTD addon 9 | OptionPanel.freeplaneGTD_filter_done=Voreinstellung: Erledigte Punkte herausfiltern 10 | OptionPanel.freeplaneGTD_default_view=Voreinstellung für OPL-Ansicht 11 | OptionPanel.freeplaneGTD_auto_fold_map=Map falten, um OPL-Punkte hervorzuheben 12 | freeplaneGTD_view_project=Projekt 13 | freeplaneGTD_view_who=Bearbeiter 14 | freeplaneGTD_view_context=Kontext 15 | freeplaneGTD_view_when=Wann 16 | freeplaneGTD.tab.project.title=projektorientiert 17 | freeplaneGTD.tab.project.tooltip=Projektorientierte Auflistung von Aktivitäten 18 | freeplaneGTD.tab.who.title=personenorientiert 19 | freeplaneGTD.tab.who.tooltip=Personenorientierte Auflistung von Aktivitäten 20 | freeplaneGTD.tab.context.title=kontextorientiert 21 | freeplaneGTD.tab.context.tooltip=Kontextorientierte Auflistung von Aktivitäten 22 | freeplaneGTD.tab.when.title=zeitorientiert 23 | freeplaneGTD.tab.when.tooltip=Zeitorientierte (chronologische) Auflistung von Aktivitäten 24 | freeplaneGTD.tab.about.title=Über FreeplaneGTD 25 | freeplaneGTD.tab.about.tooltip=Informationen bzgl. Freeplane|GTD 26 | freeplaneGTD.button.refresh=Aktualisieren 27 | freeplaneGTD.button.print=Drucken 28 | freeplaneGTD.button.copy=Kopieren 29 | freeplaneGTD.button.cancel=Abbruch 30 | freeplaneGTD.button.filter_done=Erledigte Aktivitäten herausfiltern 31 | freeplaneGTD.button.show_notes=Notizen darstellen 32 | freeplaneGTD.button.done=Speichern 33 | freeplaneGTD.button.select=Knoten in Map selektieren 34 | freeplaneGTD.view.context.unassigned=Nicht zugewiesen 35 | freeplaneGTD.view.when.today=Heute 36 | freeplaneGTD.view.when.this_week=Diese Woche 37 | freeplaneGTD.actioneditor.title=Aktivität bearbeiten 38 | freeplaneGTD.actioneditor.action=Tätigkeit / Aktivität 39 | freeplaneGTD.actioneditor.delegate=Bearbeiter 40 | freeplaneGTD.actioneditor.context=Kontext für Aktivität 41 | freeplaneGTD.actioneditor.when=Wann 42 | freeplaneGTD.actioneditor.today=Heute 43 | freeplaneGTD.actioneditor.priority=Priorität 44 | freeplaneGTD.actioneditor.done=Erledigt 45 | freeplaneGTD.actioneditor.waitFor=Warten auf 46 | freeplaneGTD.actioneditor.waitUntil=Warten bis 47 | freeplaneGTD.message.copy_ok=Auswahl wurde in den Zwischenspeicher abgelegt. 48 | freeplaneGTD.config.archiveDirName=Archiv 49 | freeplaneGTD.config.reviewDirName=Rückblick 50 | -------------------------------------------------------------------------------- /src/translations/en.properties: -------------------------------------------------------------------------------- 1 | main_menu_scripting/freeplaneGTD=FreeplaneGTD 2 | addons.${name}=FreeplaneGTD 3 | addons.${name}.listActions=Next action list 4 | addons.${name}.parseShorthand=Convert tasks from shorthand 5 | addons.${name}.editAction=Edit action 6 | addons.${name}.archiveTask=Move completed to archive 7 | addons.${name}.reviewTask=Move completed to review 8 | OptionPanel.separator.freeplaneGTD=freeplaneGTD addon 9 | OptionPanel.freeplaneGTD_filter_done=Filter completed tasks by default 10 | OptionPanel.freeplaneGTD_default_view=Default action view 11 | OptionPanel.freeplaneGTD_auto_fold_map=Fold map to show only relevant tasks 12 | freeplaneGTD_view_project=Project 13 | freeplaneGTD_view_who=Who 14 | freeplaneGTD_view_context=Context 15 | freeplaneGTD_view_when=Timeline 16 | freeplaneGTD.tab.project.title=By Project 17 | freeplaneGTD.tab.project.tooltip=List actions by project 18 | freeplaneGTD.tab.who.title=By Who 19 | freeplaneGTD.tab.who.tooltip=List action by delegates 20 | freeplaneGTD.tab.context.title=By Context 21 | freeplaneGTD.tab.context.tooltip=List actions by context 22 | freeplaneGTD.tab.when.title=Timeline 23 | freeplaneGTD.tab.when.tooltip=List actions by time 24 | freeplaneGTD.tab.done_timeline.title=Done timeline 25 | freeplaneGTD.tab.done_timeline.tooltip=Timeline of done tasks 26 | freeplaneGTD.tab.about.title=About 27 | freeplaneGTD.tab.about.tooltip=About Freeplane|GTD+ 28 | freeplaneGTD.button.refresh=Refresh 29 | freeplaneGTD.button.print=Print 30 | freeplaneGTD.button.copy=Copy 31 | freeplaneGTD.button.cancel=Close 32 | freeplaneGTD.button.filter_done=Filter done 33 | freeplaneGTD.button.show_notes=Display notes 34 | freeplaneGTD.button.done=Done 35 | freeplaneGTD.button.select=Select nodes 36 | freeplaneGTD.view.context.unassigned=Unassigned 37 | freeplaneGTD.view.when.today=Today 38 | freeplaneGTD.view.when.this_week=This week 39 | freeplaneGTD.view.when.unscheduled=Unscheduled 40 | freeplaneGTD.actioneditor.title=Edit action 41 | freeplaneGTD.actioneditor.action=Action 42 | freeplaneGTD.actioneditor.delegate=Who 43 | freeplaneGTD.actioneditor.context=Context 44 | freeplaneGTD.actioneditor.when=When 45 | freeplaneGTD.actioneditor.today=Today 46 | freeplaneGTD.actioneditor.priority=Priority 47 | freeplaneGTD.actioneditor.done=Done 48 | freeplaneGTD.actioneditor.cancelled=Cancelled 49 | freeplaneGTD.actioneditor.waitFor=Wait for 50 | freeplaneGTD.actioneditor.waitUntil=Wait until 51 | freeplaneGTD.message.copy_ok=Selection copied to clipboard. 52 | freeplaneGTD.config.archiveDirName=Archives 53 | freeplaneGTD.config.reviewDirName=Review 54 | freeplaneGTD_remember_last_position=Remember last position 55 | freeplaneGTD.doneTimeline.TODAY=Today 56 | freeplaneGTD.doneTimeline.LAST_WEEK=Last week 57 | freeplaneGTD.doneTimeline.LAST_MONTH=Last month 58 | freeplaneGTD.doneTimeline.EARLIER=Earlier -------------------------------------------------------------------------------- /src/translations/fr.properties: -------------------------------------------------------------------------------- 1 | main_menu_scripting/freeplaneGTD=FreeplaneGTD 2 | addons.${name}=FreeplaneGTD 3 | addons.${name}.listActions=Liste des prochaines actions 4 | addons.${name}.parseShorthand=Convertir action à partir du libellé 5 | addons.${name}.editAction=Modifier action 6 | addons.${name}.archiveTask=Déplacement terminée pour archiver 7 | addons.${name}.reviewTask=Déplacement terminée à donner votre avis 8 | OptionPanel.separator.freeplaneGTD=freeplaneGTD addon 9 | OptionPanel.freeplaneGTD_filter_done=Par défaut, masquer les actions terminées 10 | OptionPanel.freeplaneGTD_default_view=Par défaut, lister les actions par 11 | OptionPanel.freeplaneGTD_auto_fold_map=Réduire la carte pour n'afficher que les actions personnelles 12 | freeplaneGTD_view_project=Projet 13 | freeplaneGTD_view_who=Responsable 14 | freeplaneGTD_view_context=Contexte 15 | freeplaneGTD_view_when=Échéance 16 | freeplaneGTD.tab.project.title=Par projet 17 | freeplaneGTD.tab.project.tooltip=Liste les actions par projet 18 | freeplaneGTD.tab.who.title=Par responsable 19 | freeplaneGTD.tab.who.tooltip=Liste les actions par responsable 20 | freeplaneGTD.tab.context.title=Par contexte 21 | freeplaneGTD.tab.context.tooltip=Liste les actions par contexte 22 | freeplaneGTD.tab.when.title=Par échéance 23 | freeplaneGTD.tab.when.tooltip=Liste chronologique des actions 24 | freeplaneGTD.tab.done_timeline.title=Actions terminées 25 | freeplaneGTD.tab.done_timeline.tooltip=Liste chronologique des actions terminées 26 | freeplaneGTD.tab.about.title=À propos 27 | freeplaneGTD.tab.about.tooltip=À propos de Freeplane|GTD+ 28 | freeplaneGTD.button.refresh=Actualiser 29 | freeplaneGTD.button.print=Imprimer 30 | freeplaneGTD.button.copy=Copier 31 | freeplaneGTD.button.cancel=Annuler 32 | freeplaneGTD.button.filter_done=Masquer les actions terminées 33 | freeplaneGTD.button.show_notes=Afficher les notes 34 | freeplaneGTD.button.done=Ok 35 | freeplaneGTD.button.select=Sélectionner les noeuds 36 | freeplaneGTD.view.context.unassigned=Non assigné 37 | freeplaneGTD.view.when.today=Aujourd'hui 38 | freeplaneGTD.view.when.this_week=Cette semaine 39 | freeplaneGTD.actioneditor.title=Modifier action 40 | freeplaneGTD.actioneditor.action=Action 41 | freeplaneGTD.actioneditor.delegate=Responsable 42 | freeplaneGTD.actioneditor.context=Contexte 43 | freeplaneGTD.actioneditor.when=Échéance 44 | freeplaneGTD.actioneditor.today=Aujourd'hui 45 | freeplaneGTD.actioneditor.priority=Priorité 46 | freeplaneGTD.actioneditor.done=Terminée 47 | freeplaneGTD.actioneditor.cancelled=Annulée 48 | freeplaneGTD.actioneditor.waitFor=En attente de 49 | freeplaneGTD.actioneditor.waitUntil=En attente jusqu’au 50 | freeplaneGTD.message.copy_ok=Sélection copiée dans le presse-papiers 51 | freeplaneGTD.config.archiveDirName=Archives 52 | freeplaneGTD.config.reviewDirName=Revue 53 | freeplaneGTD_remember_last_position=Se souvenir de la dernière position 54 | freeplaneGTD.doneTimeline.TODAY=Aujourd’hui 55 | freeplaneGTD.doneTimeline.LAST_WEEK=La semaine dernière 56 | freeplaneGTD.doneTimeline.LAST_MONTH=Le mois dernier 57 | freeplaneGTD.doneTimeline.EARLIER=Antérieur 58 | -------------------------------------------------------------------------------- /src/translations/hu.properties: -------------------------------------------------------------------------------- 1 | addons.${name}=FreeplaneGTD 2 | addons.${name}.listActions=Teendők 3 | addons.${name}.parseShorthand=Feladatok létrehozása rövidítésből 4 | addons.${name}.editAction=Szerkesztés 5 | addons.${name}.archiveTask=Kész feladatok arcívumba 6 | addons.${name}.reviewTask=Kész feladatok átnézésre 7 | main_menu_scripting/freeplaneGTD=FreeplaneGTD 8 | OptionPanel.separator.freeplaneGTD=freeplaneGTD tulajdonságai 9 | OptionPanel.freeplaneGTD_filter_done=Kész elemek szűrése alapból 10 | OptionPanel.freeplaneGTD_default_view=Kezdeti feladat nézet 11 | OptionPanel.freeplaneGTD_auto_fold_map=Nem kiválasztott ágak automatikus összecsukása 12 | OptionPanel.freeplaneGTD_last_position_x=X pozíció 13 | OptionPanel.freeplaneGTD_last_position_y=Y pozíció 14 | OptionPanel.freeplaneGTD_last_position_w=Szélesség 15 | OptionPanel.freeplaneGTD_last_position_h=Magasság 16 | freeplaneGTD_view_project=Projekt 17 | freeplaneGTD_view_who=Felelős 18 | freeplaneGTD_view_context=Kontextus 19 | freeplaneGTD_view_when=Idővonal 20 | freeplaneGTD.tab.project.title=Projektenként 21 | freeplaneGTD.tab.project.tooltip=Feladatok listája projektenként 22 | freeplaneGTD.tab.who.title=Személyenként 23 | freeplaneGTD.tab.who.tooltip=Feladatok listája személyenként 24 | freeplaneGTD.tab.context.title=Kontextusonként 25 | freeplaneGTD.tab.when.title=Idővonal 26 | freeplaneGTD.tab.when.tooltip=Feladatok listája időrendi sorrendben 27 | freeplaneGTD.tab.done_timeline.title=Lezárt feladatok 28 | freeplaneGTD.tab.done_timeline.tooltip=Lezárt feladatok időrendes összesítése 29 | freeplaneGTD.tab.about.title=Névjegy 30 | freeplaneGTD.tab.about.tooltip=A Freeplane|GTD+ névjegye 31 | freeplaneGTD.button.refresh=Frissítés 32 | freeplaneGTD.button.print=Nyomtatás 33 | freeplaneGTD.button.copy=Vágólapra 34 | freeplaneGTD.button.cancel=Bezárás 35 | freeplaneGTD.button.filter_done=Lezártak szűrése 36 | freeplaneGTD.button.show_notes=Jegyzetek megjelenítése 37 | freeplaneGTD.button.done=Ok 38 | freeplaneGTD.button.select=Elemek kiválasztása 39 | freeplaneGTD.view.context.unassigned=Kontextus nélkül 40 | freeplaneGTD.view.when.today=Ma 41 | freeplaneGTD.view.when.this_week=Ezen a héten 42 | freeplaneGTD.view.when.unscheduled=Nincs ütemezve 43 | freeplaneGTD.actioneditor.title=Feladat szerkesztése 44 | freeplaneGTD.actioneditor.action=Feladat 45 | freeplaneGTD.actioneditor.delegate=Végrehajtó 46 | freeplaneGTD.actioneditor.context=Kontextus 47 | freeplaneGTD.actioneditor.when=Időpont 48 | freeplaneGTD.actioneditor.today=Ma 49 | freeplaneGTD.actioneditor.priority=Prioritás 50 | freeplaneGTD.actioneditor.done=Kész 51 | freeplaneGTD.actioneditor.cancelled=Mégsem 52 | freeplaneGTD.actioneditor.waitFor=Kire vár 53 | freeplaneGTD.actioneditor.waitUntil=Meddig vár 54 | freeplaneGTD.message.copy_ok=Választott elemek a vágólapra másolva. 55 | freeplaneGTD.config.archiveDirName=Archívum 56 | freeplaneGTD.config.reviewDirName=Átnézendő 57 | freeplaneGTD_remember_last_position=Utolsó ablakpozíció megjegyzése 58 | freeplaneGTD.doneTimeline.TODAY=Ma 59 | freeplaneGTD.doneTimeline.LAST_WEEK=Előző hét 60 | freeplaneGTD.doneTimeline.LAST_MONTH=Előző hónap 61 | freeplaneGTD.doneTimeline.EARLIER=Korábbi időszak -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/util/Tag.groovy: -------------------------------------------------------------------------------- 1 | //========================================================= 2 | // Freeplane GTD+ 3 | // 4 | // Copyright (c)2014 Gergely Papp 5 | // 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // any later version. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | // 19 | //========================================================= 20 | package freeplaneGTD.util 21 | 22 | class Tag { 23 | String tagName 24 | def content = [] 25 | Map params = [:] 26 | 27 | Tag(tagName, Map params = null) { 28 | this.tagName = tagName 29 | if (params) { 30 | this.params = params 31 | } 32 | } 33 | 34 | Tag(tagName, Object content, params = null) { 35 | this(tagName) 36 | if (content) { 37 | addContent(content.toString()) 38 | } 39 | if (params) { 40 | this.params = params 41 | } 42 | } 43 | 44 | Tag(tagName, Tag content, params = null) { 45 | this(tagName) 46 | if (content) { 47 | addContent(content) 48 | } 49 | if (params) { 50 | this.params = params 51 | } 52 | } 53 | 54 | Tag addContent(Object content, Map params = null) { 55 | // Very simple sanitation for HTML entities <> here! 56 | this.content.add(content.toString().replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>')) 57 | if (params) { 58 | this.params = params 59 | } 60 | return this 61 | } 62 | 63 | Tag addPreformatted(String tag) { 64 | this.content.add(tag) 65 | return this 66 | } 67 | 68 | Tag addContent(Tag tag) { 69 | this.content.add(tag) 70 | return this 71 | } 72 | 73 | Tag addContent(tagName, Object content, Map params = null) { 74 | Tag tag = new Tag(tagName, content, params) 75 | this.content.add(tag) 76 | return this 77 | } 78 | 79 | Tag addChild(tagName, Map params = null) { 80 | Tag tag = new Tag(tagName, params) 81 | this.content.add(tag) 82 | return tag 83 | } 84 | 85 | Tag addProperty(String key, String value) { 86 | params.put(key, value) 87 | } 88 | 89 | String toString() { 90 | StringBuilder retval = new StringBuilder() 91 | retval.append('<').append(tagName) 92 | params.each { 93 | retval.append(' ') 94 | retval.append(it.key) 95 | retval.append('=\'') 96 | retval.append(it.value) 97 | retval.append('\'') 98 | } 99 | retval.append('>') 100 | content.each { 101 | retval.append(it) 102 | } 103 | retval.append('') 104 | return retval.toString() 105 | } 106 | 107 | } 108 | 109 | -------------------------------------------------------------------------------- /harness/src/FreePlaneGTD/Harness.groovy: -------------------------------------------------------------------------------- 1 | package FreePlaneGTD 2 | 3 | import freeeplaneHarness.MyController 4 | import freeplaneGTD.ReportWindow 5 | import groovy.swing.SwingBuilder 6 | import org.freeplane.core.resources.ResourceBundles 7 | import org.freeplane.features.mode.Controller 8 | import org.freeplane.main.application.ApplicationResourceController 9 | import org.freeplane.plugin.script.FreeplaneScriptBaseClass 10 | import org.freeplane.plugin.script.proxy.Proxy 11 | import org.knopflerfish.framework.FrameworkContext 12 | import org.knopflerfish.framework.ServiceURLStreamHandlerFactory 13 | 14 | import javax.swing.* 15 | import java.awt.* 16 | import java.awt.event.ActionEvent 17 | 18 | class Harness implements Runnable { 19 | private static final String PROPERTY_FILE = "harness/keys.properties" 20 | 21 | @Override 22 | void run() { 23 | def config = new FreeplaneScriptBaseClass.ConfigProperties() 24 | Proxy.Map map 25 | 26 | JFrame myFrame 27 | SwingBuilder.edtBuilder { 28 | myFrame = frame(title: 'FreePlaneGTD.Harness', 29 | defaultCloseOperation: JFrame.EXIT_ON_CLOSE) { 30 | borderLayout() 31 | button(name: 'toolbar', constraints: BorderLayout.NORTH) 32 | button( 33 | constraints: BorderLayout.SOUTH, 34 | text: 'Refresh', 35 | action: 36 | new AbstractAction() { 37 | @Override 38 | void actionPerformed(ActionEvent e) { 39 | ReportWindow reportWindow = ReportWindow.instance 40 | reportWindow.show(config) 41 | reportWindow.refresh(map.root) 42 | } 43 | } 44 | ) 45 | } 46 | } 47 | 48 | myFrame.visible = true 49 | myFrame.setLocation(10, 10) 50 | myFrame.setSize(100, 100) 51 | 52 | def resourceController = new ApplicationResourceController() 53 | Controller.currentController = new MyController(resourceController, myFrame) 54 | ((ResourceBundles) resourceController.resources). 55 | addResources("hu", new URL("file:src/zips/translations/freeplaneGTD_en.properties")) 56 | 57 | ServiceURLStreamHandlerFactory streamHandlerFactory = new ServiceURLStreamHandlerFactory() 58 | URL.setURLStreamHandlerFactory(streamHandlerFactory) 59 | streamHandlerFactory.addFramework(new FrameworkContext([ 60 | "org.osgi.framework.security": "", 61 | "org.osgi.framework.storage" : "/usr/share/freeplane/fwdir"])) 62 | 63 | 64 | map = Controller.currentController.newMap() 65 | addNode(map, "ID__1", "*test task 1") 66 | addNode(map, "ID__2", "*test task 2 {2017-06-19}") 67 | addNode(map, "ID__3", "*test task 3 #3 {2017-06-19} @Context1 @Context2") 68 | 69 | 70 | config.properties.put(ReportWindow.FREEPLANE_GTD_DEFAULT_VIEW, ReportWindow.VIEW.WHEN.toString()) 71 | config.properties.put(ReportWindow.FREEPLANE_GTD_REMEMBER_LAST_POSITION, Boolean.TRUE) 72 | } 73 | 74 | private static void addNode(Proxy.Map map, String id, String text) { 75 | Proxy.Node child = map.root.createChild() 76 | child.text = text 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/GtdReportController.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | 3 | import freeplaneGTD.listener.GTDMapChangeListener 4 | import freeplaneGTD.listener.GTDMapSelectionListener 5 | import freeplaneGTD.listener.GTDNodeChangeListener 6 | import groovy.util.logging.Log 7 | import org.freeplane.core.extension.IExtension 8 | import org.freeplane.features.map.IMapChangeListener 9 | import org.freeplane.features.map.MapController 10 | import org.freeplane.features.mode.Controller 11 | import org.freeplane.features.mode.ModeController 12 | 13 | import javax.swing.* 14 | import java.awt.* 15 | 16 | @Log 17 | class GtdReportController implements IExtension { 18 | ModeController modeController 19 | public GtdReportViewController gtdReportViewController 20 | static final String controllerTitle = 'GTD Next Actions' 21 | static private GTDMapChangeListener gtdMapChangeListener 22 | static private GTDNodeChangeListener gtdNodeChangeListener 23 | 24 | GtdReportController(ModeController modeController) { 25 | this.modeController = modeController 26 | this.gtdReportViewController = new GtdReportViewController() 27 | } 28 | 29 | static void install(ModeController modeController) { 30 | GtdReportController reportController = new GtdReportController(modeController) 31 | modeController.addExtension(GtdReportController.class, reportController) 32 | 33 | JTabbedPane tabs = (JTabbedPane) modeController.getUserInputListenerFactory().getToolBar("/format").getComponent(1) 34 | tabs.add(controllerTitle, reportController.createPanel()) 35 | 36 | Controller controller = Controller.getCurrentController() 37 | controller.getMapViewManager().addMapSelectionListener(new GTDMapSelectionListener()) 38 | 39 | // I hate OSGI and Groovy all classloaders are running amok, so I need to find a secure location to store my "main" class otherwise I 40 | // won't be able to access my own window from a script 41 | gtdMapChangeListener = new GTDMapChangeListener(GtdReportController.class) 42 | MapController mapController = modeController.mapController 43 | mapController.addMapChangeListener(gtdMapChangeListener) 44 | 45 | gtdNodeChangeListener = new GTDNodeChangeListener() 46 | mapController.addNodeChangeListener(gtdNodeChangeListener) 47 | } 48 | 49 | static Class getGtdReportControllerClass(ModeController modeController) { 50 | // try to find my ReportController class in the change listener 51 | for (IMapChangeListener i in modeController.mapController.getMapChangeListeners()) { 52 | // MUST NOT USE INSTANCEOF, I'm not fully crazy, it`s in a different classloader 53 | if (i.class.canonicalName == GTDMapChangeListener.class.canonicalName) { 54 | // Cannot cast it to the listener either, because it is of a "Different type" 55 | return i.class.getMethod("getGtdReportControllerClass").invoke(i) 56 | } 57 | } 58 | throw new Exception("Cannot find GTDMapChangeListener in map listeners") 59 | } 60 | 61 | static void disableListeners() { 62 | gtdMapChangeListener.setEnabled(false) 63 | gtdNodeChangeListener.setEnabled(false) 64 | } 65 | 66 | static void enableListeners() { 67 | gtdMapChangeListener.setEnabled(true) 68 | gtdNodeChangeListener.setEnabled(true) 69 | } 70 | 71 | private Component createPanel() { 72 | Component reportViewController = this.gtdReportViewController.createPanel(this.modeController) 73 | this.gtdReportViewController.refreshContent() 74 | return reportViewController 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/lib/freeplaneGTD/ClipBoardUtilTest.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | 3 | import groovy.mock.interceptor.MockFor 4 | import org.freeplane.core.util.TextUtils 5 | 6 | /** 7 | * Created by gpapp on 2015.03.05.. 8 | */ 9 | class ClipBoardUtilTest extends GroovyTestCase { 10 | void testExtractText() { 11 | def mockReportModel = new MockFor(TextUtils) 12 | mockReportModel.demand.getText { 'Title' } 13 | mockReportModel.use { 14 | assert 'Title' == ClipBoardUtil.extractText([type: 'test_type', groups: []]) 15 | } 16 | mockReportModel.demand.getText { 'Title' } 17 | mockReportModel.use { 18 | assert 'Title\n\tGroup1' == ClipBoardUtil.extractText([type: 'test_type', groups: [ 19 | [title: 'Group1', list: []]]]) 20 | } 21 | mockReportModel.demand.getText { 'Title' } 22 | mockReportModel.use { 23 | assert 'Title\n' + 24 | '\tGroup1\n' + 25 | '\t\t* item1' == ClipBoardUtil.extractText([type: 'test_type', groups: [ 26 | [title: 'Group1', items: [[action: 'item1', nodeID: '1234']]]]]) 27 | } 28 | mockReportModel.demand.getText { 'Title' } 29 | mockReportModel.use { 30 | assert 'Title\n' + 31 | '\tGroup1\n' + 32 | '\t\t* item1\n' + 33 | '\t\t* item2 [who] {when} for project @context' == ClipBoardUtil.extractText([type: 'test_type', groups: [ 34 | [title: 'Group1', 35 | items: [[action: 'item1', nodeID: '1234'], 36 | [action: 'item2', nodeID: '1234', who: 'who', when: 'when', context: 'context', project: 'project']]]]]) 37 | } 38 | } 39 | 40 | void testExtractHtml() { 41 | def mockReportModel = new MockFor(TextUtils) 42 | mockReportModel.demand.getText { 'Title' } 43 | mockReportModel.use { 44 | assert '

Title

' == ClipBoardUtil.extractHtml([type: 'test_type', groups: []],true) 45 | } 46 | mockReportModel.demand.getText { 'Title' } 47 | mockReportModel.use { 48 | assert '

Title

Group1

    ' == ClipBoardUtil.extractHtml([type: 'test_type', groups: [ 49 | [title: 'Group1', list: []]]],true) 50 | } 51 | mockReportModel.demand.getText { 'Title' } 52 | mockReportModel.use { 53 | assert '

    Title

    Group1

    • item1
    ' == ClipBoardUtil.extractHtml([type: 'test_type', groups: [ 54 | [title: 'Group1', items: [[action: 'item1', notes:'abc', nodeID: '1234']]]]],false) 55 | } 56 | mockReportModel.demand.getText { 'Title' } 57 | mockReportModel.use { 58 | assert '

    Title

    Group1

    • item1
      abc
    ' == ClipBoardUtil.extractHtml([type: 'test_type', groups: [ 59 | [title: 'Group1', items: [[action: 'item1', notes:'abc', nodeID: '1234']]]]],true) 60 | } 61 | mockReportModel.demand.getText { 'Title' } 62 | mockReportModel.use { 63 | assert '

    Title

    Group1

    • item1
    • item2 [who] {when} for project @context
    ' == ClipBoardUtil.extractHtml([type: 'test_type', groups: [ 64 | [title: 'Group1', 65 | items: [[action: 'item1', nodeID: '1234'], 66 | [action: 'item2', nodeID: '1234', who: 'who', when: 'when', context: 'context', project: 'project']]]]],true) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /harness/src/freeeplaneHarness/ReportWindowCreator.groovy: -------------------------------------------------------------------------------- 1 | package freeeplaneHarness 2 | 3 | import freeplaneGTD.ReportWindow 4 | import groovy.swing.SwingBuilder 5 | import groovy.util.logging.Log 6 | import org.freeplane.core.resources.ResourceBundles 7 | import org.freeplane.features.mode.Controller 8 | import org.freeplane.main.application.ApplicationResourceController 9 | import org.freeplane.plugin.script.FreeplaneScriptBaseClass 10 | import org.freeplane.plugin.script.proxy.Proxy 11 | import org.knopflerfish.framework.FrameworkContext 12 | import org.knopflerfish.framework.ServiceURLStreamHandlerFactory 13 | 14 | import javax.swing.AbstractAction 15 | import javax.swing.JFrame 16 | import java.awt.BorderLayout 17 | import java.awt.Dimension 18 | import java.awt.event.ActionEvent 19 | 20 | // 21 | //@Grapes([ 22 | // @Grab(group = 'ch.qos.logback', module = 'logback-classic', version = '1.0.13') 23 | //]) 24 | 25 | @Log 26 | class ReportWindowCreator { 27 | static ReportWindow window 28 | 29 | public static void main(String[] args) { 30 | def properties = new FreeplaneScriptBaseClass.ConfigProperties() { 31 | Properties props = new Properties() 32 | String freeplaneUserDirectory = "/home/gpapp/.config/freeplane/1.7.x" 33 | 34 | void setProperty(String s, Object o) { 35 | this.props.setProperty(s, o.toString()) 36 | } 37 | 38 | @Override 39 | boolean getBooleanProperty(String name) { 40 | log.info("Getting boolean " + name) 41 | Boolean.getBoolean(this.props.getProperty(name)) 42 | } 43 | 44 | @Override 45 | int getIntProperty(String name, int defaultValue = 0) { 46 | log.info("Getting int " + name) 47 | Integer.parseInt(this.props.getProperty(name)) 48 | } 49 | 50 | @Override 51 | String getProperty(String name, String defaultValue = null) { 52 | log.info("Getting String " + name) 53 | this.props.getOrDefault(name, defaultValue) 54 | } 55 | 56 | @Override 57 | String getFreeplaneUserDirectory() { 58 | this.freeplaneUserDirectory 59 | } 60 | } 61 | properties.setProperty("", 500); 62 | properties.setProperty(ReportWindow.FREEPLANE_GTD_DEFAULT_VIEW, ReportWindow.VIEW.CONTEXT.name()); 63 | JFrame myFrame 64 | Proxy.Map map 65 | def resourceController = new ApplicationResourceController() 66 | Controller.currentController = new MyController(resourceController) 67 | ((ResourceBundles) resourceController.resources). 68 | addResources("hu", new URL("file:/home/gpapp/IdeaProjects/FreePlaneGTD/src/resources/translations/hu.properties")) 69 | 70 | ServiceURLStreamHandlerFactory streamHandlerFactory = new ServiceURLStreamHandlerFactory() 71 | URL.setURLStreamHandlerFactory(streamHandlerFactory) 72 | streamHandlerFactory.addFramework(new FrameworkContext([ 73 | "org.osgi.framework.security": "", 74 | "org.osgi.framework.storage" : "/usr/share/freeplane/fwdir"])) 75 | (Controller.currentController as MyController).start() 76 | map = Controller.currentController.newMap() 77 | addNode(map, "ID__1", "*test task 1") 78 | addNode(map, "ID__2", "*test task 2 {2017-06-19}") 79 | addNode(map, "ID__3", "*test task 3 #3 {2017-06-19} @Context1 @Context2") 80 | 81 | ReportWindow reportWindow = ReportWindow.instance 82 | reportWindow.show(properties) 83 | reportWindow.refresh() 84 | 85 | } 86 | 87 | private static void addNode(Proxy.Map map, String id, String text) { 88 | Proxy.Node child = map.root.createChild() 89 | child.text = text 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /doc/GTD_template.mm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/listener/GTDNodeChangeListener.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.listener 2 | 3 | import freeplaneGTD.GTDMapReader 4 | import freeplaneGTD.GtdReportController 5 | import groovy.util.logging.Log 6 | import org.freeplane.api.Node 7 | import org.freeplane.features.attribute.NodeAttributeTableModel 8 | import org.freeplane.features.map.INodeChangeListener 9 | import org.freeplane.features.map.NodeModel 10 | import org.freeplane.features.map.NodeChangeEvent 11 | import org.freeplane.features.mode.Controller 12 | import org.freeplane.plugin.script.proxy.ScriptUtils 13 | 14 | import java.util.logging.Level 15 | 16 | @Log 17 | class GTDNodeChangeListener implements INodeChangeListener { 18 | boolean enabled 19 | GTDMapReader reader 20 | 21 | GTDNodeChangeListener() { 22 | enabled = true 23 | reader = GTDMapReader.instance 24 | reader.findIcons() 25 | } 26 | 27 | Node findById (def id) { 28 | try { 29 | return ScriptUtils.c().find({ Node it -> it.id === id })[0] 30 | } catch (Exception e) { 31 | throw e 32 | } 33 | } 34 | 35 | void nodeChanged(NodeChangeEvent event) { 36 | if (!event.setsDirtyFlag()) return 37 | if (!enabled) return 38 | try { 39 | boolean changed = true 40 | if (NodeModel.NODE_TEXT==event.property) { 41 | 42 | Node node=findById(event.node.createID()) 43 | if (GTDMapReader.isConfigAlias(node)) { 44 | reader.findAliases() 45 | reader.fixIconsOnAliasConfigChange() 46 | } else if (GTDMapReader.isConfigIcon(node)) { 47 | reader.findIcons() 48 | reader.fixIconsOnContextConfigChange() 49 | } else if (GTDMapReader.isShorthandQuestion(node)) { 50 | reader.parseSingleQuestionNode(node) 51 | } else if (GTDMapReader.isShorthandTask(node)) { 52 | reader.parseSingleTaskNode(node) 53 | reader.fixAliasesForNode(node) 54 | reader.fixIconsForNode(node) 55 | } else if (reader.isTask(node)) { 56 | reader.parseSingleTaskNode(node) 57 | reader.fixAliasesForNode(node) 58 | reader.fixIconsForNode(node) 59 | } else { 60 | changed = false 61 | } 62 | } else if (NodeModel.NODE_ICON == event.property) { 63 | Node node=findById(event.node.createID()) 64 | 65 | if (reader.isTask(node)) { 66 | reader.findIcons() 67 | // re-read icons on context change 68 | if (!event.oldValue && event.newValue) { 69 | reader.handleIconAdd(node, event.newValue.name) 70 | } else if (event.oldValue && !event.newValue) { 71 | reader.handleIconRemove(node, event.oldValue.name) 72 | } else if (event.oldValue && event.newValue) { 73 | reader.handleIconRemove(node, event.oldValue.name) 74 | reader.handleIconAdd(node, event.newValue.name) 75 | } else { 76 | changed = false 77 | } 78 | } else if (GTDMapReader.isConfigIcon(node) || GTDMapReader.isConfigAlias(node)) { 79 | if (node.icons.size() > 1 && !event.oldValue) { 80 | node.icons.remove(1) 81 | } else { 82 | try { 83 | reader.findIcons() 84 | reader.handleConfigIconChange(node, event.oldValue?.name, event.newValue?.name) 85 | } catch (Exception e) { 86 | node.icons.remove(0) 87 | reader.findIcons() 88 | ScriptUtils.c().statusInfo = e.message 89 | log.severe("Error caught:" + e.message) 90 | } 91 | } 92 | } 93 | } else changed = event.property == NodeAttributeTableModel.class 94 | /* TODO: 95 | event is fired on each node attribute modification with no before/after 96 | Will trigger multiple times on parsing string or action dialog 97 | Should find a way to reduce bogus events 98 | */ 99 | if (changed) { 100 | Node node=findById(event.node.createID()) 101 | 102 | if(node){ 103 | def when=node.attributes['When'] 104 | def whenDone=node.attributes['WhenDone'] 105 | 106 | if ((!whenDone) && (when instanceof java.util.Date)) { 107 | node.reminder.remove() 108 | def nextReminder = new Date().clearTime() + 1 109 | if (when.before(nextReminder)) { 110 | when = nextReminder 111 | } 112 | node.reminder.createOrReplace (when, "DAY",1) 113 | } else { 114 | node.reminder.remove() 115 | } 116 | Controller.currentModeController.getExtension(GtdReportController.getGtdReportControllerClass(Controller.currentModeController)). 117 | gtdReportViewController.refreshContent() 118 | } 119 | } 120 | 121 | } catch (Exception e) { 122 | // exceptions from a handler are destructive for freeplane 123 | // so handling everything here 124 | log.log(Level.SEVERE, "Error caught:" + e.message, e) 125 | } 126 | } 127 | 128 | void setEnabled(boolean b) { 129 | enabled = b 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FreePlaneGTD 2 | ============ 3 | 4 | Fork of Freeplane GTD with extensions 5 | 6 | The initial fork has been completely rewritten from original bits and pieces. 7 | 8 | ## About 9 | 10 | Freeplane is a widely used mind mapping tool. GTD is a time management methodology by David Allen. 11 | 12 | Freeplane GTD is an addon, that can collect tasks specified in a mindmap, thus allowing the users of Freeplane to use the concepts of GTD in their mind mapping tool. 13 | 14 | ## Installation and usage instructions 15 | 16 | To get more information about the add-on use follow the instructions at (http://www.itworks.hu/index.php/freeplane-gtd/). 17 | 18 | =================== 19 | 20 | *Please use the [latest documentation](http://www.itworks.hu/index.php/freeplane-gtd/)!* 21 | 22 | =================== 23 | ## Installation 24 | 25 | * Download and install [Freeplane](http://freeplane.org) on your computer. 26 | * Download the latest FreeplaneGTD plugin from the [release page](https://github.com/gpapp/FreePlaneGTD/releases ) 27 | * In Freeplane open the addon manager, select the previously downloaded addon, and install it. 28 | * Restart Freeplane to enable the plugin 29 | * You can now use the addon. 30 | 31 | *Please be aware, that the addon will replace any previous version of FreePlane GTD.* 32 | 33 | ## Usage 34 | 35 | ### Using the task overview 36 | 37 | Select Tools -> Freeplane GTD -> Next Actions from the menu. As of version 0.9 the addon can be invoked using the pre-defined hotkey Ctrl-H. 38 | 39 | freeplanegtdThe main window of the Freeplane GTD addon will contain all the action items (annotated with the corresponding icon) of the current map. 40 | 41 | You can close this window using the window buttons, or the ESC hotkey. 42 | 43 | The sidebar of the window contain the different groupings of the tasks. 44 | 45 | * By project – the actions will be grouped based on the projects found above them 46 | * By who – the actions will be grouped based on the Who attribute 47 | * By context – the actions will be grouped based on the Where attribute 48 | * By when – the actions will be grouped based on the When attribute, or the ‘Do next’ icon 49 | 50 | ### Shorthand notation 51 | 52 | In order to speed up the action entry, shorthand notation can be used to add new items. These shorthand notations will be parsed upon the display or the refresh of the task overview window. The parsed items will be annotated with the task icon and the extracted attributes will be added to the task. 53 | 54 | The format of the shorthand notation is as follows. Please note, that the position of the attributes do not matter. The first character of the notation must be an "*". 55 | 56 | Examples: 57 | `* Action name {when} @context [who] #priority` 58 | 59 | Will be converted to a node with attributes 60 | * Where - 'context' 61 | * When - 'when' 62 | * Who - 'who' 63 | * Priority - 'priority' (only accepts values 0 to 9) 64 | 65 | `* Increase default size of the GTD pane @Coding {v0.9}` 66 | 67 | Will be converted to a node with attributes 68 | * Where - Coding 69 | * When - v0.9 70 | 71 | ### Icons 72 | 73 | The plugin uses a number of pre-set icons to identify the action items, and their properties. These can be changed according to the users liking. 74 | 75 | * Icon: Project (default icon: 'list' looks like a checklist) – specifies the grouping node for actions 76 | * Icon: Next action (default icon: 'yes' - looks like !) – specifies the individual actions 77 | * Icon: Today (default icon: 'bookmark' - looks like a star) – specifies the next actions (When property will be ignored on grouping) 78 | * Icon: Week (default icon: 'idea' - looks like a lightbulb)– specifies the tasks until the end of the week (will be sorted accordingly) 79 | * Icon: Done (default icon: 'button_ok' - looks like a checkmark)– Once an item is completed it can be marked as done. 80 | * Icon: Cancel (default icon: 'button_cancel' - looks like a cross)– Once an item is cancelled it can be marked as cancelled. 81 | 82 | To customize these icons you must place a node with the text above anywhere in your map and add a desired icon to that node. I suggest you place it under a "Config" or "Settings" node in the root of the map, so it doesn't get mixed up with the actual map content. 83 | 84 | ## Using the editor 85 | 86 | The add-on comes with a simple task editor, that is mapped to the F4 key by default. This can be used to edit/set the task properties without having to use the property editor directy. 87 | 88 | The editor is also the place, where the WaitFor and WaitUntil properties can be edited for the tasks. These properties are relevant, when you want to override the ordering of certain tasks. Eg. if you know that a task to be performed by Alice is waiting for some other responsible Bob, than set WaitFor to Bob and set the responsible to Alice. This task will be present at both their names as responsible. 89 | 90 | ## Context icons 91 | 92 | If you want to add pre-defined icons for some contexts specified in your action nodes, it's available for versions over 1.0. To use this feature you must create icon nodes such as the ones above with the text `Icon: @Context` where the Context text can be replaced with the context of your liking. 93 | 94 | The icons specified will be converted to the corresponding context attribute (Where property). 95 | 96 | ## Aliases for people and contexts 97 | 98 | If you prefer to use shortcuts for people or contexts, it is available starting V2.0. All you have to do is to create nodes with the text `Alias: @Context @ctx` or `Alias: [John Connor] [jc] ` where the texts can be any string of your liking. 99 | 100 | The defined aliases will replace the text when node is parsed with the shorcuts or edited through the editor. 101 | 102 | Aliases are case insensitive and you can specify multiple aliases for the same context/people either on the same config line or separately. 103 | 104 | ## Credits 105 | 106 | The addon is based on the v0.8 FreeplaneGTD addon by Auxilus. 107 | 108 | ## Versions 109 | 110 | Versions can be found on the official [release page](https://www.itworks.hu/freeplanegtd-release) or on [GitHub release page](https://github.com/gpapp/FreePlaneGTD/releases) 111 | -------------------------------------------------------------------------------- /test/resources/Archive test.mm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/util/ClipBoardUtil.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.util 2 | 3 | import freeplaneGTD.GTDMapReader 4 | import org.freeplane.core.util.TextUtils 5 | import org.freeplane.features.format.FormattedDate 6 | import org.freeplane.features.format.FormattedNumber 7 | 8 | import java.awt.datatransfer.DataFlavor 9 | import java.awt.datatransfer.Transferable 10 | import java.awt.datatransfer.UnsupportedFlavorException 11 | 12 | class ClipBoardUtil { 13 | 14 | static class MyTransferable implements Transferable { 15 | 16 | private static final DataFlavor[] supportedFlavors = [ 17 | new DataFlavor("text/freeplane-nodes; class=java.lang.String"), 18 | new DataFlavor('text/html; class=java.lang.String'), 19 | new DataFlavor('text/plain; class=java.lang.String'), 20 | ] 21 | 22 | private final String freeplaneData 23 | private final String plainData 24 | private final String htmlData 25 | 26 | MyTransferable(String freeplaneData, String plainData, String htmlData) { 27 | this.freeplaneData = freeplaneData 28 | this.plainData = plainData 29 | this.htmlData = htmlData 30 | } 31 | 32 | DataFlavor[] getTransferDataFlavors() { 33 | return supportedFlavors 34 | } 35 | 36 | boolean isDataFlavorSupported(DataFlavor flavor) { 37 | for (DataFlavor supportedFlavor : supportedFlavors) { 38 | if (supportedFlavor == flavor) { 39 | return true 40 | } 41 | } 42 | return false 43 | } 44 | 45 | Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 46 | if (flavor == supportedFlavors[0]) { 47 | return freeplaneData 48 | } 49 | if (flavor == supportedFlavors[1]) { 50 | return htmlData 51 | } 52 | if (flavor == supportedFlavors[2]) { 53 | return plainData 54 | } 55 | throw new UnsupportedFlavorException(flavor) 56 | } 57 | } 58 | 59 | static Transferable createTransferable(Map content, GTDMapReader reader, boolean showNotes) { 60 | return new MyTransferable( 61 | extractFreeplane(content, reader), 62 | extractText(content), 63 | extractHtml(content,showNotes)) 64 | } 65 | 66 | static String contextsToList(String context) { 67 | String[] list = context.split(',') 68 | return ' @' + list.join(' @') 69 | } 70 | 71 | static String extractFreeplane(Map content, GTDMapReader reader) { 72 | Tag body = new Tag('node', [TEXT: TextUtils.getText('freeplaneGTD_view_' + content['type'])]) 73 | content['groups'].each { 74 | Tag curItem = body.addChild('node', [TEXT: it['title']]) 75 | it['items'].each { 76 | Tag node = curItem.addChild('node', [TEXT: it['action']]) 77 | node.addChild('icon', [BUILTIN: reader.iconNextAction]) 78 | it['done'] ? node.addChild('icon', [BUILTIN: reader.iconDone]) : false 79 | it['context'] ? node.addChild('attribute', [NAME: 'Where', VALUE: it['context']]) : false 80 | if (it['when']) { 81 | Tag attr = node.addChild('attribute', [ 82 | NAME : 'When', 83 | VALUE: it['when']]) 84 | if (it['when'] instanceof FormattedDate) { 85 | attr.addProperty('OBJECT', 'org.freeplane.features.format.FormattedDate|' + it['when'] + '|yyyy-MM-dd') 86 | } 87 | } 88 | it['who'] ? node.addChild('attribute', [NAME: 'Who', VALUE: it['who']]) : false 89 | if (it['priority']) { 90 | Tag attr = node.addChild('attribute', [NAME : 'Priority', 91 | VALUE: it['priority'] 92 | ]) 93 | if (it['priority'] instanceof FormattedNumber) { 94 | attr.addProperty('OBJECT', 'org.freeplane.features.format.FormattedNumber|' + it['priority']) 95 | } 96 | } 97 | it['details'] ? node.addChild('richcontent', [TYPE: 'DETAILS']).addPreformatted((String) it['details']) : false 98 | it['notes'] ? node.addChild('richcontent', [TYPE: 'NOTE']).addPreformatted((String) it['notes']) : false 99 | } 100 | } 101 | return body.toString() 102 | } 103 | 104 | static String extractText(Map content) { 105 | def list = [] 106 | list << TextUtils.getText('freeplaneGTD_view_' + content['type']) 107 | content['groups'].each { 108 | list << '\t' + it['title'] 109 | it['items'].each { 110 | list << '\t\t*' + (it['priority'] ? ' #' + it['priority'] : '') + ' ' + it['action'] + 111 | (it['who'] ? ' [' + it['who'] + ']' : '') + 112 | (it['when'] ? ' {' + it['when'] + '}' : '') + 113 | (it['project'] ? ' for ' + it['project'] : '') + 114 | (it['context'] ? contextsToList((String) it['context']) : '') 115 | } 116 | } 117 | return list.join('\n') 118 | } 119 | 120 | static String extractHtml(Map content, boolean showNotes) { 121 | Tag body = new Tag('body') 122 | body.addContent('h1', TextUtils.getText('freeplaneGTD_view_' + content['type'])) 123 | content['groups'].each { 124 | body.addContent('h2', it['title']) 125 | Tag curItem = body.addChild('ul') 126 | it['items'].each { 127 | Tag wrap = curItem.addChild('li') 128 | if (it['done']) wrap.params = [style: 'text-decoration: line-through'] 129 | if (it['priority']) { 130 | wrap = wrap.addContent('span', it['priority'] + ' ', [style: 'color:red']) 131 | } 132 | wrap.addContent(it['action'] + 133 | (it['who'] ? ' [' + it['who'] + ']' : '') + 134 | (it['when'] ? ' {' + it['when'] + '}' : '') + 135 | (it['project'] ? ' for ' + it['project'] : '') + 136 | (it['context'] ? contextsToList((String) it['context']) : '') 137 | ) 138 | if (showNotes && (it['details'] || it['notes'])) { 139 | Tag tag = new Tag('div',) 140 | if (it['details']) { 141 | tag.addChild('div', [style: 'background-color: rgb(240,250,240);font-size:10pt']).addPreformatted((String) it['details']) 142 | } 143 | if (it['notes']) { 144 | tag.addChild('div', [style: 'background-color: rgb(250,250,240);font-size:10pt']).addPreformatted((String) it['notes']) 145 | } 146 | wrap.addContent(tag) 147 | } 148 | } 149 | } 150 | return body.toString() 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /test/lib/freeplaneGTD/GTDMapReaderTest.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | 3 | import org.freeplane.features.map.MapModel 4 | import org.freeplane.features.map.NodeModel 5 | import org.freeplane.plugin.script.ScriptContext 6 | import org.freeplane.plugin.script.proxy.Proxy 7 | import org.freeplane.plugin.script.proxy.ProxyFactory 8 | import org.junit.Test 9 | 10 | class GTDMapReaderTest { 11 | 12 | @Test 13 | void testParseShortHandSimple() { 14 | assert ([action: 'a b c'] == GTDMapReader.parseShorthand('*a b c')) 15 | assert ([action: 'a b c'] == GTDMapReader.parseShorthand(' *a b c')) 16 | assert ([action: 'a b c'] == GTDMapReader.parseShorthand(' * a b c')) 17 | } 18 | 19 | @Test 20 | void testParseShortHandNow() { 21 | assert ([action: 'a b c', when: 'now'] == GTDMapReader.parseShorthand('*a b c {now}')) 22 | assert ([action: 'a b c', when: 'now'] == GTDMapReader.parseShorthand(' { now }*a b c')) 23 | assert ([action: 'a b c', when: 'now'] == GTDMapReader.parseShorthand('*a {now} b c')) 24 | } 25 | 26 | @Test 27 | void testParseShortHandDate() { 28 | assert ([action: 'a b c', when: Date.parse('yyyy-MM-dd', '2003-01-02')] == GTDMapReader.parseShorthand('*a b c {1/2/3}')) 29 | assert ([action: 'a b c', when: Date.parse('yyyy-MM-dd', '2014-11-24')] == GTDMapReader.parseShorthand(' { 14-11-24 }*a b c')) 30 | assert ([action: 'a b c', when: Date.parse('yyyy-MM-dd', '2014-11-24')] == GTDMapReader.parseShorthand('*a {2014-11-24} b c')) 31 | } 32 | 33 | @Test 34 | void testParseShortSingleDelegate() { 35 | assert ([action: 'a b c', when: 'now', delegate: 'Joe'] == GTDMapReader.parseShorthand('*a b c {now} [Joe]')) 36 | assert ([action: 'a b c', when: 'now', delegate: 'Joe'] == GTDMapReader.parseShorthand(' [Joe]{ now }*a b c')) 37 | assert ([action: 'a b c', when: 'now', delegate: 'Joe'] == GTDMapReader.parseShorthand('*a {now}[ Joe ] b c')) 38 | } 39 | 40 | @Test 41 | void testParseShortHandSingleContext() { 42 | assert ([action: 'a b c', when: 'now', delegate: 'Joe', context: 'Home'] == GTDMapReader.parseShorthand('*a b c {now}@Home [Joe]')) 43 | assert ([action: 'a b c', when: 'now', delegate: 'Joe', context: 'Home'] == GTDMapReader.parseShorthand(' [Joe]{ now }*a b @Home c')) 44 | assert ([action: 'a b c', when: 'now', delegate: 'Joe', context: 'Home'] == GTDMapReader.parseShorthand('@Home*a {now}[ Joe ] b c')) 45 | } 46 | 47 | @Test 48 | void testParseShortHandSinglePriority() { 49 | assert ([action: 'a b c', when: 'now', delegate: 'Joe', context: 'Home', priority: '2'] == GTDMapReader.parseShorthand('*a b c {now}@Home#2 [Joe]')) 50 | assert ([action: 'a b c', when: 'now', delegate: 'Joe', context: 'Home', priority: '4'] == GTDMapReader.parseShorthand(' [Joe]{ now }*a b #4@Home c')) 51 | assert ([action: 'a b c', when: 'now', delegate: 'Joe', context: 'Home', priority: '6'] == GTDMapReader.parseShorthand('#6 @Home*a {now}[ Joe ] b c')) 52 | } 53 | 54 | @Test 55 | void testParseShortHandMultiContext() { 56 | assert ([action: 'a b c', when: 'now', delegate: 'John,Ringo,George', context: 'Home,Office'] == GTDMapReader.parseShorthand('*a b c {now}@Home,Office [John,Ringo,George]')) 57 | assert ([action: 'a b c', when: 'now', delegate: 'John,Ringo,George', context: 'Home,Barn,Office'] == GTDMapReader.parseShorthand(' [John][ Ringo ][ George]{ now }*a b @Home@Barn @Office c')) 58 | assert ([action: 'a b c', when: 'now', delegate: 'John,Ringo,George', context: 'Home,Office'] == GTDMapReader.parseShorthand('@Home[John]*a {now}[ John ,Ringo] b c@Office[George]')) 59 | 60 | } 61 | 62 | @Test 63 | void testConvertShortHand() { 64 | Proxy.Node rootNode = ProxyFactory.createNode(new NodeModel(new MapModel()),new ScriptContext()) 65 | rootNode.icons.add('aa') 66 | rootNode.text = 'aa' 67 | Proxy.Node child1 = rootNode.createChild() 68 | child1.text = 'bb' 69 | Proxy.Node child2 = rootNode.createChild() 70 | child2.text = 'cc' 71 | Proxy.Node child3 = rootNode.createChild() 72 | child3.text = 'dd' 73 | assert rootNode.text == 'aa' 74 | assert child1.text == 'bb' 75 | assert child2.text == 'cc' 76 | assert child3.text == 'dd' 77 | 78 | Proxy.Node configNode = rootNode.createChild() 79 | configNode.text = 'Config' 80 | List config = [ 81 | [text: 'Icon: Project', icon: 'project'], 82 | [text: 'Icon: Next action', icon: 'action'], 83 | [text: 'Icon: Done', icon: 'done'], 84 | [text: 'Icon: Today', icon: 'today'], 85 | [text: 'Icon: @Home', icon: 'home'], 86 | [text: 'Icon: @Email', icon: 'email'], 87 | ] 88 | config.each { 89 | Proxy.Node n = configNode.createChild() 90 | n.text = it['text'] 91 | n.icons.add(it['icon']) 92 | } 93 | GTDMapReader mapreader = GTDMapReader.instance 94 | assert mapreader != null 95 | mapreader.convertShorthand(rootNode) 96 | assert 'project' == mapreader.iconProject 97 | assert 'action' == mapreader.iconNextAction 98 | assert 'done' == mapreader.iconDone 99 | assert 'today' == mapreader.iconToday 100 | assert mapreader.contextIcons.containsKey('Home') 101 | assert mapreader.contextIcons.containsValue('home') 102 | assert mapreader.contextIcons.containsKey('Email') 103 | assert mapreader.contextIcons.containsValue('email') 104 | 105 | 106 | List testvalues = 107 | [ 108 | [orig: '*a b c', action: 'a b c'], 109 | [orig: ' *a b c', action: 'a b c'], 110 | [orig: ' * a b c', action: 'a b c'], 111 | [orig: '*a {now} b c', action: 'a b c', attr: [When: 'now']], 112 | [orig: '*a {2014-11-24} b c', action: 'a b c', attr: [When: Date.parse('yyyy-MM-dd', '2014-11-24')]], 113 | [orig: '*a {2014-11-24} [Joe]b c', action: 'a b c', attr: [When: Date.parse('yyyy-MM-dd', '2014-11-24'), Who: 'Joe']], 114 | [orig: '*a {2014-11-24} [Joe]b @Home c', action: 'a b c', attr: [When: Date.parse('yyyy-MM-dd', '2014-11-24'), Who: 'Joe', Where: 'Home'], icon: ['home']], 115 | [orig: '*@Home[John]a @Email {now}[ John ,Ringo] b c@Office[George]', action: 'a b c', attr: [When: 'now', Who: 'John,Ringo,George', Where: 'Home,Email,Office'], icon: ['home', 'email']], 116 | [orig: '*@Home[John]a @Email {now}[ John ,Ringo] @Email b c@Office[George]', action: 'a b c', attr: [When: 'now', Who: 'John,Ringo,George', Where: 'Home,Email,Office'], icon: ['home', 'email']], 117 | [orig: '*Killer node <>', action: 'Killer node <>'], 118 | ] 119 | 120 | testvalues.each { 121 | Proxy.Node n = child1.createChild() 122 | n.text = it['orig'] 123 | mapreader.convertShorthand(rootNode) 124 | assert n.text == it['action'] 125 | it['attr'].each { 126 | key, value -> assert n.attributes.getFirst(key) == value 127 | } 128 | it['icon'].each { 129 | assert n.icons.contains(it) 130 | } 131 | } 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/util/DateUtil.groovy: -------------------------------------------------------------------------------- 1 | //========================================================= 2 | // Freeplane GTD 3 | // 4 | // Utility classes for parsing dates, etc. 5 | // 6 | // Version 1.0-beta 7 | // 8 | // Copyright (c)2014 Gergely Papp 9 | // 10 | // This program is free software: you can redistribute it and/or modify 11 | // it under the terms of the GNU General Public License as published by 12 | // the Free Software Foundation, either version 3 of the License, or 13 | // any later version. 14 | // 15 | // This program is distributed in the hope that it will be useful, 16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | // GNU General Public License for more details. 19 | // 20 | // You should have received a copy of the GNU General Public License 21 | // along with this program. If not, see . 22 | // 23 | //========================================================= 24 | package freeplaneGTD.util 25 | 26 | import org.freeplane.features.format.FormattedDate 27 | import org.freeplane.plugin.script.proxy.Convertible 28 | import org.freeplane.plugin.script.proxy.ConvertibleDate 29 | 30 | import java.text.SimpleDateFormat 31 | 32 | import static java.util.Calendar.YEAR 33 | 34 | class DateUtil { 35 | public static final SimpleDateFormat stdFormat = new SimpleDateFormat('yyyy-MM-dd') 36 | 37 | private static final def DATE_FORMAT_REGEXPS = [ 38 | // System date parsing 39 | '^\\d{4}-\\d{1,2}-\\d{1,2}t\\d{1,2}:\\d{2}\\+\\d{4}$' : new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mmZ'), 40 | '^\\d{4}-\\d{1,2}-\\d{1,2}t\\d{1,2}:\\d{2}:\\d{2}\\+\\d{4}$': new SimpleDateFormat('yyyy-MM-dd\'T\'HH:mm:ssZ'), 41 | '^\\d{8}$' : new SimpleDateFormat('yyyyMMdd'), 42 | // Short date parsing 43 | '^\\d{1,2}\\ \\d{1,2}$' : new SimpleDateFormat('MM dd'), 44 | '^\\d{1,2}\\/\\d{1,2}$' : new SimpleDateFormat('MM/dd'), 45 | '^\\d{1,2}\\.\\d{1,2}$' : new SimpleDateFormat('MM.dd'), 46 | '^\\d{1,2}\\.\\d{1,2}\\.$' : new SimpleDateFormat('MM.dd.'), 47 | // Medium date parsing 48 | '^\\d{1,2}-\\d{1,2}-\\d{1,2}$' : new SimpleDateFormat('yy-MM-dd'), 49 | '^\\d{1,2}\\ \\d{1,2}\\ \\d{1,2}$' : new SimpleDateFormat('yy MM dd'), 50 | '^\\d{1,2}\\/\\d{1,2}\\/\\d{1,2}$' : new SimpleDateFormat('MM/dd/yy'), 51 | '^\\d{1,2}\\.\\d{1,2}\\.\\d{1,2}$' : new SimpleDateFormat('yy.MM.dd'), 52 | '^\\d{1,2}\\.\\d{1,2}\\.\\d{1,2}\\.$' : new SimpleDateFormat('yy.MM.dd.'), 53 | // Date parsing 54 | '^\\d{1,2}-\\d{1,2}-\\d{4}$' : new SimpleDateFormat('dd-MM-yyyy'), 55 | '^\\d{4}-\\d{1,2}-\\d{1,2}$': stdFormat, 56 | '^\\d{1,2}/\\d{1,2}/\\d{4}$' : new SimpleDateFormat('MM/dd/yyyy'), 57 | '^\\d{4}/\\d{1,2}/\\d{1,2}$' : new SimpleDateFormat('yyyy/MM/dd'), 58 | '^\\d{4}\\.\\d{1,2}\\.\\d{1,2}$' : new SimpleDateFormat('yyyy.MM.dd'), 59 | '^\\d{4}\\.\\d{1,2}\\.\\d{1,2}\\.$' : new SimpleDateFormat('yyyy.MM.dd.'), 60 | '^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$' : new SimpleDateFormat('dd MMM yyyy'), 61 | '^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$' : new SimpleDateFormat('dd MMMM yyyy'), 62 | // Timestamp parsing 63 | '^\\d{12}$' : new SimpleDateFormat('yyyyMMddHHmm'), 64 | '^\\d{8}\\s\\d{4}$' : new SimpleDateFormat('yyyyMMdd HHmm'), 65 | '^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$' : new SimpleDateFormat('dd-MM-yyyy HH:mm'), 66 | '^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$' : new SimpleDateFormat('yyyy-MM-dd HH:mm'), 67 | '^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$' : new SimpleDateFormat('MM/dd/yyyy HH:mm'), 68 | '^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$' : new SimpleDateFormat('yyyy/MM/dd HH:mm'), 69 | '^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$' : new SimpleDateFormat('dd MMM yyyy HH:mm'), 70 | '^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$' : new SimpleDateFormat('dd MMMM yyyy HH:mm'), 71 | '^\\d{14}$' : new SimpleDateFormat('yyyyMMddHHmmss'), 72 | '^\\d{8}\\s\\d{6}$' : new SimpleDateFormat('yyyyMMdd HHmmss'), 73 | '^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$' : new SimpleDateFormat('dd-MM-yyyy HH:mm:ss'), 74 | '^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$' : new SimpleDateFormat('yyyy-MM-dd HH:mm:ss'), 75 | '^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$' : new SimpleDateFormat('MM/dd/yyyy HH:mm:ss'), 76 | '^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$' : new SimpleDateFormat('yyyy/MM/dd HH:mm:ss'), 77 | '^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$' : new SimpleDateFormat('dd MMM yyyy HH:mm:ss'), 78 | '^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$' : new SimpleDateFormat('dd MMMM yyyy HH:mm:ss'), 79 | ] 80 | 81 | /** 82 | * Determine SimpleDateFormat pattern matching with the given date string. Returns null if 83 | * format is unknown. You can simply extend DateUtil with more formats if needed. 84 | * @param dateString The date string to determine the SimpleDateFormat pattern for. 85 | * @return The matching SimpleDateFormat pattern, or null if format is unknown. 86 | * @see SimpleDateFormat 87 | */ 88 | static SimpleDateFormat determineDateFormat(String dateString) { 89 | for (key in DATE_FORMAT_REGEXPS.keySet()) { 90 | if (dateString.toLowerCase().matches(key)) { 91 | return DATE_FORMAT_REGEXPS[key] 92 | } 93 | } 94 | return null // Unknown format. 95 | } 96 | 97 | 98 | static Object normalizeDate(Date date) { 99 | return date 100 | } 101 | 102 | static Object normalizeDate(String dateString) { 103 | String field = dateString.trim() 104 | SimpleDateFormat fmt = determineDateFormat(field) 105 | if (fmt != null) { 106 | Date date = fmt.parse(dateString) 107 | if (fmt.toPattern().indexOf('y') < 0) { 108 | Date now = new Date() 109 | date[YEAR] = now[YEAR] 110 | } 111 | if (date[YEAR] < 100) { 112 | date[YEAR] += 2000 113 | } 114 | return new FormattedDate(date.getTime(), stdFormat) 115 | } else { 116 | return field 117 | } 118 | } 119 | 120 | static Object normalizeDate(ConvertibleDate date) { 121 | date.date 122 | } 123 | 124 | static Object normalizeDate(Convertible date) { 125 | normalizeDate(date.text) 126 | } 127 | 128 | static FormattedDate getFormattedDate() { 129 | new FormattedDate(new Date().getTime(), stdFormat) 130 | } 131 | } -------------------------------------------------------------------------------- /doc/GTD with Freeplane.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Getting Things Done using Freeplane GTD+ 5 | 6 | 19 | 20 | 21 | 253 | 255 | 258 | 259 | -------------------------------------------------------------------------------- /src/zips/templates/GTD_template.mm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |

    69 | Hover the mouse over the items for help. 70 |

    71 | 72 | 73 | 74 |
    75 | 76 | 77 | 78 | 97 | 98 | 99 | 100 | 101 | 126 | 127 | 128 | 129 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 247 | 248 | 249 | 250 | 251 | 276 | 277 | 278 | 279 | 280 | 305 | 306 |
    307 |
    308 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/editor/ActionEditor.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD.editor 2 | 3 | import freeplaneGTD.GTDMapReader 4 | import freeplaneGTD.GtdReportController 5 | import freeplaneGTD.util.DateUtil 6 | import freeplaneGTD.util.IconUtil 7 | import groovy.swing.SwingBuilder 8 | import org.freeplane.api.Node 9 | import org.freeplane.core.ui.components.UITools 10 | import org.freeplane.core.util.TextUtils 11 | import org.freeplane.features.mode.Controller 12 | 13 | import javax.swing.* 14 | import java.awt.* 15 | import java.awt.event.ActionEvent 16 | import java.awt.event.KeyEvent 17 | 18 | class ActionEditor { 19 | class ActionEditorModel { 20 | String action 21 | String delegate 22 | String context 23 | boolean today 24 | String when 25 | String priority 26 | String waitFor 27 | String waitUntil 28 | boolean done 29 | boolean cancelled 30 | 31 | Node editedNode 32 | 33 | boolean setNode(Node node) { 34 | this.editedNode = node 35 | GTDMapReader mapReader = GTDMapReader.instance 36 | if (GTDMapReader.isShorthandTask(node)) { 37 | mapReader.parseSingleTaskNode(node) 38 | } 39 | mapReader.findIcons() 40 | if (!mapReader.isTask(node)) { 41 | UITools.errorMessage('Selected node is not a task') 42 | return false 43 | } 44 | action = node.text 45 | delegate = node.attributes['Who']?.replaceAll(',', ', ') 46 | context = node.attributes['Where']?.replaceAll(',', ', ') 47 | today = node.icons.contains(GTDMapReader.instance.iconToday) 48 | when = node.attributes['When'] 49 | priority = node.attributes['Priority'] 50 | waitFor = node.attributes['WaitFor']?.replaceAll(',', ', ') 51 | waitUntil = node.attributes['WaitUntil'] 52 | done = node.icons.contains(GTDMapReader.instance.iconDone) 53 | cancelled = node.icons.contains(GTDMapReader.instance.iconCancel) 54 | return true 55 | } 56 | 57 | void updateNode() { 58 | editedNode.text = "$action" 59 | delegate ? editedNode['Who'] = delegate : editedNode.attributes.removeAll('Who') 60 | context ? editedNode['Where'] = context : editedNode.attributes.removeAll('Where') 61 | when ? editedNode['When'] = when : editedNode.attributes.removeAll('When') 62 | priority ? editedNode['Priority'] = priority : editedNode.attributes.removeAll('Priority') 63 | 64 | if (waitFor) { 65 | editedNode.attributes.set('WaitFor', waitFor.split(',')*.trim().unique({ a, b -> a.toLowerCase() <=> b.toLowerCase() }).join(',')) 66 | } else 67 | editedNode.attributes.removeAll('WaitFor') 68 | 69 | if (waitUntil) { 70 | def waitUntilDate = DateUtil.normalizeDate(waitUntil) 71 | editedNode.attributes.set('WaitUntil', waitUntilDate) 72 | } else 73 | editedNode.attributes.removeAll('WaitUntil') 74 | 75 | GTDMapReader mapReader = GTDMapReader.instance 76 | if (editedNode.icons.contains(mapReader.iconToday) != today) { 77 | if (!today) { 78 | editedNode.icons.remove(mapReader.iconToday) 79 | } else { 80 | editedNode.icons.add(mapReader.iconToday) 81 | } 82 | } 83 | if (editedNode.icons.contains(mapReader.iconDone) != done) { 84 | if (!done) { 85 | editedNode.icons.remove(mapReader.iconDone) 86 | } else { 87 | editedNode.icons.add(mapReader.iconDone) 88 | } 89 | } 90 | if (editedNode.icons.contains(mapReader.iconCancel) != cancelled) { 91 | if (!cancelled) { 92 | editedNode.icons.remove(mapReader.iconCancel) 93 | } else { 94 | editedNode.icons.add(mapReader.iconCancel) 95 | } 96 | } 97 | // Find icons in the entire map 98 | mapReader.findIcons() 99 | 100 | // Re-parse map 101 | mapReader.fixAliasesForNode(editedNode) 102 | mapReader.fixIconsForNode(editedNode) 103 | 104 | Controller.currentModeController.getExtension(GtdReportController.getGtdReportControllerClass(Controller.currentModeController)). 105 | gtdReportViewController.refreshContent() 106 | } 107 | } 108 | ActionEditorModel model = new ActionEditorModel() 109 | 110 | JDialog mainFrame 111 | JTextField actionField 112 | JTextField delegateField 113 | JTextField contextField 114 | JCheckBox todayField 115 | JTextField whenField 116 | JTextField priorityField 117 | JCheckBox doneField 118 | JCheckBox cancelledField 119 | JTextField waitForField 120 | JTextField waitUntilField 121 | JButton doneButton 122 | 123 | ActionEditor() { 124 | SwingBuilder.edtBuilder { 125 | mainFrame = dialog( 126 | title: TextUtils.getText("freeplaneGTD.actioneditor.title"), 127 | defaultCloseOperation: JFrame.DISPOSE_ON_CLOSE, 128 | show: false, 129 | modal: true) { 130 | boxLayout(axis: BoxLayout.Y_AXIS) 131 | panel(border: BorderFactory.createEmptyBorder(10, 10, 10, 10)) { 132 | gridBagLayout() 133 | label(text: TextUtils.getText("freeplaneGTD.actioneditor.action"), 134 | constraints: gbc(gridx: 0, gridy: 0, ipadx: 5, fill: HORIZONTAL)) 135 | actionField = textField(preferredSize: new Dimension(400, 25), 136 | constraints: gbc(gridx: 1, gridy: 0, gridwidth: REMAINDER, fill: HORIZONTAL)) 137 | 138 | label(text: TextUtils.getText("freeplaneGTD.actioneditor.delegate"), 139 | constraints: gbc(gridx: 0, gridy: 1, ipadx: 5, fill: HORIZONTAL)) 140 | delegateField = textField(preferredSize: new Dimension(300, 25), 141 | constraints: gbc(gridx: 1, gridy: 1, gridwidth: REMAINDER, fill: HORIZONTAL)) 142 | 143 | label(text: TextUtils.getText("freeplaneGTD.actioneditor.context"), 144 | constraints: gbc(gridx: 0, gridy: 2, ipadx: 5, fill: HORIZONTAL)) 145 | contextField = textField(preferredSize: new Dimension(300, 25), 146 | constraints: gbc(gridx: 1, gridy: 2, gridwidth: REMAINDER, fill: HORIZONTAL)) 147 | 148 | label(text: TextUtils.getText("freeplaneGTD.actioneditor.when"), 149 | constraints: gbc(gridx: 0, gridy: 3, ipadx: 5, fill: HORIZONTAL)) 150 | todayField = checkBox(text: TextUtils.getText("freeplaneGTD.actioneditor.today"), 151 | preferredSize: new Dimension(50, 25), 152 | constraints: gbc(gridx: 1, gridy: 3, ipadx: 5)) 153 | whenField = textField(preferredSize: new Dimension(250, 25), 154 | constraints: gbc(gridx: 2, gridy: 3, fill: HORIZONTAL)) 155 | hbox(constraints: gbc(gridx: 3, gridy: 3, fill: HORIZONTAL)) { 156 | doneField = checkBox( 157 | text: TextUtils.getText("freeplaneGTD.actioneditor.done"), 158 | icon: IconUtil.getIcon("unchecked"), 159 | selectedIcon: IconUtil.getIcon("button_ok"), 160 | actionPerformed: { 161 | if (doneField.selected && cancelledField.selected) { 162 | cancelledField.selected = false 163 | } 164 | }) 165 | cancelledField = checkBox( 166 | text: TextUtils.getText("freeplaneGTD.actioneditor.cancelled"), 167 | icon: IconUtil.getIcon("unchecked"), 168 | selectedIcon: IconUtil.getIcon("button_cancel"), 169 | actionPerformed: { 170 | if (doneField.selected && cancelledField.selected) { 171 | doneField.selected = false 172 | } 173 | }) 174 | } 175 | 176 | label(text: TextUtils.getText("freeplaneGTD.actioneditor.waitFor"), 177 | constraints: gbc(gridx: 0, gridy: 4, ipadx: 5, fill: HORIZONTAL)) 178 | waitForField = textField(preferredSize: new Dimension(250, 25), 179 | constraints: gbc(gridx: 1, gridy: 4, gridwidth: REMAINDER, fill: HORIZONTAL)) 180 | 181 | label(text: TextUtils.getText("freeplaneGTD.actioneditor.waitUntil"), 182 | constraints: gbc(gridx: 0, gridy: 5, ipadx: 5, fill: HORIZONTAL)) 183 | waitUntilField = textField(preferredSize: new Dimension(250, 25), 184 | constraints: gbc(gridx: 1, gridy: 5, gridwidth: REMAINDER, fill: HORIZONTAL)) 185 | 186 | label(text: TextUtils.getText("freeplaneGTD.actioneditor.priority"), 187 | constraints: gbc(gridx: 0, gridy: 6, ipadx: 5, fill: HORIZONTAL)) 188 | priorityField = textField(preferredSize: new Dimension(20, 25), 189 | constraints: gbc(gridx: 1, gridy: 6, gridwidth: REMAINDER, fill: HORIZONTAL)) 190 | } 191 | 192 | panel() { 193 | boxLayout(axis: BoxLayout.X_AXIS) 194 | button(text: TextUtils.getText("freeplaneGTD.button.cancel"), 195 | actionPerformed: { 196 | mainFrame.setVisible(false) 197 | mainFrame.dispose() 198 | }) 199 | doneButton = button(id: 'doneButton', text: TextUtils.getText("freeplaneGTD.button.done"), 200 | actionPerformed: { 201 | model.action = actionField.text 202 | model.delegate = delegateField.text 203 | model.context = contextField.text 204 | model.today = todayField.selected 205 | model.when = whenField.text 206 | model.priority = priorityField.text 207 | model.waitFor = waitForField.text 208 | model.waitUntil = waitUntilField.text 209 | model.done = doneField.selected 210 | model.cancelled = cancelledField.selected 211 | model.updateNode() 212 | mainFrame.setVisible(false) 213 | mainFrame.dispose() 214 | }) 215 | } 216 | } 217 | mainFrame.getRootPane().setDefaultButton(doneButton) 218 | } 219 | // on ESC key close frame 220 | mainFrame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 221 | KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), TextUtils.getText("freeplaneGTD.button.cancel")) 222 | mainFrame.getRootPane().getActionMap().put(TextUtils.getText("freeplaneGTD.button.cancel"), 223 | new AbstractAction() { 224 | void actionPerformed(ActionEvent e) { 225 | mainFrame.setVisible(false) 226 | mainFrame.dispose() 227 | } 228 | }) 229 | } 230 | 231 | void editNode(Node node) { 232 | if (!model.setNode(node)) { 233 | return 234 | } 235 | actionField.text = model.action 236 | delegateField.text = model.delegate 237 | contextField.text = model.context 238 | todayField.selected = model.today 239 | whenField.text = model.when 240 | priorityField.text = model.priority 241 | waitForField.text = model.waitFor 242 | waitUntilField.text = model.waitUntil 243 | doneField.selected = model.done 244 | cancelledField.selected = model.cancelled 245 | mainFrame.pack() 246 | mainFrame.setLocationRelativeTo(UITools.frame) 247 | mainFrame.setVisible(true) 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /test/resources/freeplaneGTD-test-gtd.mm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |

    68 | *a 69 |

    70 |

    71 | *a@b 72 |

    73 |

    74 | *a@b{v1.0} 75 |

    76 |

    77 | *a@b[me]{v1.0} 78 |

    79 |

    80 | *{v1.0}a 81 |

    82 |

    83 | *[me]{v1.0}a 84 |

    85 |

    86 | *@b[me]{v1.0}a 87 |

    88 |

    89 | *abc 90 |

    91 |

    92 | *abc@bde 93 |

    94 |

    95 | *abc@bde{v1.0} 96 |

    97 |

    98 | *abc@bde[me]{v1.0} 99 |

    100 |

    101 | * abc @bde [me] {v1.0} 102 |

    103 |

    104 | 105 |

    106 | 107 | 108 |
    109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |

    120 | Here is a long stuff 121 |

    122 |

    123 | ... 124 |

    125 |

    126 | 127 |

    128 |

    129 | 130 |

    131 |

    132 | 133 |

    134 |

    135 | about long stuff 136 |

    137 | 138 | 139 |
    140 |
    141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |

    161 | ? aa 162 |

    163 |

    164 | ?*aa 165 |

    166 |

    167 | ? * aa {10 9} 168 |

    169 | 170 | 171 |
    172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 |

    238 | * Parse normal {2014-10-05} 239 |

    240 |

    241 | * Parse normal {2014.10.05.} 242 |

    243 |

    244 | * Parse normal {2014.10.05} 245 |

    246 |

    247 | * Parse normal {05/10/2014} 248 |

    249 |

    250 | * Parse short {10 05} 251 |

    252 |

    253 | * Parse short {05/10} 254 |

    255 |

    256 | * Parse short {10.05} 257 |

    258 |

    259 | * Parse short {10.05.} 260 |

    261 |

    262 | 263 |

    264 |

    265 | 266 |

    267 | 268 | 269 |
    270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 |

    282 | 123456 283 |

    284 | 285 | 286 | 287 |
    288 |
    289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 |

    317 | You can set up your icons as specified below. 318 |

    319 |

    320 | 321 |

    322 |

    323 | The examples are the default values, that are used when the icons are not otherwise specified. 324 |

    325 |

    326 | 327 |

    328 |

    329 | The configuration nodes can be placed anywhere in the map. 330 |

    331 | 332 | 333 |
    334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 |
    368 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/ReportModel.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | 3 | import freeplaneGTD.util.DateUtil 4 | import groovy.util.logging.Log 5 | import org.freeplane.core.util.TextUtils 6 | import org.freeplane.plugin.script.proxy.ConvertibleDate 7 | 8 | import java.time.temporal.ChronoUnit 9 | import java.util.logging.Level 10 | 11 | /** 12 | * Model for the freeplaneGTD.report pane. 13 | * 14 | * Created by gpapp on 2015.03.05.. 15 | */ 16 | @Log 17 | class ReportModel { 18 | List actionList 19 | List doneList 20 | GTDMapReader mapReader 21 | 22 | String todayText = TextUtils.getText("freeplaneGTD.view.when.today") 23 | String thisWeekText = TextUtils.getText("freeplaneGTD.view.when.this_week") 24 | 25 | /** 26 | * Calculate the next time the action should be taken care of 27 | * (either waitUntil date, or the due date, whichever comes first) 28 | */ 29 | def taskDate = { task -> 30 | def when = task['when'] 31 | def waitUntil = task['waitUntil'] 32 | Date today = new Date().clearTime() 33 | Date whenDate = null 34 | Date waitUntilDate = null 35 | if (when == todayText) whenDate = today 36 | else if (when instanceof Date) whenDate = when 37 | else if (when instanceof ConvertibleDate) whenDate = when.calendar.time 38 | 39 | if (waitUntil instanceof Date) waitUntilDate = waitUntil 40 | else if (waitUntil instanceof ConvertibleDate) waitUntilDate = waitUntil.calendar.time 41 | 42 | if (!whenDate && !waitUntilDate) { 43 | return waitUntil ?: (when ?: thisWeekText) 44 | } 45 | Date retval 46 | if (whenDate && !waitUntilDate) retval = whenDate 47 | else if (!whenDate && waitUntilDate) retval = waitUntilDate 48 | else retval = whenDate < waitUntilDate ? whenDate : waitUntilDate 49 | return retval == today ? todayText : retval 50 | } 51 | 52 | /** 53 | * Compare tasks based on the task date calculated above 54 | */ 55 | def taskDateComparator = { a, b -> 56 | def aw = a['when'] 57 | def bw = b['when'] 58 | if ((!aw && !bw) || aw == bw) { 59 | return 0 60 | } 61 | Date ad = null 62 | Date bd = null 63 | Date today = new Date().clearTime() 64 | if (aw instanceof Date) ad = aw 65 | else if (aw == todayText) ad = today 66 | else if (aw == thisWeekText) ad = today + 7 67 | if (bw instanceof Date) bd = bw 68 | else if (bw == todayText) bd = today 69 | else if (bw == thisWeekText) bd = today + 7 70 | if (ad == null && bd == null) { 71 | return aw <=> bw 72 | } 73 | if (ad && !bd) return -1 74 | if (!ad && bd) return 1 75 | return ad <=> bd 76 | } 77 | 78 | /** 79 | * Sort tasks according to priority and next action time 80 | */ 81 | def taskSortComparator = { a, b -> 82 | def ap = a['priority'] ?: '5' 83 | def bp = b['priority'] ?: '5' 84 | if ((!ap && !bp) || ap == bp) { 85 | return taskDateComparator.call(a, b) 86 | } 87 | return ap <=> bp 88 | } 89 | 90 | ReportModel() { 91 | this.mapReader = GTDMapReader.instance 92 | } 93 | 94 | //-------------------------------------------------------------- 95 | // parse the GTD mind map 96 | void parseMap() { 97 | // Expand any nodes with next action shorthand 98 | mapReader.convertShorthand() 99 | } 100 | 101 | void refreshModel(boolean filterDone) { 102 | try { 103 | // Get next action lists 104 | actionList = mapReader.getActionList(filterDone) 105 | doneList = mapReader.getDoneList() 106 | 107 | // Fill actionList with next actionable time 108 | actionList.each { it['time'] = taskDate(it) } 109 | } catch (Exception e) { 110 | log.log(Level.SEVERE, "Error refreshing model", e) 111 | } 112 | } 113 | 114 | def projectList() { 115 | Map retval = [type: 'project' as Object] 116 | List groups = [] 117 | if (actionList) { 118 | Map naByGroup = actionList.groupBy { it['project'] } 119 | naByGroup = naByGroup.sort { it.toString().toLowerCase() } 120 | naByGroup.each { 121 | key, value -> 122 | List items = [] 123 | def curGroup = naByGroup[key].sort { a, b -> taskSortComparator.call(a, b) } 124 | curGroup.each { 125 | items << [done : it['done'], 126 | cancelled: it['cancelled'], 127 | whenDone : it['whenDone'], 128 | time : it['time'], 129 | priority : it['priority'], 130 | action : it['action'], 131 | nodeID : it['nodeID'], 132 | who : it['who'], 133 | when : it['when'], 134 | context : it['context'], 135 | waitFor : it['waitFor'], 136 | waitUntil: it['waitUntil'], 137 | details : it['details'], 138 | notes : it['notes'] 139 | ] 140 | } 141 | groups << [title: key, items: items] 142 | } 143 | } 144 | retval['groups'] = groups 145 | return retval 146 | } 147 | 148 | def delegateList() { 149 | Map retval = [type: 'who' as Object] 150 | List groups = [] 151 | Map naByWhoFull = actionList.groupBy({ it['who'] }) 152 | Map naByWaitForFull = actionList.groupBy({ it['waitFor'] }) 153 | Map naByDelegate = [:] 154 | naByWhoFull.each { 155 | key, value -> 156 | if (key) { 157 | def keyList = key.split(',')*.trim() 158 | keyList.each { 159 | naByDelegate.put(it, naByDelegate[it] ? (naByDelegate[it] + value).unique() : value) 160 | } 161 | } 162 | } 163 | naByWaitForFull.each { 164 | key, value -> 165 | if (key) { 166 | def keyList = key.split(',')*.trim() 167 | keyList.each { 168 | naByDelegate.put(it, naByDelegate[it] ? (naByDelegate[it] + value).unique() : value) 169 | } 170 | } 171 | } 172 | 173 | naByDelegate = naByDelegate.sort { it.toString().toLowerCase() } 174 | naByDelegate.each { 175 | key, value -> 176 | List items = [] 177 | def curGroup = naByDelegate[key].sort { a, b -> taskSortComparator.call(a, b) } 178 | curGroup.each { 179 | def newItem = [done : it['done'], 180 | cancelled: it['cancelled'], 181 | whenDone : it['whenDone'], 182 | time : it['time'], 183 | priority : it['priority'], 184 | action : it['action'], 185 | nodeID : it['nodeID'], 186 | when : it['when'], 187 | context : it['context'], 188 | project : it['project'], 189 | waitUntil: it['waitUntil'], 190 | details : it['details'], 191 | notes : it['notes'] 192 | ] 193 | if (it['who'] && it['who'].trim() != key) { 194 | newItem['who'] = it['who'] 195 | } 196 | if (it['waitFor'] && it['waitFor'].trim() != key) { 197 | newItem['waitFor'] = it['waitFor'] 198 | } 199 | items << newItem 200 | } 201 | groups << [title: key, items: items] 202 | } 203 | retval['groups'] = groups 204 | return retval 205 | } 206 | 207 | def contextList() { 208 | Map retval = [type: 'context' as Object] 209 | List groups = [] 210 | Map naByGroupFull = actionList.groupBy { it['context'] } 211 | Map naByGroup = [:] 212 | naByGroupFull.each { 213 | key, value -> 214 | if (!key) { 215 | naByGroup.put(key, value) 216 | } else { 217 | def keyList = key?.split(',') 218 | keyList.each { 219 | naByGroup.put(it, naByGroup[it] ? naByGroup[it] + value : value) 220 | } 221 | } 222 | } 223 | naByGroup = naByGroup.sort { it.toString().toLowerCase() } 224 | naByGroup.each { 225 | key, value -> 226 | List items = [] 227 | def curGroup = naByGroup[key].sort { a, b -> taskSortComparator.call(a, b) } 228 | curGroup.each { 229 | items << [done : it['done'], 230 | cancelled: it['cancelled'], 231 | whenDone : it['whenDone'], 232 | time : it['time'], 233 | priority : it['priority'], 234 | action : it['action'], 235 | nodeID : it['nodeID'], 236 | when : it['when'], 237 | who : it['who'], 238 | project : it['project'], 239 | waitFor : it['waitFor'], 240 | waitUntil: it['waitUntil'], 241 | details : it['details'], 242 | notes : it['notes'] 243 | ] 244 | } 245 | groups << [title: key ?: TextUtils.getText("freeplaneGTD.view.context.unassigned"), items: items] 246 | } 247 | retval['groups'] = groups 248 | return retval 249 | } 250 | 251 | def timelineList() { 252 | Map retval = [type: 'when' as Object] 253 | List groups = [] 254 | def sortedList = actionList.sort { a, b -> taskDateComparator.call(a, b) } 255 | def naByGroup = sortedList.groupBy { it['time'] } 256 | naByGroup.each { 257 | key, value -> 258 | List items = [] 259 | def curGroup = naByGroup[key].sort { a, b -> taskSortComparator.call(a, b) } 260 | curGroup.each { 261 | def newItem = [done : it['done'], 262 | cancelled: it['cancelled'], 263 | whenDone : it['whenDone'], 264 | time : it['time'], 265 | priority : it['priority'], 266 | action : it['action'], 267 | nodeID : it['nodeID'], 268 | who : it['who'], 269 | project : it['project'], 270 | context : it['context'], 271 | waitFor : it['waitFor'], 272 | details : it['details'], 273 | notes : it['notes'] 274 | ] 275 | if (it['when'] && it['when'] != key) { 276 | newItem['when'] = it['when'] 277 | } 278 | if (it['waitUntil'] && it['waitUntil'] != key) { 279 | newItem['waitUntil'] = it['waitUntil'] 280 | } 281 | items << newItem 282 | } 283 | groups << [title: key, items: items] 284 | } 285 | retval['groups'] = groups 286 | return retval 287 | } 288 | 289 | GtdReportViewController.DONE_TIMELINE doneTimeline(Date today, Object time) { 290 | if (!time) return GtdReportViewController.DONE_TIMELINE.EARLIER 291 | Date date = DateUtil.normalizeDate(time) as Date 292 | 293 | // consider today as exactly this day 294 | // consider this month as today + month before 295 | def days = ChronoUnit.DAYS.between(date.toInstant(), today.toInstant()) 296 | println "diff days $today->$date = $days" 297 | 298 | if (days < 1) return GtdReportViewController.DONE_TIMELINE.TODAY 299 | if (days < 7) return GtdReportViewController.DONE_TIMELINE.LAST_WEEK 300 | if (days < 30) return GtdReportViewController.DONE_TIMELINE.LAST_MONTH 301 | return GtdReportViewController.DONE_TIMELINE.EARLIER 302 | } 303 | 304 | def doneTimeline() { 305 | Map retval = [type: 'whenDone' as Object] 306 | List groups = [] 307 | // today to compare with 308 | Date today = new Date(); 309 | Map byDoneTimeline = doneList.groupBy({ node -> doneTimeline(today, node['whenDone']) }).sort { a, b -> a.key.compareTo(b.key) } 310 | byDoneTimeline.each { 311 | key, value -> 312 | groups << [title: TextUtils.getText("freeplaneGTD.doneTimeline." + key), items: value] 313 | } 314 | retval['groups'] = groups 315 | return retval 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /harness/src/freeeplaneHarness/MyController.groovy: -------------------------------------------------------------------------------- 1 | package freeeplaneHarness 2 | 3 | import groovy.util.logging.Log 4 | import org.freeplane.api.* 5 | import org.freeplane.core.resources.ResourceController 6 | import org.freeplane.core.ui.AFreeplaneAction 7 | import org.freeplane.core.ui.IUserInputListenerFactory 8 | import org.freeplane.core.ui.components.FreeplaneMenuBar 9 | import org.freeplane.core.ui.menubuilders.generic.Entry 10 | import org.freeplane.core.undo.IUndoHandler 11 | import org.freeplane.core.undo.UndoHandler 12 | import org.freeplane.core.util.FreeplaneVersion 13 | import org.freeplane.features.attribute.AttributeController 14 | import org.freeplane.features.attribute.AttributeRegistry 15 | import org.freeplane.features.attribute.mindmapmode.MAttributeController 16 | import org.freeplane.features.clipboard.ClipboardControllers 17 | import org.freeplane.features.clipboard.mindmapmode.MClipboardControllers 18 | import org.freeplane.features.cloud.CloudController 19 | import org.freeplane.features.edge.EdgeController 20 | import org.freeplane.features.filter.FilterController 21 | import org.freeplane.features.filter.condition.ICondition 22 | import org.freeplane.features.format.FormatController 23 | import org.freeplane.features.format.ScannerController 24 | import org.freeplane.features.icon.IconController 25 | import org.freeplane.features.icon.mindmapmode.MIconController 26 | import org.freeplane.features.link.LinkController 27 | import org.freeplane.features.map.MapModel 28 | import org.freeplane.features.map.mindmapmode.MMapController 29 | import org.freeplane.features.mode.Controller 30 | import org.freeplane.features.mode.ModeController 31 | import org.freeplane.features.mode.mindmapmode.MModeController 32 | import org.freeplane.features.nodelocation.LocationController 33 | import org.freeplane.features.nodestyle.NodeStyleController 34 | import org.freeplane.features.nodestyle.mindmapmode.MNodeStyleController 35 | import org.freeplane.features.note.NoteController 36 | import org.freeplane.features.note.mindmapmode.MNoteController 37 | import org.freeplane.features.styles.LogicalStyleController 38 | import org.freeplane.features.styles.MapStyle 39 | import org.freeplane.features.styles.MapStyleModel 40 | import org.freeplane.features.text.TextController 41 | import org.freeplane.features.text.mindmapmode.MTextController 42 | import org.freeplane.features.ui.FrameController 43 | import org.freeplane.plugin.script.ScriptContext 44 | import org.freeplane.plugin.script.proxy.MapProxy 45 | import org.freeplane.plugin.script.proxy.NodeProxy 46 | import org.freeplane.plugin.script.proxy.Proxy 47 | import org.freeplane.view.swing.map.MapView 48 | import org.freeplane.view.swing.map.MapViewController 49 | import org.freeplane.view.swing.ui.UserInputListenerFactory 50 | 51 | import javax.swing.* 52 | import java.awt.* 53 | import java.awt.event.ActionEvent 54 | import java.util.List 55 | 56 | /** 57 | * Created by gpapp on 2017.05.28.. 58 | */ 59 | @Log 60 | class MyController extends Controller implements Proxy.Controller { 61 | ScriptContext scriptContext 62 | MapModel myMap 63 | 64 | MyController(ResourceController resourceController) { 65 | super(resourceController) 66 | } 67 | 68 | void start() { 69 | scriptContext = new ScriptContext() 70 | mapViewManager = new MapViewController(this) 71 | 72 | viewController = new FrameController(this, mapViewManager, "dummy_prefix") { 73 | @Override 74 | FreeplaneMenuBar getFreeplaneMenuBar() { 75 | return new FreeplaneMenuBar() 76 | } 77 | 78 | @Override 79 | void insertComponentIntoSplitPane(JComponent jComponent) { 80 | 81 | } 82 | 83 | @Override 84 | boolean isApplet() { 85 | return false 86 | } 87 | 88 | @Override 89 | void openDocument(URI uri) throws IOException { 90 | 91 | } 92 | 93 | @Override 94 | void openDocument(URL url) throws Exception { 95 | 96 | } 97 | 98 | @Override 99 | void removeSplitPane() { 100 | 101 | } 102 | 103 | @Override 104 | protected void setFreeplaneMenuBar(FreeplaneMenuBar freeplaneMenuBar) { 105 | 106 | } 107 | 108 | @Override 109 | void setTitle(String s) { 110 | 111 | } 112 | 113 | @Override 114 | void setWaitingCursor(boolean b) { 115 | 116 | } 117 | 118 | @Override 119 | void previousMapView() { 120 | 121 | } 122 | 123 | @Override 124 | void nextMapView() { 125 | 126 | } 127 | 128 | @Override 129 | Component getCurrentRootComponent() { 130 | return null 131 | } 132 | 133 | @Override 134 | Component getMenuComponent() { 135 | return null 136 | } 137 | } 138 | mapViewManager.changeToMap(myMap) 139 | 140 | ModeController modeController = new MyModeController(this) 141 | selectMode(modeController) 142 | 143 | modeController.addExtension(ClipboardControllers.class, new MClipboardControllers()) 144 | 145 | 146 | MMapController mapController = new MMapController(modeController) { 147 | 148 | } 149 | modeController.setMapController(mapController) 150 | 151 | addExtension(FormatController.class, new FormatController()) 152 | addExtension(FilterController.class, new FilterController()) 153 | addExtension(ScannerController.class, new ScannerController()) 154 | 155 | modeController.addExtension(TextController.class, new MTextController(modeController)) 156 | modeController.addExtension(IconController.class, new MIconController(modeController)) 157 | modeController.addExtension(NodeStyleController.class, new MNodeStyleController(modeController)) 158 | modeController.addExtension(EdgeController.class, new EdgeController(modeController)) 159 | modeController.addExtension(LinkController.class, new LinkController(modeController)) 160 | modeController.addExtension(CloudController.class, new CloudController(modeController)) 161 | modeController.addExtension(LocationController.class, new LocationController()) 162 | 163 | NoteController noteController = new MNoteController(modeController) 164 | modeController.addExtension(NoteController.class, noteController) 165 | NoteController.install(noteController) 166 | modeController.addExtension(MapStyle.class, new MapStyle(true)) 167 | 168 | modeController.addExtension(LogicalStyleController.class, new LogicalStyleController(modeController)) 169 | def attributeController = new MAttributeController(modeController) 170 | modeController.addExtension(AttributeController.class, attributeController) 171 | 172 | 173 | // modeController.userInputListenerFactory.updateMenus("/xml/mindmapmodemenu.xml", new HashSet()) 174 | 175 | 176 | } 177 | 178 | @Override 179 | Proxy.Map newMap() { 180 | myMap = new MapModel() 181 | myMap.addExtension(IUndoHandler.class, new UndoHandler(myMap)) 182 | myMap.createNewRoot() 183 | myMap.rootNode.addExtension(new AttributeRegistry(myMap,modeController.getExtension(AttributeController.class),modeController.getExtension(TextController.class))) 184 | MapStyleModel styleModel = new MapStyleModel() 185 | myMap.rootNode.addExtension(styleModel) 186 | styleModel.createStyleMap(myMap, styleModel, "\n" + 187 | "\n" + 188 | "\n" + 189 | "\n" + 190 | "\n" + 191 | "\n" + 192 | "\n" + 193 | "\n" + 194 | "\n" + 195 | "\n" + 196 | "\n" + 197 | "\n" + 198 | "\n" + 199 | "\n" + 200 | "\n" + 201 | "\n" + 202 | "\n" + 203 | "\n" + 204 | "\n" + 205 | "\n" + 206 | "\n" + 207 | "\n" + 208 | "\n" + 209 | "\n" + 210 | "\n" + 211 | "\n" + 212 | "\n" + 213 | "\n" + 214 | "\n" + 215 | "\n" + 216 | "\n" + 217 | "\n" + 218 | "\n" + 219 | "\n" + 220 | "\n" + 221 | "\n" + 222 | "\n" + 223 | "\n" + 224 | "\n" + 225 | "\n" + 226 | "\n" + 227 | "\n" + 228 | "\n" + 229 | "\n" + 230 | "\n" + 231 | "\n" + 232 | "\n" + 233 | "\n" + 234 | "") 235 | this.getMapViewManager().changeToMap(myMap) 236 | this.getMapViewManager().changeToMapView(new MapView(myMap, modeController)) 237 | 238 | return new MapProxy(myMap, scriptContext) 239 | } 240 | 241 | 242 | @Override 243 | void select(Collection collection) { 244 | 245 | } 246 | 247 | @Override 248 | void centerOnNode(Node node) { 249 | 250 | } 251 | 252 | @Override 253 | void edit(Node node) { 254 | 255 | } 256 | 257 | @Override 258 | void editInPopup(Node node) { 259 | 260 | } 261 | 262 | @Override 263 | void select(Node node) { 264 | 265 | } 266 | 267 | 268 | @Override 269 | void selectBranch(Node node) { 270 | 271 | } 272 | 273 | @Override 274 | void selectMultipleNodes(Collection collection) { 275 | 276 | } 277 | 278 | @Override 279 | void deactivateUndo() { 280 | 281 | } 282 | 283 | @Override 284 | void undo() { 285 | 286 | } 287 | 288 | @Override 289 | void redo() { 290 | 291 | } 292 | 293 | @Override 294 | void setStatusInfo(String s) { 295 | 296 | } 297 | 298 | @Override 299 | void setStatusInfo(String s, String s1) { 300 | 301 | } 302 | 303 | @Override 304 | void setStatusInfo(String s, String s1, String s2) { 305 | 306 | } 307 | 308 | @Override 309 | void setStatusInfo(String s, Icon icon) { 310 | 311 | } 312 | 313 | @Override 314 | Loader load(File file) { 315 | return null 316 | } 317 | 318 | @Override 319 | Loader load(URL url) { 320 | return null 321 | } 322 | 323 | @Override 324 | Loader load(String s) { 325 | return null 326 | } 327 | 328 | @Override 329 | Proxy.Map newMap(URL url) { 330 | return null 331 | } 332 | 333 | @Override 334 | Proxy.Map newMapFromTemplate(File file) { 335 | return null 336 | } 337 | 338 | @Override 339 | void setZoom(float v) { 340 | 341 | } 342 | 343 | @Override 344 | List getOpenMaps() { 345 | return null 346 | } 347 | 348 | @Override 349 | Proxy.Node getSelected() { 350 | return null 351 | } 352 | 353 | @Override 354 | List getSelecteds() { 355 | return null 356 | } 357 | 358 | @Override 359 | List getSortedSelection(boolean b) { 360 | return null 361 | } 362 | 363 | @Override 364 | FreeplaneVersion getFreeplaneVersion() { 365 | return null 366 | } 367 | 368 | @Override 369 | File getUserDirectory() { 370 | return new File( 371 | ResourceController.getResourceController().getFreeplaneUserDirectory() 372 | ).absoluteFile 373 | } 374 | 375 | @Override 376 | List find(NodeCondition condition) { 377 | return null 378 | } 379 | 380 | @Override 381 | List find(boolean withAncestors, boolean withDescendants, NodeCondition condition) { 382 | return null 383 | } 384 | 385 | @Override 386 | List find(ICondition iCondition) { 387 | return null 388 | } 389 | 390 | @Override 391 | List find(Closure closure) { 392 | return new NodeProxy(myMap.root, scriptContext).find(closure) 393 | } 394 | 395 | @Override 396 | List find(boolean withAncestors, boolean withDescendants, Closure closure) { 397 | return null 398 | } 399 | 400 | @Override 401 | List findAll() { 402 | return null 403 | } 404 | 405 | @Override 406 | List findAllDepthFirst() { 407 | return null 408 | } 409 | 410 | @Override 411 | float getZoom() { 412 | return 0 413 | } 414 | 415 | @Override 416 | boolean isInteractive() { 417 | return false 418 | } 419 | 420 | @Override 421 | List getExportTypeDescriptions() { 422 | return null 423 | } 424 | 425 | @Override 426 | void export(Map map, File destinationFile, String exportTypeDescription, boolean overwriteExisting) { 427 | 428 | } 429 | 430 | @Override 431 | Proxy.Loader mapLoader(File file) { 432 | return null 433 | } 434 | 435 | @Override 436 | Proxy.Loader mapLoader(URL file) { 437 | return null 438 | } 439 | 440 | @Override 441 | Proxy.Loader mapLoader(String file) { 442 | return null 443 | } 444 | 445 | @Override 446 | Script script(File file) { 447 | return null 448 | } 449 | 450 | @Override 451 | Script script(String s, String s1) { 452 | return null 453 | } 454 | 455 | @Override 456 | void quit(ActionEvent actionEvent) { 457 | System.exit(0) 458 | } 459 | 460 | class MyModeController extends MModeController { 461 | private UserInputListenerFactory factory 462 | 463 | MyModeController(Controller controller) { 464 | super(controller) 465 | } 466 | 467 | 468 | @Override 469 | IUserInputListenerFactory getUserInputListenerFactory() { 470 | if (!this.factory){ 471 | 472 | this.factory = new UserInputListenerFactory(this) 473 | factory.genericMenuStructure=new Entry() 474 | } 475 | return this.factory 476 | } 477 | 478 | @Override 479 | String getModeName() { 480 | return "HarnessController" 481 | } 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /src/zips/lib/freeplaneGTD/GtdReportViewController.groovy: -------------------------------------------------------------------------------- 1 | package freeplaneGTD 2 | 3 | import freeplaneGTD.util.ClipBoardUtil 4 | import freeplaneGTD.util.IconUtil 5 | import groovy.swing.SwingBuilder 6 | import groovy.util.logging.Log 7 | import org.freeplane.api.Node 8 | import org.freeplane.core.resources.ResourceController 9 | import org.freeplane.core.ui.components.MultipleImageIcon 10 | import org.freeplane.core.ui.components.UITools 11 | import org.freeplane.core.util.TextUtils 12 | import org.freeplane.features.clipboard.ClipboardController 13 | import org.freeplane.features.format.FormattedDate 14 | import org.freeplane.features.map.clipboard.MapClipboardController 15 | import org.freeplane.features.mode.Controller 16 | import org.freeplane.features.mode.ModeController 17 | import org.freeplane.plugin.script.proxy.ScriptUtils 18 | 19 | import javax.swing.* 20 | import java.awt.* 21 | import java.awt.event.MouseAdapter 22 | import java.awt.event.MouseEvent 23 | import java.util.logging.Level 24 | 25 | @Log 26 | class GtdReportViewController { 27 | 28 | public static final String FREEPLANE_GTD_DEFAULT_VIEW = 'freeplaneGTD_default_view' 29 | static enum VIEW { 30 | PROJECT, WHO, CONTEXT, WHEN, DONETIMELINE 31 | } 32 | 33 | static enum DONE_TIMELINE { 34 | TODAY, 35 | LAST_WEEK, 36 | LAST_MONTH, 37 | EARLIER 38 | 39 | } 40 | 41 | private ReportModel report = new ReportModel() 42 | private JPanel taskPanel 43 | private ButtonGroup contentTypeGroup 44 | private JCheckBox cbFilterDone 45 | private Font titleFont 46 | 47 | protected boolean showNotes 48 | 49 | Component createPanel(ModeController modeController) { 50 | String defaultView 51 | try { 52 | defaultView = VIEW.valueOf(ResourceController.resourceController.getProperty(FREEPLANE_GTD_DEFAULT_VIEW)).toString() 53 | } catch (Exception e) { 54 | defaultView = VIEW.PROJECT.toString() 55 | log.log(Level.WARNING, "Cannot parse default view property:" + ResourceController.resourceController.getProperty(FREEPLANE_GTD_DEFAULT_VIEW), e) 56 | } 57 | 58 | JPanel newPanel = null 59 | SwingBuilder.edtBuilder { 60 | try { 61 | newPanel = panel() { 62 | borderLayout() 63 | panel(constraints: BorderLayout.NORTH) { 64 | gridLayout(columns: 1, rows: 1) 65 | contentTypeGroup = buttonGroup() 66 | radioButton( 67 | buttonGroup: contentTypeGroup, 68 | actionCommand: VIEW.PROJECT.name(), 69 | text: "1 - " + TextUtils.getText("freeplaneGTD.tab.project.title"), 70 | toolTipText: TextUtils.getText("freeplaneGTD.tab.project.tooltip"), 71 | mnemonic: (char) '1', 72 | selected: defaultView == "PROJECT", 73 | actionPerformed: { refreshContent() } 74 | ) 75 | radioButton( 76 | buttonGroup: contentTypeGroup, 77 | actionCommand: VIEW.WHO.name(), 78 | text: "2 - " + TextUtils.getText("freeplaneGTD.tab.who.title"), 79 | toolTipText: TextUtils.getText("freeplaneGTD.tab.who.tooltip"), 80 | mnemonic: (char) '2', 81 | selected: defaultView == "WHO", 82 | actionPerformed: { refreshContent() } 83 | ) 84 | radioButton( 85 | buttonGroup: contentTypeGroup, 86 | actionCommand: VIEW.CONTEXT.name(), 87 | text: "3 - " + TextUtils.getText("freeplaneGTD.tab.context.title"), 88 | toolTipText: TextUtils.getText("freeplaneGTD.tab.context.tooltip"), 89 | mnemonic: (char) '3', 90 | selected: defaultView == "CONTEXT", 91 | actionPerformed: { refreshContent() } 92 | ) 93 | radioButton( 94 | buttonGroup: contentTypeGroup, 95 | actionCommand: VIEW.WHEN.name(), 96 | text: "4 - " + TextUtils.getText("freeplaneGTD.tab.when.title"), 97 | toolTipText: TextUtils.getText("freeplaneGTD.tab.when.tooltip"), 98 | mnemonic: (char) '4', 99 | selected: defaultView == "WHEN", 100 | actionPerformed: { refreshContent() } 101 | ) 102 | radioButton( 103 | buttonGroup: contentTypeGroup, 104 | actionCommand: VIEW.DONETIMELINE.name(), 105 | text: "5 - " + TextUtils.getText("freeplaneGTD.tab.done_timeline.title"), 106 | toolTipText: TextUtils.getText("freeplaneGTD.tab.done_timeline.tooltip"), 107 | mnemonic: (char) '5', 108 | actionPerformed: { refreshContent() } 109 | ) 110 | } 111 | scrollPane(constraints: BorderLayout.CENTER) { 112 | taskPanel = panel(border: emptyBorder(bottom: 10, left: 10, top: 10, right: 10)) { 113 | borderLayout() 114 | } 115 | } 116 | 117 | panel(constraints: BorderLayout.SOUTH) { 118 | boxLayout(axis: BoxLayout.X_AXIS) 119 | button(text: TextUtils.getText("freeplaneGTD.button.refresh"), 120 | actionPerformed: { 121 | refreshContent() 122 | }) 123 | button(text: TextUtils.getText("freeplaneGTD.button.copy"), 124 | actionPerformed: { 125 | copyToClipboard(-1) 126 | }) 127 | cbFilterDone = checkBox(text: TextUtils.getText("freeplaneGTD.button.filter_done"), 128 | selected: ResourceController.resourceController.getBooleanProperty('freeplaneGTD_filter_done'), 129 | actionPerformed: { 130 | ResourceController.resourceController.properties['freeplaneGTD_filter_done'] = Boolean.toString(it.source.selected as boolean) 131 | refreshContent() 132 | }) 133 | checkBox(text: TextUtils.getText("freeplaneGTD.button.show_notes"), 134 | selected: showNotes, 135 | actionPerformed: { 136 | showNotes = it.source.selected 137 | refreshContent() 138 | }) 139 | } 140 | } 141 | 142 | } catch (Exception e) { 143 | newPanel = panel() { 144 | textPane(text: "Exception rendering list" + e.dump()) 145 | } 146 | } 147 | } 148 | titleFont = newPanel.getFont().deriveFont(Font.BOLD, 16.0) 149 | return newPanel 150 | } 151 | 152 | void refreshContent() { 153 | cbFilterDone.selected = ResourceController.resourceController.getBooleanProperty('freeplaneGTD_filter_done') 154 | report.refreshModel(cbFilterDone.selected) 155 | taskPanel.clear() 156 | taskPanel.add(formatList(findSelectedGroup(), report.mapReader.contextIcons, showNotes)) 157 | taskPanel.updateUI() 158 | } 159 | 160 | private Component formatList(Map list, Map contextIcons, boolean showNotes) { 161 | JPanel newList 162 | SwingBuilder.edtBuilder { 163 | try { 164 | newList = panel() { 165 | gridBagLayout() 166 | list['groups'].eachWithIndex { group, index -> 167 | panel(constraints: gbc(anchor: GridBagConstraints.NORTHWEST, fill: GridBagConstraints.HORIZONTAL, gridwidth: GridBagConstraints.REMAINDER)) { 168 | gridBagLayout() 169 | if (contextIcons.keySet().contains(group['title'])) { 170 | label(icon: IconUtil.getIcon(contextIcons.get(group['title'])), 171 | text: group['title'], font: titleFont, constraints: gbc(weightx: 1.0, fill: GridBagConstraints.BOTH)) 172 | } else { 173 | label(text: group['title'], font: titleFont, constraints: gbc(weightx: 1.0, fill: GridBagConstraints.BOTH)) 174 | } 175 | 176 | button( 177 | text: TextUtils.getText("freeplaneGTD.button.copy"), 178 | constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.EAST), 179 | actionPerformed: { 180 | copyToClipboard(index) 181 | } 182 | ) 183 | button(text: TextUtils.getText("freeplaneGTD.button.select"), 184 | constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.EAST), 185 | actionPerformed: { 186 | selectOnMap(index) 187 | } 188 | ) 189 | } 190 | 191 | group['items'].each { Object item -> 192 | rigidArea(width: 15) 193 | 194 | // priority block 195 | panel(constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.NORTHWEST)) { 196 | if (item['priority']) { 197 | label(icon: IconUtil.getIcon("full-" + item['priority'])) 198 | } 199 | } 200 | // done checkbox 201 | checkBox(selected: item['done'], 202 | constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.NORTH), 203 | icon: IconUtil.getIcon("unchecked"), 204 | selectedIcon: IconUtil.getIcon("button_ok"), 205 | actionPerformed: { 206 | toggleDone((String) item['nodeID'], false) 207 | }) 208 | // cancelled checkbox 209 | checkBox(selected: item['cancelled'], 210 | constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.NORTH), 211 | icon: IconUtil.getIcon("unchecked"), 212 | selectedIcon: IconUtil.getIcon("button_cancel"), 213 | actionPerformed: { 214 | toggleDone((String) item['nodeID'], true) 215 | }) 216 | // context icons 217 | panel(constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.NORTHWEST)) { 218 | MultipleImageIcon mii = new MultipleImageIcon() 219 | (item['context'] as String)?.tokenize(',')?.each { key -> 220 | if (contextIcons.keySet().contains(key)) { 221 | mii.addIcon(IconUtil.getUIIcon(contextIcons.get(key))) 222 | } 223 | } 224 | label(icon: mii) 225 | } 226 | // task content 227 | 228 | boolean overdue = item['time'] instanceof FormattedDate && ((FormattedDate) item['time']).before(new Date()) 229 | StringBuilder actionText = new StringBuilder(item['action'] as String) 230 | 231 | !item['who'] ?: actionText.append(' [' + item['who'] + ']') 232 | !item['when'] ?: actionText.append(' {' + item['when'] + '}') 233 | !item['context'] ?: (item['context'] as String)?.tokenize(',')?.each { key -> 234 | actionText.append(' @') 235 | actionText.append(key) 236 | } 237 | !item['project'] ?: actionText.append(' for ' + item['project']) 238 | // TODO: differentiate waitfor somehow? 239 | if (item['waitFor'] || item['waitUntil']) { 240 | actionText.append(' wait' + (item['waitFor'] ? ' for ' + item['waitFor'] : '') + (item['waitUntil'] ? ' until ' + item['waitUntil'] : '')) 241 | } 242 | vbox(constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.NORTHEAST, fill: GridBagConstraints.BOTH)) { 243 | JLabel taskLabel = label(text: actionText, foreground: overdue ? Color.RED : Color.BLACK) 244 | 245 | taskLabel.addMouseListener(new MouseAdapter() { 246 | @Override 247 | void mouseClicked(MouseEvent e) { 248 | followLink(item['nodeID'] as String) 249 | } 250 | }) 251 | 252 | if (showNotes) { 253 | if (item['details']) { 254 | label(text: item['details'], background: new Color(255, 247, 147), 255 | constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.NORTHEAST, fill: GridBagConstraints.BOTH) 256 | ) 257 | } 258 | if (item['notes']) { 259 | label(text: item['notes'], background: new Color(255, 255, 230), 260 | constraints: gbc(weightx: 0.0, anchor: GridBagConstraints.NORTHEAST, fill: GridBagConstraints.BOTH) 261 | ) 262 | } 263 | } 264 | } 265 | glue(constraints: gbc(weightx: 1.0, fill: GridBagConstraints.BOTH, gridwidth: GridBagConstraints.REMAINDER)) 266 | } 267 | } 268 | glue(constraints: gbc(fill: GridBagConstraints.BOTH, gridheight: GridBagConstraints.REMAINDER, gridwidth: GridBagConstraints.REMAINDER)) 269 | } 270 | } catch (Exception e) { 271 | log.log(Level.SEVERE, e.message, e) 272 | newList = panel() { 273 | textPane(text: "ERROR RENDERING LIST!") 274 | } 275 | } 276 | } 277 | 278 | return newList 279 | } 280 | 281 | private Map findSelectedGroup() { 282 | Map list 283 | switch (VIEW.valueOf(contentTypeGroup.selection.actionCommand)) { 284 | case VIEW.PROJECT: list = report.projectList(); break 285 | case VIEW.WHO: list = report.delegateList(); break 286 | case VIEW.CONTEXT: list = report.contextList(); break 287 | case VIEW.WHEN: list = report.timelineList(); break 288 | case VIEW.DONETIMELINE: list = report.doneTimeline(); break 289 | default: throw new UnsupportedOperationException("Invalid selection pane: " + contentTypeGroup.selection.actionCommand) 290 | } 291 | return list 292 | } 293 | 294 | void copyToClipboard(int pos) { 295 | try { 296 | ClipboardController clip = Controller.currentModeController.getExtension(MapClipboardController.class) 297 | def selection = findSelectedGroup() 298 | if (pos >= 0) { 299 | selection['groups'] = [selection['groups'][pos]] 300 | } 301 | clip.clipboardContents = ClipBoardUtil.createTransferable(selection, report.mapReader, showNotes) 302 | UITools.informationMessage(TextUtils.getText('freeplaneGTD.message.copy_ok')) 303 | } catch (Exception e) { 304 | log.severe(e.message) 305 | } 306 | } 307 | 308 | void selectOnMap(int pos) { 309 | try { 310 | java.util.List ids = findSelectedGroup()['groups'][pos]['items'].collect { it['nodeID'] } 311 | SwingUtilities.invokeLater(new Runnable() { 312 | @Override 313 | void run() { 314 | def nodesFound = ScriptUtils.c().find { ids.contains(it.id) } 315 | if (nodesFound.size() > 0) { 316 | if (autoFoldMap) { 317 | foldToTop(nodesFound[0]) 318 | } 319 | nodesFound.each { 320 | unfoldBranch(it) 321 | } 322 | ScriptUtils.c().centerOnNode(nodesFound[0]) 323 | ScriptUtils.c().selectMultipleNodes(nodesFound) 324 | } else { 325 | UITools.informationMessage("Error finding selection") 326 | } 327 | } 328 | }) 329 | } catch (Exception e) { 330 | log.severe(e.message) 331 | } 332 | } 333 | 334 | boolean getAutoFoldMap() { 335 | return ResourceController.resourceController.getBooleanProperty('freeplaneGTD_auto_fold_map') 336 | } 337 | 338 | void toggleDone(String linkNodeID, boolean isCancel) { 339 | try { 340 | SwingUtilities.invokeLater(new Runnable() { 341 | @Override 342 | void run() { 343 | def nodesFound = ScriptUtils.c().find { it.id == linkNodeID } 344 | 345 | if (nodesFound[0] != null) { 346 | def node = nodesFound[0] 347 | boolean needsNewIcon = isCancel ? !node.icons.contains(report.mapReader.iconCancel) : !node.icons.contains(report.mapReader.iconDone) 348 | node.icons.remove(report.mapReader.iconDone) 349 | node.icons.remove(report.mapReader.iconCancel) 350 | if (needsNewIcon) { 351 | node.icons.add(isCancel ? report.mapReader.iconCancel : report.mapReader.iconDone) 352 | } 353 | refreshContent() 354 | } else { 355 | log.severe("Cannot find node to mark as done") 356 | UITools.informationMessage("Cannot find node to mark as done") 357 | } 358 | } 359 | }) 360 | } catch (Exception e) { 361 | log.severe("Error in toggling as done:" + e.message) 362 | } 363 | } 364 | 365 | void followLink(String linkNodeID) { 366 | try { 367 | SwingUtilities.invokeLater(new Runnable() { 368 | @Override 369 | void run() { 370 | java.util.List nodesFound = ScriptUtils.c().find { it.id == linkNodeID } 371 | 372 | if (nodesFound[0] != null) { 373 | if (autoFoldMap) { 374 | foldToTop(nodesFound[0]) 375 | } 376 | unfoldBranch(nodesFound[0]) 377 | ScriptUtils.c().centerOnNode(nodesFound[0]) 378 | ScriptUtils.c().select(nodesFound[0]) 379 | } else { 380 | log.severe("Next Action not found in mind map. Refresh Next Action list") 381 | UITools.informationMessage("Next Action not found in mind map. Refresh Next Action list") 382 | } 383 | } 384 | }) 385 | } catch (Exception e) { 386 | log.severe(e.message) 387 | } 388 | } 389 | 390 | // recursive unfolding of branch 391 | private static void unfoldBranch(Node thisNode) { 392 | Node rootNode = thisNode.getMap().getRoot() 393 | if (thisNode != rootNode) { 394 | if (thisNode.folded) thisNode.setFolded(false) 395 | unfoldBranch(thisNode.getParent()) 396 | } 397 | } 398 | 399 | // fold to first level 400 | private static void foldToTop(Node thisNode) { 401 | Node rootNode = thisNode.getMap().getRoot() 402 | def Nodes = ScriptUtils.c().findAll() 403 | Nodes.each { 404 | if (!it.folded) it.setFolded(true) 405 | } 406 | rootNode.setFolded(false) 407 | } 408 | 409 | void parseAndRefresh() { 410 | report.parseMap() 411 | refreshContent() 412 | } 413 | } 414 | --------------------------------------------------------------------------------