前言
去年9月份开发的那个小工具sTodo,只是做到了能用,但是还很不好用,比如没有定时的提醒功能,整个框架比较死板,不方便扩展,虽然设计之初就考虑要使用脚本语言进行脚本化,提供更强的定制能力,但是一直因为工作上事情太多,没有经历投入而悬停。
不明真相的可以参看以前的两篇文章:
sTodo的现状
sTodo内部用了一套任务管理模块,可以做到定时提醒的功能,效果还是不错的:
虽然UI还很原始,但是基本功能都已经实现,剩下就是美化UI了。另外,插件系统也基本可用,后台使用JavaScript作为脚本语言,并使用rhino引擎解释。可以看一个例子:
/* * put all initialize code here */ function _init_(){ var app = application; var ui = app.getUI(); //set look and feel to windows ui.setLookAndFeel("windows"); //load some new scripts app.activePlugin("scripts/help.js"); app.activePlugin("scripts/util.js"); var menubar = ui.getMenuBar(); //menubar.add(new menu("", "", "")) //launch the main frame app.launch(); }
而同时在Java代码中,将application(STodo的实例)作为变量传递给脚本,就可以实现两者的通信了:
public static void main(String[] args){
STodo sTodo = new STodo(new MainFrame("My todo list"));
sTodo.initEnv();
Plugin system = TodoPluginManager.getInstance().getPlugin("system");
system.putValueToContext("application", sTodo);
system.execute("_init_", new Object());
}
比如,上边的JavaScript代码中的这一句:
ui.setLookAndFeel("windows");
表示,将界面风格设置为windows主题,而:
ui.setLookAndFeel("motif");
表示,将界面风格设置为motif主题,就是SUN的那个solaris系统的默认风格。两者的对比如下:
| |
关于脚本化Java,还可以参考这篇文章,是我之前发表在IBM Developerworks上的:
使用 JavaScript 脚本化 Java 应用 其中使用的例子就是这个sTodo.
目标
后边需要加入更多的内容,下面是一个列表:
- 插件机制,包括暴露更多,更方便的全局引用给用户,方便定制
- 提供更方便的编辑方式
- 提供更灵活的日期格式,比如:明天晚上,这个周末,5分钟后等自然语言来定制提醒时刻
- UI的完善,使得sTodo成为一个高可用的应用程序
更新
最近离职了,新的公司要到5月10号左右去报到,因此有大量的时间来做sTodo后边的完善工作,经过近一周的重构,现在的sTodo已经基本可用了。正如上文中的目标部分列出来的项目,现在已经完成的己有:
- 暴露了大部分UI组件
- 暴露了更多的API,比如通过脚本插入新的待办事项,发送邮件等
- 日期更加灵活,使用的是一个JavScript库(http://www.datejs.com/),这个库的发现,对sTodo来说,简直是雪中送炭
- UI部分的美化,现在还没有做到,这个后期有时间再来慢慢修改
function main(){ var app = Application; var ui = app.getUI(); //set look and feel to windows ui.setLookAndFeel("windows"); //load some new scripts app.activePlugin("scripts/json.js"); app.activePlugin("scripts/date.js"); app.activePlugin("scripts/util.js"); app.activePlugin("scripts/menubar.js"); app.activePlugin("scripts/misc.js"); app.launch(); //loadTodosFromFile("todos.txt"); }
var jQuery = jQuery || {}; jQuery.extend = function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) target = {}; // extend jQuery itself if only one argument is passed if ( length == i ) { target = this; --i; } for ( ; i < length; i++ ) // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) // Extend the base object for ( var name in options ) { var src = target[ name ], copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) continue; // Recurse if we're merging object values if ( deep && copy && typeof copy === "object" && !copy.nodeType ) target[ name ] = jQuery.extend( deep, // Never move original objects, clone them src || ( copy.length != null ? [ ] : { } ) , copy ); // Don't bring in undefined values else if ( copy !== undefined ) target[ name ] = copy; } // Return the modified object return target; };
function createNewTodo(def){ var item = { desc : "short description", type : "node", timeout : "now +5 min", period : "never", status : "new", note : "long message type here." }; item = jQuery.extend(item, def); return item; }
function insertNewTodo(def){ //the default new to-do template var item = createNewTodo(def); DataModel.addItem(JSON.stringify(item)); } insertNewTodo({desc : "Gonna to kill bill", timeout : "tomorrow 10 am" }); insertNewTodo({desc : "Relive bill killed", timeout : "5/1 9 pm" });
var STodoMenuItem = function(text, icon, func){ this.menu = new JMenuItem(); this.menu.setText(text); if(icon){ this.menu.setIcon(new ImageIcon(icon)); } if(func){ this.click(func); } }; STodoMenuItem.prototype.click = function(func){ this.menu.addActionListener( new JavaAdapter( ActionListener, { actionPerformed : func } ) ); }; STodoMenuItem.prototype.getMenuObject = function(){ return this.menu; }
var menu = new STodoMenuItem("test", "imgs/plugin.png"); menu.click(function(){ alert("tomorrow is another day"); }); menuHelp.add(menu.getMenuObject());
当然,这里的Alert并非DOM中的window的属性,而是重新定义的,使用Swing的弹出对话框实现的:
//show the message as a message dialog function alert(message){ JOptionPane.showMessageDialog( null, message, "Alert", JOptionPane.INFORMATION_MESSAGE); }好了,这一篇就先说这么多,代码开发之余,写写文档也是不错的。如果有下一篇,就讲讲sTodo中的任务调度部分,这是整个sTodo的核心。