From 9fcf15a06b9ec422dbad53508e8ce71d2d4145c3 Mon Sep 17 00:00:00 2001
From: deva <deva>
Date: Tue, 13 Jan 2009 09:59:22 +0000
Subject: A huge amount of changes, based on the results of two usertest. The
 changes are contained (but not limited to) in the following list:  - Make
 disabled widgets ignored in validation test.  - Do not commit values of
 disabled widgets to the database.  - Make storechildren attribute on
 metawidget, that enables storing of the child widgets in the database.  -
 Implement LUA resume generator.  - Make language attribute on resume tag, and
 switch parser (format/LUA).  - Case insensitive search in combobox.  - Click
 on macro name or line, expands macro.  - Greyed out widgets in AltComboBox
 should be hidden instead.  - Keyboard 'delete' should delete item from
 multilist.  - "Commit" button needs to be more visible? Icon?  - Upon opening
 of a second macro, the first macro should indicate itself as 'not saved'.  -
 After 'add' in multilist, the input widgets should be reset.  - First widget
 in a macro should have keyboard focus after expansion.  - "Endnu ikke
 udfyldt" needs to be more clear (darker).  - Meta widgets must recurse the
 isValid() call to its children.  - Greyed out widgets must be hidden.  -
 Multilist should be read as a list prior to its input fields.  - Visible
 field on widgets. Hides a widget without disabling it.

---
 client/client.pro              |   4 ++
 client/collapser.cc            |   4 ++
 client/collapser.h             |   4 ++
 client/icons/arrows.png        | Bin 547 -> 545 bytes
 client/lua.cc                  |  46 ++++++++++--
 client/lua.h                   |   1 +
 client/macrodrawer.cc          |  67 ++++++++++++++++++
 client/macrodrawer.h           |  46 ++++++++++++
 client/macrowindow.cc          | 154 ++++++++++++++++++++++++++++++-----------
 client/macrowindow.h           |  14 ++++
 client/mainwindow.cc           |  81 ++++++++++++++--------
 client/mainwindow.h            |   3 +-
 client/netcom.cc               |  11 +--
 client/resumewidget.cc         |   2 +-
 client/widgetbuilder.cc        |  53 +++++++++++---
 client/widgetbuilder.h         |   3 +-
 client/widgets.h               |   1 +
 client/widgets/altcombobox.cc  |  82 +++++++++++++++++++---
 client/widgets/altcombobox.h   |  16 ++++-
 client/widgets/button.cc       |  53 +++++++++++++-
 client/widgets/button.h        |  17 ++++-
 client/widgets/checkbox.cc     |  42 +++++++++--
 client/widgets/checkbox.h      |  19 ++++-
 client/widgets/combobox.cc     |  54 ++++++++++++---
 client/widgets/combobox.h      |  18 ++++-
 client/widgets/common.cc       |   7 ++
 client/widgets/common.h        |   1 +
 client/widgets/datetime.cc     | 120 ++++++++++++++++++++++++++++++++
 client/widgets/datetime.h      |  64 +++++++++++++++++
 client/widgets/dbwidget.cc     |  35 +++++++++-
 client/widgets/dbwidget.h      |  17 ++++-
 client/widgets/frame.cc        |  17 +++++
 client/widgets/frame.h         |   7 ++
 client/widgets/groupbox.cc     |  17 +++++
 client/widgets/groupbox.h      |   7 ++
 client/widgets/label.cc        |  16 +++++
 client/widgets/label.h         |   7 ++
 client/widgets/lineedit.cc     |  35 +++++++++-
 client/widgets/lineedit.h      |  15 +++-
 client/widgets/listbox.cc      |  40 ++++++++++-
 client/widgets/listbox.h       |  23 +++++-
 client/widgets/metawidget.cc   | 115 +++++++++++++++++++++---------
 client/widgets/metawidget.h    |  24 ++++++-
 client/widgets/multilist.cc    | 113 ++++++++++++++++++------------
 client/widgets/multilist.h     |  25 +++++--
 client/widgets/radiobutton.h   |   1 +
 client/widgets/radiobuttons.cc |  51 +++++++++++++-
 client/widgets/radiobuttons.h  |  19 ++++-
 client/widgets/textedit.cc     |  37 +++++++++-
 client/widgets/textedit.h      |  20 +++++-
 client/widgets/widget.cc       |  25 ++++---
 client/widgets/widget.h        |  33 +++++++--
 client/widgets/window.cc       |  12 ++++
 client/widgets/window.h        |   6 ++
 54 files changed, 1470 insertions(+), 234 deletions(-)
 create mode 100644 client/macrodrawer.cc
 create mode 100644 client/macrodrawer.h
 create mode 100644 client/widgets/datetime.cc
 create mode 100644 client/widgets/datetime.h

diff --git a/client/client.pro b/client/client.pro
index 61b63fb..6df6c3f 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -28,6 +28,7 @@ HEADERS += \
         lua.h \
         macrowindow.h \
 	mainwindow.h \
+	macrodrawer.h \
 	netcom.h \
 	resumewidget.h \
         widgetbuilder.h \
@@ -39,6 +40,7 @@ HEADERS += \
         widgets/multilist.h \
 	widgets/textedit.h \
 	widgets/button.h \
+	widgets/datetime.h \
 	widgets/dbwidget.h \
 	widgets/combobox.h \
 	widgets/listbox.h \
@@ -58,6 +60,7 @@ SOURCES += \
         lua.cc \
         macrowindow.cc \
 	mainwindow.cc \
+	macrodrawer.cc \
 	netcom.cc \
 	resumewidget.cc \
         widgetbuilder.cc \
@@ -69,6 +72,7 @@ SOURCES += \
         widgets/textedit.cc \
         widgets/button.cc \
         widgets/combobox.cc \
+	widgets/datetime.cc	 \
 	widgets/dbwidget.cc	 \
 	widgets/listbox.cc \
         widgets/frame.cc \
diff --git a/client/collapser.cc b/client/collapser.cc
index e06a5cf..ff1ab69 100644
--- a/client/collapser.cc
+++ b/client/collapser.cc
@@ -107,6 +107,8 @@ void Collapser::setCollapsed(bool setcollapsed)
 
 void Collapser::collapse()
 {
+  emit collapsing();
+
   t_anim.start();
 
   is_collapsed = true;
@@ -116,6 +118,8 @@ void Collapser::collapse()
 
 void Collapser::expand()
 {
+  emit expanding();
+
   t_anim.start();
 
   // show expanded
diff --git a/client/collapser.h b/client/collapser.h
index fa25dd5..2a3581b 100644
--- a/client/collapser.h
+++ b/client/collapser.h
@@ -51,6 +51,10 @@ public slots:
   void expand();
   void toggleCollapse();
 
+signals:
+  void collapsing();
+  void expanding();
+
 protected:
   void timerEvent(QTimerEvent *);
 
diff --git a/client/icons/arrows.png b/client/icons/arrows.png
index ae56b01..960824b 100644
Binary files a/client/icons/arrows.png and b/client/icons/arrows.png differ
diff --git a/client/lua.cc b/client/lua.cc
index 9494ee2..2dcd279 100644
--- a/client/lua.cc
+++ b/client/lua.cc
@@ -119,7 +119,7 @@ static int _setValue(lua_State *L)
   int n = lua_gettop(L); // number of arguments
   if(n != 2) {
     char errstr[512];
-    sprintf(errstr, "Number of args expected 0, got %d", n);
+    sprintf(errstr, "Number of args expected 2, got %d", n);
     lua_pushstring(L, errstr);
     lua_error(L);
     return 0;
@@ -144,6 +144,37 @@ static int _setValue(lua_State *L)
   return 0;
 }
 
+static int _setVisible(lua_State *L)
+{
+  int n = lua_gettop(L); // number of arguments
+  if(n != 2) {
+    char errstr[512];
+    sprintf(errstr, "Number of args expected 2, got %d", n);
+    lua_pushstring(L, errstr);
+    lua_error(L);
+    return 0;
+  }
+
+  bool value = lua_toboolean(L, lua_gettop(L));
+  lua_pop(L, 1);
+  QString name = lua_tostring(L, lua_gettop(L));
+  lua_pop(L, 1);
+
+  lua_getglobal(L, GLOBAL_POINTER);
+  LUA *lua = (LUA*)lua_touserdata(L, lua_gettop(L));
+
+  if(!lua) {
+    lua_pushstring(L, "No LUA pointer!");
+    lua_error(L);
+    return 1;
+  }
+
+  lua->setVisible(name, value);
+
+  return 0;
+}
+
+
 LUA::LUA(MacroWindow *macrowindow)
 {
   this->macrowindow = macrowindow;
@@ -163,6 +194,7 @@ LUA::LUA(MacroWindow *macrowindow)
   lua_register(L, "setValue", _setValue);
   lua_register(L, "enable", _enable);
   lua_register(L, "disable", _disable);
+  lua_register(L, "setVisible", _setVisible);
 }
 
 LUA::~LUA()
@@ -180,19 +212,25 @@ QString LUA::getValue(QString name)
 void LUA::setValue(QString name, QString value)
 {
   Widget *widget = macrowindow->getWidget(name);
-  if(widget) return widget->setValue(value);
+  if(widget) widget->setValue(value);
 }
 
 void LUA::enable(QString name)
 {
   Widget *widget = macrowindow->getWidget(name);
-  if(widget) return widget->enable();
+  if(widget) widget->enable();
 }
 
 void LUA::disable(QString name)
 {
   Widget *widget = macrowindow->getWidget(name);
-  if(widget) return widget->disable();
+  if(widget) widget->disable();
+}
+
+void LUA::setVisible(QString name, bool value)
+{
+  Widget *widget = macrowindow->getWidget(name);
+  if(widget) widget->setVisibility(value);
 }
 
 bool LUA::run(QString program, QString name, QString value)
diff --git a/client/lua.h b/client/lua.h
index a3867ca..4777dd4 100644
--- a/client/lua.h
+++ b/client/lua.h
@@ -45,6 +45,7 @@ public:
   void setValue(QString name, QString value);
   void enable(QString name);
   void disable(QString name);
+  void setVisible(QString name, bool value);
 
   void error(QString message);
 
diff --git a/client/macrodrawer.cc b/client/macrodrawer.cc
new file mode 100644
index 0000000..97868f8
--- /dev/null
+++ b/client/macrodrawer.cc
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ *            macrodrawer.cc
+ *
+ *  Tue Jan  6 15:49:48 CET 2009
+ *  Copyright 2009 Bent Bisballe Nyeng
+ *  deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ *  This file is part of Pracro.
+ *
+ *  Pracro is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Pracro is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Pracro; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#include "macrodrawer.h"
+
+#include <QPushButton>
+
+MacroDrawer::MacroDrawer(MacroWindow *p, QString title)
+{
+  installEventFilter(this);
+  mw = p;
+  
+  if(!mw->isstatic) setTitle("       " + title);
+  setFlat(true);
+  
+  {
+    QFont f = font();
+    f.setItalic(true);
+    setFont(f);
+  }
+  
+  if(!mw->isstatic) {
+    QPushButton *b = new QPushButton("�", this);
+    b->setFixedSize(16,16);
+    b->move(0,0);
+    {
+      QFont f = b->font();
+      f.setBold(false);
+      f.setItalic(false);
+      b->setFont(f);
+    }
+
+    connect(b, SIGNAL(clicked()), mw, SLOT(toggleMacro()));
+  }
+}
+
+bool MacroDrawer::eventFilter(QObject *obj, QEvent *event)
+{
+  if(event->type() == QEvent::MouseButtonRelease) {
+    QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
+    if(mouseEvent->y() < 16) mw->toggleMacro(); // Only activate when clicking the top.
+  }
+  return QObject::eventFilter(obj, event);
+}
diff --git a/client/macrodrawer.h b/client/macrodrawer.h
new file mode 100644
index 0000000..b634198
--- /dev/null
+++ b/client/macrodrawer.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ *            macrodrawer.h
+ *
+ *  Tue Jan  6 15:49:48 CET 2009
+ *  Copyright 2009 Bent Bisballe Nyeng
+ *  deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ *  This file is part of Pracro.
+ *
+ *  Pracro is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Pracro is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Pracro; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#ifndef __PRACRO_MACRODRAWER_H__
+#define __PRACRO_MACRODRAWER_H__
+
+#include <QGroupBox>
+#include <QString>
+#include "macrowindow.h"
+
+class MacroDrawer : public QGroupBox {
+Q_OBJECT
+public:
+  MacroDrawer(MacroWindow *p, QString title);
+
+protected:
+  bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+  MacroWindow *mw;
+};
+
+#endif/*__PRACRO_MACRODRAWER_H__*/
diff --git a/client/macrowindow.cc b/client/macrowindow.cc
index ac44b06..2206929 100644
--- a/client/macrowindow.cc
+++ b/client/macrowindow.cc
@@ -47,6 +47,8 @@ MacroWindow::MacroWindow(NetCom *netcom, QDomNode &xml_doc, QString course,
                          bool collapsed, bool compact)
   : Collapser()
 {
+  waschanged = false;
+
   this->course = course;
   this->netcom = netcom;
 
@@ -115,7 +117,7 @@ void MacroWindow::initMacro(QDomNode &node)
       widgets += widgetBuilder(child, mainwidget, this);
     }
 
-    // Insert their values (this must be done last for lua progs to work properly)
+    // Insert their values (this must be done last for scripts to work properly)
     for (int i=0; i<children.count();i++) {
       QDomNode child = children.at(i);
       setValues(child, this);
@@ -140,7 +142,7 @@ bool MacroWindow::doCommit()
   QVector< Widget* >::iterator i = widgets.begin();
   while (i != widgets.end()) {
     Widget* w = *i;
-    if(!w->isValid()) faulty++; // Regexp check, returns valid if entry passed
+    if(!w->isDisabled() && !w->isValid()) faulty++; // Regexp check, returns valid if entry passed
     i++;
   }
 
@@ -167,24 +169,40 @@ void MacroWindow::commit()
     //    close();
   } else {
    QMessageBox::critical(NULL, "Fejl",
-			  "Makroen er ikke udfyldt korrekt, pr�v igen.\n"
+			  "Makroen " + macrotitle + " er ikke udfyldt korrekt, pr�v igen.\n"
 			  , QMessageBox::Ok);
   }
 }
 
 void MacroWindow::reset()
 {
+  /*
   QMessageBox::warning(NULL, tr("Reset"),
                    tr("Du har valgt at nulstille de indtastede data.\n"
                       "Er du sikker?"),
                    QMessageBox::Yes | QMessageBox::Cancel);
   printf("MacroWindow -> resetting...\n");
+  */
+  QVector< Widget* >::iterator i = widgets.begin();
+  while (i != widgets.end()) {
+    Widget* w = *i;
+    w->reset();
+    i++;
+  }
+
+  QVector< Widget* >::iterator j = auxwidgets.begin();
+  while (j != auxwidgets.end()) {
+    Widget* w = *j;
+    w->reset();
+    j++;
+  }
+
+  waschanged = false;
 }
 
 void MacroWindow::cancel()
 {
-  printf("MacroWindow -> cancelling...\n");
-  //  close();
+  collapseWrapper();
 }
 
 void MacroWindow::cont(QString name)
@@ -208,7 +226,7 @@ void MacroWindow::cont(QString name)
     //    close();
   } else {
    QMessageBox::critical(NULL, "Fejl",
-			 "Makroen er ikke udfyldt korrekt, pr�v igen.\n",
+			 "Makroen " + macrotitle + " er ikke udfyldt korrekt, pr�v igen.\n",
 			 QMessageBox::Ok);
   }
   printf("%s : MacroWindow -> continuing...\n", macro.toStdString().c_str());
@@ -235,7 +253,7 @@ void MacroWindow::cont_nocommit(QString name)
     //    close();
   } else {
    QMessageBox::critical(NULL, "Fejl",
-			 "Makroen er ikke udfyldt korrekt, pr�v igen.\n",
+			 "Makroen " + macrotitle + " er ikke udfyldt korrekt, pr�v igen.\n",
 			 QMessageBox::Ok);
   }
   printf("%s : MacroWindow -> continuing...\n", macro.toStdString().c_str());
@@ -272,49 +290,105 @@ void MacroWindow::addAuxWidgets(QVector< Widget* > ws)
   auxwidgets += ws;
 }
 
-void MacroWindow::toggleMacro()
+void MacroWindow::addWidgets(QVector< Widget* > ws)
 {
-  if(isCollapsed()) {
-    widgets.clear();
-    auxwidgets.clear();
-    luaprograms.clear();
+  widgets += ws;
+}
 
-    QDomDocument xml_doc = netcom->send(course, macro);
+void MacroWindow::expandWrapper()
+{
+  if(!isCollapsed()) return;
 
-    //
-    // TODO: This is where the dependency checking should occur.
-    //
+  widgets.clear();
+  auxwidgets.clear();
+  luaprograms.clear();
+  waschanged = false;
+  
+  QDomDocument xml_doc = netcom->send(course, macro);
+  
+  //
+  // TODO: This is where the dependency checking should occur.
+  //
+  
+  // Initiate the new macro window with the xml document and push
+  //  it to the window list
+  QDomNodeList courses = xml_doc.documentElement().childNodes();
+  QDomNode coursenode = courses.at(0); // There can be only one! (Swush, flomp)
+  QDomNodeList macronodes = coursenode.childNodes();
+  for(int j = 0; j < macronodes.count(); j++) {
+    QDomNode macronode = macronodes.at(j);
     
-    // Initiate the new macro window with the xml document and push
-    //  it to the window list
-    QDomNodeList courses = xml_doc.documentElement().childNodes();
-    QDomNode coursenode = courses.at(0); // There can be only one! (Swush, flomp)
-    QDomNodeList macronodes = coursenode.childNodes();
-    for(int j = 0; j < macronodes.count(); j++) {
-      QDomNode macronode = macronodes.at(j);
+    if(true || macronode.childNodes().count()) {
+      // macrowindows.push_back( new MacroWindow( netcom, macronode ) );
+      QDomElement xml_elem = macronode.toElement();
       
-      if(true || macronode.childNodes().count()) {
-        // macrowindows.push_back( new MacroWindow( netcom, macronode ) );
-        QDomElement xml_elem = macronode.toElement();
+      if(xml_elem.tagName() == "macro") {
         
-        if(xml_elem.tagName() == "macro") {
-          
-          // Assign the macro name and version to QStrings for use when comitting
-          QString macroname;
-          if(xml_elem.hasAttribute("name")) {
-            if(xml_elem.attribute("name") == macro) {
-              // update me!
-              initMacro(macronode);
-            }
+        // Assign the macro name and version to QStrings for use when comitting
+        QString macroname;
+        if(xml_elem.hasAttribute("name")) {
+          if(xml_elem.attribute("name") == macro) {
+            // update me!
+            initMacro(macronode);
           }
-        }      
-      }
+        }
+      }      
     }
-    setExpandedWidget(mainwidget);
-    expand();
+  }
+  setExpandedWidget(mainwidget);
+  expand();
+
+  QVector< Widget* >::iterator i = widgets.begin();
+  while (i != widgets.end()) {
+    Widget* w = *i;
+    if(w->setKeyboardFocus()) break;
+    i++;
+  }
+}
 
+void MacroWindow::collapseWrapper()
+{
+  if(isCollapsed()) return;
+
+  if(waschanged) {
+    switch(QMessageBox::warning(NULL,
+                                "Gem �ndringerne i makroen?",
+                                "Du har valgt at lukke makroen " + macrotitle + ".\n"
+                                "�nsker du at gemme inden du lukker?",
+                                QMessageBox::Save | QMessageBox::Close | QMessageBox::Cancel)) {
+    case QMessageBox::Save:
+      if(doCommit()) setCollapsed(true);
+      else QMessageBox::critical(NULL,
+                                 "Fejl",
+                                 "Makroen " + macrotitle + "  er ikke udfyldt korrekt, pr�v igen.\n",
+                                 QMessageBox::Ok);
+      
+      break;
+    case QMessageBox::Close:
+      collapse();
+      break;
+    case QMessageBox::Cancel:
+    default:
+      //      emit expanding(); // signal the other to close again (if any)
+      break;
+    }
   } else {
     collapse();
-    
   }
 }
+
+void MacroWindow::toggleMacro()
+{
+  if(isCollapsed()) {
+    expandWrapper();
+  } else {
+    collapseWrapper();
+  }
+}
+
+void MacroWindow::macroChanged()
+{
+  printf("This macro was changed!\n");
+  emit macroHasChanged();
+  waschanged = true;
+}
diff --git a/client/macrowindow.h b/client/macrowindow.h
index 76d068c..c34c978 100644
--- a/client/macrowindow.h
+++ b/client/macrowindow.h
@@ -58,11 +58,17 @@ public:
   LUA *lua;
 
   Widget *getWidget(QString name);
+
+  // Add a widget that can be seen from script, and will be committed, and validated.
+  void addWidgets(QVector< Widget* >);
+
+  // Add a widget that can only be seen from scripts, but will not be committed or validated.
   void addAuxWidgets(QVector< Widget* >);
 
   void update(QDomNode &xml_doc);
 
   QString macrotitle;
+  bool isstatic;
 
 public slots:
   void commit();
@@ -73,8 +79,14 @@ public slots:
 
   void toggleMacro();
 
+  void macroChanged();
+
+  void collapseWrapper();
+  void expandWrapper();
+
 signals:
   void updateOnCommit();
+  void macroHasChanged();
 
 private:
   void initMacro(QDomNode &node);
@@ -92,6 +104,8 @@ private:
   void close();
 
   NetCom *netcom;
+
+  bool waschanged;
 };
 
 #endif/*__PRACRO_MACROWINDOW_H__*/
diff --git a/client/mainwindow.cc b/client/mainwindow.cc
index 86de48a..639c9d0 100644
--- a/client/mainwindow.cc
+++ b/client/mainwindow.cc
@@ -34,15 +34,20 @@
 #include <QHBoxLayout>
 #include <QLabel>
 #include <QPushButton>
-#include <QGroupBox>
 #include <QScrollArea>
+#include <QSettings>
+#include <QStatusBar>
+
+#include "macrodrawer.h"
 
 MainWindow::MainWindow(QString cpr, QString course, QString host, quint16 port, QString user)
-  : netcom(host, port, user, cpr)
+  : QMainWindow(0, Qt::WindowContextHelpButtonHint),
+    netcom(host, port, user, cpr)
 {
-  resize(768, 1024);
+  setWindowTitle("Pracro - " + cpr);
 
-  setWindowFlags(windowFlags() | Qt::WindowContextHelpButtonHint );
+  QStatusBar *status = statusBar();
+  status->addPermanentWidget(new QLabel("Pracro v.1.0.0 - test2"));
 
   QScrollArea *s = new QScrollArea();
   setCentralWidget(s);
@@ -54,14 +59,33 @@ MainWindow::MainWindow(QString cpr, QString course, QString host, quint16 port,
   this->course = course;
 
   init();
+
+  status->showMessage("Makroen blev succesfuldt indl�st.");
 }
 
 MainWindow::~MainWindow()
 {
 }
 
+void MainWindow::closeEvent(QCloseEvent *)
+{
+  QSettings settings("Aasimon.org", "Pracro");
+
+  settings.beginGroup("MainWindow");
+  settings.setValue("size", size());
+  settings.setValue("pos", pos());
+  settings.endGroup();
+}
+
 void MainWindow::init()
 {
+  QSettings settings("Aasimon.org", "Pracro");
+
+  settings.beginGroup("MainWindow");
+  resize(settings.value("size", QSize(700, 800)).toSize());
+  move(settings.value("pos", QPoint(0, 0)).toPoint());
+  settings.endGroup();
+
   initialising = true;
   update();
   initialising = false;
@@ -108,31 +132,10 @@ void MainWindow::update()
         if(xml_elem.attribute("static", "false") == "true") isstatic = true;
         if(xml_elem.attribute("compact", "false") == "true") iscompact = true;
         macros[macroname] = new MacroWindow(&netcom, macronode, course, !isstatic, iscompact);
+        macros[macroname]->isstatic = isstatic;
 
-        QGroupBox *g = new QGroupBox();
-        g->setTitle("      " + xml_elem.attribute("caption", macroname));
-        g->setFlat(true);
-        {
-          QFont f = g->font();
-          //f.setBold(true);
-          f.setItalic(true);
-          g->setFont(f);
-        }
+        MacroDrawer *g = new MacroDrawer(macros[macroname], xml_elem.attribute("caption", macroname));
         ((QBoxLayout*)w->layout())->addWidget(g);
-
-        if(!isstatic) {
-          QPushButton *b = new QPushButton("�", g);
-          b->setFixedSize(16,16);
-          b->move(0,0);
-          {
-            QFont f = b->font();
-            f.setBold(false);
-            f.setItalic(false);
-            b->setFont(f);
-          }
-
-          connect(b, SIGNAL(clicked()), macros[macroname], SLOT(toggleMacro()));
-        }
         
         QHBoxLayout *l = new QHBoxLayout();
         l->setContentsMargins(10,0,10,0);
@@ -145,6 +148,7 @@ void MainWindow::update()
           f.setItalic(false);
           macros[macroname]->setFont(f);
         }
+
       } else {
 
         macros[macroname]->update(macronode);
@@ -155,5 +159,26 @@ void MainWindow::update()
       }
     }
   }
-}
 
+  // Make sure that all macros will collapse when a new one is expanded.
+  QMap< QString, MacroWindow* >::iterator i = macros.begin();
+  while(i != macros.end()) {
+    MacroWindow *m1 = i.value();
+
+    QMap< QString, MacroWindow* >::iterator j = macros.begin();
+    while(j != macros.end()) {
+      MacroWindow *m2 = j.value();
+
+      if(m1 != m2 && m2->isstatic == false) {
+        // Remove old connection (if any), to avoid multiple connections.
+        disconnect(m1, SIGNAL(expanding()), m2, SLOT(collapseWrapper()));
+
+        connect(m1, SIGNAL(expanding()), m2, SLOT(collapseWrapper()));
+      }
+
+      j++;
+    }
+
+    i++;
+  }
+}
diff --git a/client/mainwindow.h b/client/mainwindow.h
index f4b12ec..f23ade1 100644
--- a/client/mainwindow.h
+++ b/client/mainwindow.h
@@ -39,6 +39,8 @@ public:
   MainWindow(QString cpr, QString course, QString host, quint16 port, QString user);
   ~MainWindow();
 
+  void closeEvent(QCloseEvent *event);
+
 public slots:
   void update();
 
@@ -52,7 +54,6 @@ private:
   bool initialising;
 
   void init();
-
 };
 
 #endif/*__PRACRO_MAINWINDOW_H__*/
diff --git a/client/netcom.cc b/client/netcom.cc
index ab2446e..b6b5bd9 100644
--- a/client/netcom.cc
+++ b/client/netcom.cc
@@ -101,7 +101,6 @@ void NetCom::send(QVector< Widget* > widgets, QString macro, QString version)
   printf("Socket state: %d\n", socket.state());
   if(socket.state() != 3) printf("Socket state not connected: %s\n", socket.errorString().toStdString().c_str());
 
-  qApp->activeWindow()->setEnabled(false);
   if(qApp->activeWindow()) qApp->activeWindow()->setEnabled(false);
 
   QDomDocument doc;
@@ -125,10 +124,12 @@ void NetCom::send(QVector< Widget* > widgets, QString macro, QString version)
   while (i != widgets.end()) {
     Widget* w = *i;
     
-    QDomElement field_elem = doc.createElement("field");
-    field_elem.setAttribute("name", w->getName());
-    field_elem.setAttribute("value", w->getValue());
-    commit_elem.appendChild(field_elem);
+    if(!w->isDisabled()) {
+      QDomElement field_elem = doc.createElement("field");
+      field_elem.setAttribute("name", w->getName());
+      field_elem.setAttribute("value", w->getValue());
+      commit_elem.appendChild(field_elem);
+    }
 
     i++;
   }
diff --git a/client/resumewidget.cc b/client/resumewidget.cc
index f43c4df..6877c54 100644
--- a/client/resumewidget.cc
+++ b/client/resumewidget.cc
@@ -42,7 +42,7 @@ ResumeWidget::ResumeWidget(bool compact)
   this->compact = compact;
   setLayout(new QHBoxLayout());
   layout()->setContentsMargins(10,2,2,2);
-  resume = new QLabel("<font style='color: #ccc;'>Endnu ikke udfyldt</font>");
+  resume = new QLabel("<font style='color: #bbb;'>Endnu ikke udfyldt</font>");
 
 #ifdef RICH
   resume->setTextFormat(Qt::RichText);
diff --git a/client/widgetbuilder.cc b/client/widgetbuilder.cc
index 3d47d41..848157c 100644
--- a/client/widgetbuilder.cc
+++ b/client/widgetbuilder.cc
@@ -28,7 +28,8 @@
 #include <QLayout>
 #include "widgets.h"
 
-QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent, MacroWindow *macrowindow)
+QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent, 
+                                 MacroWindow *macrowindow, bool watchChanges)
 {
   QVector< Widget* > widgets;
 
@@ -42,11 +43,11 @@ QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent, MacroWindow
   } else if(xml_elem.tagName() == "frame") {
     if(xml_elem.hasAttribute("caption")) {
       GroupBox *frame = new GroupBox(xml_elem, macrowindow);
-      widgets.push_back(frame);
+      //      widgets.push_back(frame);
       widget = frame;
     } else {
       Frame *frame = new Frame(xml_elem, macrowindow);
-      widgets.push_back(frame);
+      //      widgets.push_back(frame);
       widget = frame;
     }
 
@@ -58,63 +59,91 @@ QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent, MacroWindow
     LineEdit *lineedit = new LineEdit(xml_elem, macrowindow);
     widgets.push_back(lineedit);
     widget = lineedit;
+    if(watchChanges)
+      lineedit->connectFrom(SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
+
+  } else if(xml_elem.tagName() == "datetime") {
+    DateTime *datetime = new DateTime(xml_elem, macrowindow);
+    widgets.push_back(datetime);
+    widget = datetime;
+    if(watchChanges)
+      datetime->connectFrom(SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
 
   } else if(xml_elem.tagName() == "button") {
     Button *button = new Button(xml_elem, macrowindow);
     //macrowindow->connect(pushbutton, SIGNAL(act_continue()), main, SLOT(get_macro()));
-    macrowindow->connect(button, SIGNAL(act_commit()), macrowindow, SLOT(commit()));
-    macrowindow->connect(button, SIGNAL(act_reset()), macrowindow, SLOT(reset()));
-    macrowindow->connect(button, SIGNAL(act_cancel()), macrowindow, SLOT(cancel()));
-    macrowindow->connect(button, SIGNAL(act_continue(QString)), macrowindow, SLOT(cont(QString)));
-    macrowindow->connect(button, SIGNAL(act_continue_nocommit(QString)),
-                         macrowindow, SLOT(cont_nocommit(QString)));
+    QObject::connect(button, SIGNAL(act_commit()), macrowindow, SLOT(commit()));
+    QObject::connect(button, SIGNAL(act_reset()), macrowindow, SLOT(reset()));
+    QObject::connect(button, SIGNAL(act_cancel()), macrowindow, SLOT(cancel()));
+    QObject::connect(button, SIGNAL(act_continue(QString)), macrowindow, SLOT(cont(QString)));
+    QObject::connect(button, SIGNAL(act_continue_nocommit(QString)),
+                     macrowindow, SLOT(cont_nocommit(QString)));
+    QObject::connect(macrowindow, SIGNAL(macroHasChanged()), button, SLOT(do_enable()));
     widget = button;
 
   } else if(xml_elem.tagName() == "textedit") {
     TextEdit *textedit = new TextEdit(xml_elem, macrowindow);
     widgets.push_back(textedit);
     widget = textedit;
+    if(watchChanges)
+      QObject::connect(textedit, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
 
   } else if(xml_elem.tagName() == "checkbox") {
     CheckBox *checkbox = new CheckBox(xml_elem, macrowindow);
     widgets.push_back(checkbox);
     widget = checkbox;
+    if(watchChanges)
+      QObject::connect(checkbox, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
 
   } else if(xml_elem.tagName() == "radiobuttons") {
     RadioButtons *radiobuttons = new RadioButtons(xml_elem, macrowindow);
     widgets.push_back(radiobuttons);
     widget = radiobuttons;
+    if(watchChanges)
+      QObject::connect(radiobuttons, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
     //return; // Don't iterate children
 
   } else if(xml_elem.tagName() == "combobox") {
     ComboBox *combobox = new ComboBox(xml_elem, macrowindow);
     widgets.push_back(combobox);
     widget = combobox;
+    if(watchChanges)
+      QObject::connect(combobox, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
     //return; // Don't iterate children
 
   } else if(xml_elem.tagName() == "dbwidget") {
     DBWidget *dbwidget = new DBWidget(xml_elem, macrowindow);
     widgets.push_back(dbwidget);
     widget = dbwidget;
+    if(watchChanges)
+      QObject::connect(dbwidget, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
 
   } else if(xml_elem.tagName() == "listbox") {
     ListBox *listbox = new ListBox(xml_elem, macrowindow);
     widgets.push_back(listbox);
     widget = listbox;
+    if(watchChanges)
+      QObject::connect(listbox, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
     //return; // Don't iterate children
+
   } else if(xml_elem.tagName() == "multilist") {
     MultiList *multilist = new MultiList(xml_elem, macrowindow);
     widgets.push_back(multilist);
     widget = multilist;
+    if(watchChanges)
+      QObject::connect(multilist, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
 
     if(parent && widget && parent->layout()) parent->layout()->addWidget(widget);
     if(widget) widget->show();
     
     return widgets; // Don't iterate children
+
   } else if(xml_elem.tagName() == "altcombobox") {
     AltComboBox *altcombobox = new AltComboBox(xml_elem, macrowindow);
     widgets.push_back(altcombobox);
     widget = altcombobox;
+    if(watchChanges)
+      QObject::connect(altcombobox, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
 
     if(parent && widget && parent->layout()) parent->layout()->addWidget(widget);
     if(widget) widget->show();
@@ -124,6 +153,8 @@ QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent, MacroWindow
     MetaWidget *metawidget = new MetaWidget(xml_elem, macrowindow);
     widgets.push_back(metawidget);
     widget = metawidget;
+    if(watchChanges)
+      QObject::connect(metawidget, SIGNAL(wasChanged()), macrowindow, SLOT(macroChanged()));
 
     if(parent && widget && parent->layout()) parent->layout()->addWidget(widget);
     if(widget) widget->show();
@@ -135,7 +166,7 @@ QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent, MacroWindow
 
   for (int i=0; i<children.count();i++) {
     QDomNode child = children.at(i);
-    widgets += widgetBuilder(child, widget, macrowindow);
+    widgets += widgetBuilder(child, widget, macrowindow, watchChanges);
   }
 
   if(parent && widget && parent->layout()) parent->layout()->addWidget(widget);
@@ -152,7 +183,7 @@ void setValues(QDomNode xml_node, MacroWindow *macrowindow)
 
   if(xml_elem.hasAttribute("name") && xml_elem.hasAttribute("value")) {
     Widget *widget = macrowindow->getWidget(xml_elem.attribute("name"));
-    if(widget) widget->setValue(xml_elem.attribute("value"));
+    if(widget) widget->setValue(xml_elem.attribute("value"), xml_elem.attribute("prefilled", ""));
   }
 
   QDomNodeList children = xml_node.childNodes();
diff --git a/client/widgetbuilder.h b/client/widgetbuilder.h
index 21bf15e..7b77e67 100644
--- a/client/widgetbuilder.h
+++ b/client/widgetbuilder.h
@@ -33,7 +33,8 @@
 #include "widgets/widget.h"
 #include "macrowindow.h"
 
-QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent, MacroWindow *macrowindow);
+QVector< Widget* > widgetBuilder(QDomNode xml_node, QWidget *parent,
+                                 MacroWindow *macrowindow, bool watchChanges = true);
 void setValues(QDomNode xml_node, MacroWindow *macrowindow);
 
 #endif/*__PRACRO_WIDGETBUILDER_H__*/
diff --git a/client/widgets.h b/client/widgets.h
index dda0213..bc0a83b 100644
--- a/client/widgets.h
+++ b/client/widgets.h
@@ -32,6 +32,7 @@
 #include "widgets/lineedit.h"
 #include "widgets/textedit.h"
 #include "widgets/button.h"
+#include "widgets/datetime.h"
 #include "widgets/dbwidget.h"
 #include "widgets/combobox.h"
 #include "widgets/listbox.h"
diff --git a/client/widgets/altcombobox.cc b/client/widgets/altcombobox.cc
index 01fd36f..c68f816 100644
--- a/client/widgets/altcombobox.cc
+++ b/client/widgets/altcombobox.cc
@@ -33,6 +33,7 @@
 #include "widgetbuilder.h"
 
 #include <QObject>
+#include "multilist.h"
 
 AltComboBox::AltComboBox(QDomNode &node, MacroWindow *macrowindow)
   : QFrame(), Widget(node, macrowindow)
@@ -45,7 +46,11 @@ AltComboBox::AltComboBox(QDomNode &node, MacroWindow *macrowindow)
   combobox = new ComboBox(node, macrowindow);
   layout()->addWidget(combobox);
   combobox->show();
-  
+
+  altframerepl = new QFrame();
+  QHBoxLayout *l = new QHBoxLayout();
+  altframerepl->setLayout(l);
+  l->addStretch();
   altframe = new QFrame();
   layout()->addWidget(altframe);
 
@@ -87,7 +92,7 @@ AltComboBox::AltComboBox(QDomNode &node, MacroWindow *macrowindow)
       QDomNodeList children = item.childNodes();
       for(int i = 0; i < children.count(); i++) {
         QDomNode child = children.at(i);
-        widgets += widgetBuilder(child, altframe, macrowindow);
+        widgets += widgetBuilder(child, altframe, macrowindow, false);
       }
     }
 
@@ -108,22 +113,31 @@ AltComboBox::AltComboBox(QDomNode &node, MacroWindow *macrowindow)
            iwname.toStdString().c_str());
   }
 
+  // To detect if the altvalue has been selected:
   connect(combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(onValueChange(int)));
   connect(combobox, SIGNAL(editTextChanged(const QString&)), this, SLOT(onValueChange(const QString&)));
 
+  // To react to changes in any of the children:
+  connect(combobox, SIGNAL(wasChanged()), this, SLOT(onChildChange()));
+  innerwidget->connectFrom(SIGNAL(wasChanged()), this, SLOT(onChildChange()));
+
   layout()->setContentsMargins(0,0,0,0);
   altframe->layout()->setContentsMargins(0,0,0,0);
+
+  show(); // Force altframe to get resized to its real size.
+  altframerepl->setFixedHeight(altframe->height());
 }
 
+
 bool AltComboBox::isValid()
 {
   if(!combobox->isValid()) return false;
 
   if(innerwidget && combobox->getValue() == altvalue) {
-    return innerwidget->isValid();
+    if(!innerwidget->isValid()) return false;
   }
 
-  return true;
+  return regexpValidator() && luaValidator();
 }
 
 QString AltComboBox::getValue()
@@ -136,25 +150,41 @@ QString AltComboBox::getValue()
   }
 }
 
-void AltComboBox::setValue(QString value)
+void AltComboBox::setValue(QString value, QString source)
 {
-  combobox->setValue(value);
+  //  if(isUserSource(source)) emit wasChanged(); // No need for this, it will be enitted by the children.
+
+  if(combobox->findData(value) != -1) {
+
+    combobox->setValue(value, source);
 
-  if(combobox->isValid() == false) { // Combobox contain idx == -1 (invalid) if value didn't exist.
+  } else {
     combobox->setValue(altvalue);
 
     if(innerwidget) {
-      innerwidget->setValue(value);
+      innerwidget->setValue(value, source);
     }
   }
+
+  setInitialValue(value);
 }
 
 void AltComboBox::onValueChange(int index)
 {
   if(combobox->itemData(index).toString() == altvalue) {
-    altframe->setEnabled(true);
+    //    altframe->setEnabled(true);
+    altframerepl->setVisible(false);
+    layout()->removeWidget(altframerepl);
+
+    layout()->addWidget(altframe);
+    altframe->setVisible(true);
   } else {
-    altframe->setEnabled(false);
+    //    altframe->setEnabled(false);
+    altframe->setVisible(false);
+    layout()->removeWidget(altframe);
+
+    layout()->addWidget(altframerepl);
+    altframerepl->setVisible(true);
   }
 }
 
@@ -172,3 +202,35 @@ void AltComboBox::disable()
 {
   setEnabled(false);
 }
+
+void AltComboBox::onChildChange()
+{
+  emit wasChanged();
+}
+
+void AltComboBox::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void AltComboBox::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool AltComboBox::setKeyboardFocus()
+{
+  if(combobox->getValue() == altvalue) {
+    if(innerwidget) return innerwidget->setKeyboardFocus();
+  }
+
+  combobox->setKeyboardFocus();
+  return true;
+}
+
+void AltComboBox::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/altcombobox.h b/client/widgets/altcombobox.h
index 222c797..5504f4a 100644
--- a/client/widgets/altcombobox.h
+++ b/client/widgets/altcombobox.h
@@ -44,20 +44,34 @@ public:
   bool isValid();
 
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
 
   void enable();
   void disable();
 
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  bool setKeyboardFocus();
+  void setVisibility(bool visible);
+
 public slots:
   void onValueChange(int index);
   void onValueChange(const QString &text);
+  void onChildChange();
+
+signals:
+  void wasChanged();
 
 private:
   ComboBox *combobox;
   Widget *innerwidget;
   QString altvalue;
   QWidget *altframe;
+  QWidget *altframerepl;
 };
 
 #endif/*__PRACRO_ALTCOMBOBOX_H__*/
diff --git a/client/widgets/button.cc b/client/widgets/button.cc
index c524084..6032678 100644
--- a/client/widgets/button.cc
+++ b/client/widgets/button.cc
@@ -32,6 +32,7 @@
 Button::Button(QDomNode &node, MacroWindow *macrowindow)
   : QPushButton(), Widget(node, macrowindow)
 {
+  resetToDisabled = false;
   setCommonAttributes(this, node);
 
   QDomElement elem = node.toElement();
@@ -49,12 +50,27 @@ Button::Button(QDomNode &node, MacroWindow *macrowindow)
   if(elem.hasAttribute("action")) {
     if(elem.attribute("action") == "commit") {
       connect(this, SIGNAL(clicked()), this, SLOT(commit()));
+      setIcon(QPixmap(":icons/add.png"));
+      setEnabled(false);
+
+      //
+      // Hack to re-disable the commit button when the macro is reset.
+      //
+      resetToDisabled = true;
+      widget_name = "commit_button_" + QString::number((int)this);
+      QVector< Widget* > ws;
+      ws.push_back(this);
+      macrowindow->addAuxWidgets(ws);
+
     } else if(elem.attribute("action") == "reset") {
-      connect(this, SIGNAL(clicked()), this, SLOT(reset()));
+      connect(this, SIGNAL(clicked()), this, SLOT(_reset()));
+      setIcon(QPixmap(":icons/del.png"));
     } else if(elem.attribute("action") == "cancel") {
       connect(this, SIGNAL(clicked()), this, SLOT(cancel()));
+      setIcon(QPixmap(":icons/del.png"));
     } else if(elem.attribute("action") == "continue") {
       connect(this, SIGNAL(clicked()), this, SLOT(cont()));
+      setIcon(QPixmap(":icons/add.png"));
     } else if(elem.attribute("action") == "continue_nocommit") {
       connect(this, SIGNAL(clicked()), this, SLOT(cont_nocommit()));
     } 
@@ -69,7 +85,7 @@ void Button::commit()
   printf("Emit: commit\n");
 }
 
-void Button::reset()
+void Button::_reset()
 {
   emit act_reset();
   printf("Emit: reset\n");
@@ -92,3 +108,36 @@ void Button::cont_nocommit()
   emit act_continue_nocommit(field);
   printf("Emit: continue_nocommit\n");
 }
+
+void Button::do_enable()
+{
+  setEnabled(true);
+}
+
+void Button::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void Button::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+void Button::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
+
+bool Button::setKeyboardFocus()
+{
+  setFocus();
+  return true;
+}
+
+void Button::reset()
+{
+  if(resetToDisabled) setEnabled(false);
+}
diff --git a/client/widgets/button.h b/client/widgets/button.h
index aba7ac7..d9d0b26 100644
--- a/client/widgets/button.h
+++ b/client/widgets/button.h
@@ -39,12 +39,25 @@ public:
   Button(QDomNode &node, MacroWindow *macrowindow);
   QString field;
 
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  void setVisibility(bool visible);
+
+  bool setKeyboardFocus();
+
+  void reset();
+
 public slots:
   void commit();
-  void reset();
+  void _reset();
   void cancel();
   void cont();
   void cont_nocommit();
+  void do_enable();
 
 signals:
   void act_commit();
@@ -54,6 +67,6 @@ signals:
   void act_continue_nocommit(QString);
 
 private:
-
+  bool resetToDisabled;
 };
 #endif/*__PRACRO_BUTTON_H__*/
diff --git a/client/widgets/checkbox.cc b/client/widgets/checkbox.cc
index 1b8d64b..4fbdad1 100644
--- a/client/widgets/checkbox.cc
+++ b/client/widgets/checkbox.cc
@@ -31,6 +31,8 @@
 CheckBox::CheckBox(QDomNode &node, MacroWindow *macrowindow)
   : QCheckBox(), Widget(node, macrowindow)
 {
+  changedByUser = true;
+  
   setCommonAttributes(this, node);
 
   QDomElement elem = node.toElement();
@@ -51,7 +53,7 @@ CheckBox::CheckBox(QDomNode &node, MacroWindow *macrowindow)
     falsevalue = "false";
   }
   
-  connect(this, SIGNAL(stateChanged(int)), this, SLOT(state_change()));
+  connect(this, SIGNAL(stateChanged(int)), this, SLOT(state_change(int)));
 }
 
 QString CheckBox::getValue()
@@ -60,8 +62,12 @@ QString CheckBox::getValue()
   return falsevalue;
 }
 
-void CheckBox::setValue(QString value)
+void CheckBox::setValue(QString value, QString source)
 {
+  if(isUserSource(source)) emit wasChanged();
+
+  changedByUser = false;
+
   bool old = isChecked();
 
   if(value == truevalue) {
@@ -73,7 +79,11 @@ void CheckBox::setValue(QString value)
   }
 
   // If set operation did not change the value we must invocate the code manually.
-  if(old == isChecked()) state_change();
+  if(old == isChecked()) state_change(0);
+
+  setInitialValue(value);
+
+  changedByUser = true;
 }
 /*
 bool CheckBox::isValid()
@@ -81,7 +91,31 @@ bool CheckBox::isValid()
   return luaValidator();
 }
 */
-void CheckBox::state_change()
+void CheckBox::state_change(int)
 {
+  if(changedByUser) emit wasChanged();
   luaValidator();
 }
+
+void CheckBox::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void CheckBox::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+void CheckBox::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
+
+bool CheckBox::setKeyboardFocus()
+{
+  setFocus();
+  return true;
+}
diff --git a/client/widgets/checkbox.h b/client/widgets/checkbox.h
index 89f1f1b..34cac6b 100644
--- a/client/widgets/checkbox.h
+++ b/client/widgets/checkbox.h
@@ -40,14 +40,29 @@ public:
   //  bool isValid();
 
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  void setVisibility(bool visible);
+
+  bool setKeyboardFocus();
 
 public slots:
-  void state_change();
+  void state_change(int);
+
+signals:
+  void wasChanged();
 
 private:
   QString truevalue;
   QString falsevalue;
+
+  bool changedByUser;
 };
 
 #endif/*__PRACRO_CHECKBOX_H__*/
diff --git a/client/widgets/combobox.cc b/client/widgets/combobox.cc
index 630a449..4db6172 100644
--- a/client/widgets/combobox.cc
+++ b/client/widgets/combobox.cc
@@ -77,7 +77,6 @@ ComboBox::ComboBox(QDomNode &node, MacroWindow *macrowindow)
     setEditable(true);
 
     connect(this, SIGNAL(editTextChanged(QString)), this, SLOT(changed()));
-    //setEditText(elem.attribute("value"));
     break;
 
   case SEARCH:
@@ -90,6 +89,7 @@ ComboBox::ComboBox(QDomNode &node, MacroWindow *macrowindow)
       }
       rxs += ")";
       rx = QRegExp(rxs);
+      rx.setCaseSensitivity(Qt::CaseInsensitive);
     }
     {    
       QCompleter *completer = new QCompleter(itemlist, this);
@@ -100,11 +100,12 @@ ComboBox::ComboBox(QDomNode &node, MacroWindow *macrowindow)
     }
 
     connect(this, SIGNAL(editTextChanged(QString)), this, SLOT(changed()));
-    //setEditText(elem.attribute("value"));
     break;
   }
 
+  ischangingbyuser = false;
   changed();
+  ischangingbyuser = true;
 }
 
 QString ComboBox::getValue()
@@ -119,13 +120,20 @@ QString ComboBox::getValue()
   return value;
 }
 
-void ComboBox::setValue(QString value)
+void ComboBox::setValue(QString value, QString source)
 {
+  if(isUserSource(source)) emit wasChanged();
+
   int idx = findData(value);
 
   printf("setValue(\"%s\") - %d\n", value.toStdString().c_str(), idx);
 
+  ischangingbyuser = false;
   setCurrentIndex(idx);
+  ischangingbyuser = true;
+
+  setInitialValue(value);
+
 }
 
 bool ComboBox::isValid()
@@ -134,17 +142,12 @@ bool ComboBox::isValid()
     if(currentIndex() != -1) return true;
     else return false;
   }
-  return rx.exactMatch(currentText());
+  return rx.exactMatch(currentText()) && luaValidator();
 }
 
 void ComboBox::changed()
 {
-  /*
-  if(combotype == SELECT) {
-    luaValidator();
-    return;
-  }
-  */
+  if(ischangingbyuser) emit wasChanged();
 
   QPalette palette;
 
@@ -169,3 +172,34 @@ void ComboBox::disable()
 {
   setEnabled(false);
 }
+
+void ComboBox::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void ComboBox::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+/*
+bool ComboBox::eventFilter(QObject *, QEvent *event)
+{
+  if (event->type() == QEvent::KeyPress) emit wasChanged();
+  return false;
+}
+*/
+
+bool ComboBox::setKeyboardFocus()
+{
+  setFocus();
+  return true;
+}
+
+void ComboBox::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/combobox.h b/client/widgets/combobox.h
index f072bea..2691ec3 100644
--- a/client/widgets/combobox.h
+++ b/client/widgets/combobox.h
@@ -47,18 +47,34 @@ public:
   bool isValid();
 
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
 
   void enable();
   void disable();
 
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  bool setKeyboardFocus();
+  void setVisibility(bool visible);
+
 public slots:
   void changed();
 
+signals:
+  void wasChanged();
+
+protected:
+  //  bool eventFilter(QObject *, QEvent *event);
+
 private:
   QRegExp rx;
   QString combo_value;
   types_t combotype;
+  bool ischangingbyuser;
 };
 
 #endif/*__PRACRO_COMBOBOX_H__*/
diff --git a/client/widgets/common.cc b/client/widgets/common.cc
index f39c607..b650701 100644
--- a/client/widgets/common.cc
+++ b/client/widgets/common.cc
@@ -96,3 +96,10 @@ void setCommonLayout(QWidget *widget, QDomNode &node)
   widget->setContentsMargins(0,0,0,0);
   //widget->layout()->setContentsMargins(0,0,0,0);
 }
+
+bool isUserSource(QString source)
+{
+  if(source == "pentominos") return true;
+  if(source == "pracro") return false;
+  return false;
+}
diff --git a/client/widgets/common.h b/client/widgets/common.h
index 11ce317..a3db783 100644
--- a/client/widgets/common.h
+++ b/client/widgets/common.h
@@ -32,5 +32,6 @@
 
 void setCommonAttributes(QWidget *widget, QDomNode &node);
 void setCommonLayout(QWidget *widget, QDomNode &node);
+bool isUserSource(QString source);
 
 #endif/*__PRACRO_COMMON_H__*/
diff --git a/client/widgets/datetime.cc b/client/widgets/datetime.cc
new file mode 100644
index 0000000..1ca52ed
--- /dev/null
+++ b/client/widgets/datetime.cc
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ *            datetime.cc
+ *
+ *  Fri Jan  9 09:00:12 CET 2009
+ *  Copyright 2009 Bent Bisballe Nyeng
+ *  deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ *  This file is part of Pracro.
+ *
+ *  Pracro is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Pracro is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Pracro; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#include "datetime.h"
+
+#include "common.h"
+
+DateTime::DateTime(QDomNode &node, MacroWindow *macrowindow)
+  : QDateTimeEdit(), Widget(node, macrowindow)
+{
+  changedByUser = true;
+  setCommonAttributes(this, node);
+
+  setCalendarPopup(true);
+  setMinimumDateTime(QDateTime::fromTime_t(0));
+
+  QDomElement elem = node.toElement();
+  /*
+  if(elem.hasAttribute("readonly")) {
+    if(elem.attribute("readonly") == "true" || elem.attribute("readonly") == "1") {
+      setReadOnly(true);
+    } else if(elem.attribute("readonly") == "false" || elem.attribute("readonly") == "0") {
+      setReadOnly(false);
+    } else {
+      printf("Unknown value of readonly: %s\n", elem.attribute("readonly").toStdString().c_str());
+    }
+  }
+  */
+  connect(this, SIGNAL(dateTimeChanged(const QDateTime &)),
+          this, SLOT(changed(const QDateTime &)));
+}
+
+void DateTime::changed(const QDateTime &)
+{
+  QPalette palette;
+
+  if(luaValidator()) {
+    // valid string
+    palette.setBrush(QPalette::Base, QBrush(QColor(255, 255, 255)));
+  } else {
+    // invalid string
+    palette.setBrush(QPalette::Base, QBrush(QColor(200, 230, 200)));
+  }
+
+  setPalette(palette);
+
+  if(changedByUser) emit wasChanged(); 
+}
+
+QString DateTime::getValue()
+{
+  return QString::number(dateTime().toUTC().toTime_t());
+}
+
+void DateTime::setValue(QString value, QString source)
+{
+  changedByUser = false;
+  if(isUserSource(source)) emit wasChanged();
+
+  setDateTime(QDateTime::fromTime_t(value.toUInt()));
+
+  setInitialValue(value);
+  changedByUser = true;
+}
+
+void DateTime::enable()
+{
+  setEnabled(true);
+}
+
+void DateTime::disable()
+{
+  setEnabled(false);
+}
+
+void DateTime::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void DateTime::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool DateTime::setKeyboardFocus()
+{
+  setFocus();
+  return true;
+}
+
+void DateTime::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/datetime.h b/client/widgets/datetime.h
new file mode 100644
index 0000000..0fb5703
--- /dev/null
+++ b/client/widgets/datetime.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ *            datetime.h
+ *
+ *  Fri Jan  9 09:00:12 CET 2009
+ *  Copyright 2009 Bent Bisballe Nyeng
+ *  deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ *  This file is part of Pracro.
+ *
+ *  Pracro is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Pracro is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Pracro; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#ifndef __PRACRO_DATETIME_H__
+#define __PRACRO_DATETIME_H__
+
+#include "widget.h"
+#include <QDateTimeEdit>
+#include <QDomNode>
+
+class DateTime : public QDateTimeEdit, public Widget
+{
+Q_OBJECT
+public:
+  DateTime(QDomNode &node, MacroWindow *macrowindow);
+
+  QString getValue();
+  void setValue(QString value, QString source = "");
+  void enable();
+  void disable();
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  bool setKeyboardFocus();
+  void setVisibility(bool visible);
+
+public slots:
+  void changed(const QDateTime &);
+
+signals:
+  void wasChanged();
+
+private:
+  bool changedByUser;
+};
+
+#endif/*__PRACRO_DATETIME_H__*/
diff --git a/client/widgets/dbwidget.cc b/client/widgets/dbwidget.cc
index 1a68d2a..00b3886 100644
--- a/client/widgets/dbwidget.cc
+++ b/client/widgets/dbwidget.cc
@@ -40,6 +40,8 @@
 DBWidget::DBWidget(QDomNode &node, MacroWindow *macrowindow)
   : QComboBox(), Widget(node, macrowindow)
 {
+  changedByUser = true;
+
   QDomElement elem = node.toElement();
 
   if(!elem.hasAttribute("driver") ||
@@ -108,9 +110,15 @@ QString DBWidget::getValue()
   return value;
 }
 
-void DBWidget::setValue(QString value)
+void DBWidget::setValue(QString value, QString source)
 {
+  changedByUser = false;
+  if(isUserSource(source)) emit wasChanged();
+
   setEditText(value);
+
+  setInitialValue(value);
+  changedByUser = true;
 }
 
 bool DBWidget::isValid()
@@ -133,6 +141,8 @@ void DBWidget::changed()
   }
 
   lineEdit()->setPalette(palette);
+
+  if(changedByUser) emit wasChanged();
 }
 
 void DBWidget::update_list(QString prefix)
@@ -175,6 +185,29 @@ void DBWidget::disable()
 {
   setEnabled(false);
 }
+
+void DBWidget::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void DBWidget::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool DBWidget::setKeyboardFocus()
+{
+  setFocus();
+  return true;
+}
+
+void DBWidget::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
 /*
 $ psql -h sensei -d lms -U postgres
 ===================================================================
diff --git a/client/widgets/dbwidget.h b/client/widgets/dbwidget.h
index 98499b9..13e552d 100644
--- a/client/widgets/dbwidget.h
+++ b/client/widgets/dbwidget.h
@@ -45,15 +45,28 @@ public:
   bool isValid();
 
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
 
   void enable();
   void disable();
 
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  void setVisibility(bool visible);
+
+  bool setKeyboardFocus();
+
 public slots:
   void changed();
   void update_list(QString prefix);
 
+signals:
+  void wasChanged();
+
 protected:
   //  void focusInEvent(QFocusEvent *);
   
@@ -64,6 +77,8 @@ private:
   QString from;
   QString where;
   QString format;
+
+  bool changedByUser;
 };
 
 #endif/*__PRACRO_DBWIDGET_H__*/
diff --git a/client/widgets/frame.cc b/client/widgets/frame.cc
index 79e060b..04a0e90 100644
--- a/client/widgets/frame.cc
+++ b/client/widgets/frame.cc
@@ -48,3 +48,20 @@ void Frame::disable()
 {
   setEnabled(false);
 }
+
+void Frame::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void Frame::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+void Frame::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/frame.h b/client/widgets/frame.h
index 4fabd40..5375d06 100644
--- a/client/widgets/frame.h
+++ b/client/widgets/frame.h
@@ -38,6 +38,13 @@ public:
 
   void enable();
   void disable();
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+  void setVisibility(bool visible);
 };
 
 #endif/*__PRACRO_FRAME_H__*/
diff --git a/client/widgets/groupbox.cc b/client/widgets/groupbox.cc
index c3947a7..f6c9b28 100644
--- a/client/widgets/groupbox.cc
+++ b/client/widgets/groupbox.cc
@@ -54,3 +54,20 @@ void GroupBox::disable()
 {
   setEnabled(false);
 }
+
+void GroupBox::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void GroupBox::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+void GroupBox::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/groupbox.h b/client/widgets/groupbox.h
index 99e05ce..d1ffb86 100644
--- a/client/widgets/groupbox.h
+++ b/client/widgets/groupbox.h
@@ -38,6 +38,13 @@ public:
 
   void enable();
   void disable();
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+  void setVisibility(bool visible);
 };
 
 #endif/*__PRACRO_GROUPBOX_H__*/
diff --git a/client/widgets/label.cc b/client/widgets/label.cc
index d4f5387..3f69861 100644
--- a/client/widgets/label.cc
+++ b/client/widgets/label.cc
@@ -59,5 +59,21 @@ Label::Label(QDomNode &node, MacroWindow *macrowindow)
   
   // Always center vertically in the addressed space
   setAlignment(Qt::AlignVCenter);
+}
+
+void Label::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
 
+void Label::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+void Label::setVisibility(bool visible)
+{
+  setVisible(visible);
 }
diff --git a/client/widgets/label.h b/client/widgets/label.h
index cbf3e06..6c02d1b 100644
--- a/client/widgets/label.h
+++ b/client/widgets/label.h
@@ -37,6 +37,13 @@ class Label : public QLabel, public Widget
 Q_OBJECT
 public:
   Label(QDomNode &node, MacroWindow *macrowindow);
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+  void setVisibility(bool visible);
 };
 
 #endif/*__PRACRO_LABEL_H__*/
diff --git a/client/widgets/lineedit.cc b/client/widgets/lineedit.cc
index 29ea06c..b699553 100644
--- a/client/widgets/lineedit.cc
+++ b/client/widgets/lineedit.cc
@@ -47,6 +47,7 @@ LineEdit::LineEdit(QDomNode &node, MacroWindow *macrowindow)
   }
   
   connect(this, SIGNAL(textChanged(QString)), this, SLOT(changed()));
+  connect(this, SIGNAL(textEdited(QString)), this, SLOT(user_changed()));
 }
 
 void LineEdit::changed()
@@ -69,15 +70,24 @@ void LineEdit::changed()
   setPalette(palette);
 }
 
+void LineEdit::user_changed()
+{
+  emit wasChanged(); 
+}
+
 QString LineEdit::getValue()
 {
   return text();
 }
 
-void LineEdit::setValue(QString value)
+void LineEdit::setValue(QString value, QString source)
 {
+  if(isUserSource(source)) emit wasChanged();
+
   if(text() == value) setText(value + " "); // Hack to make sure the textChanged signal is emitted.
   setText(value);
+
+  setInitialValue(value);
 }
 
 void LineEdit::enable()
@@ -89,3 +99,26 @@ void LineEdit::disable()
 {
   setEnabled(false);
 }
+
+void LineEdit::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void LineEdit::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool LineEdit::setKeyboardFocus()
+{
+  setFocus();
+  return true;
+}
+
+void LineEdit::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/lineedit.h b/client/widgets/lineedit.h
index 3f0bb17..0e060d6 100644
--- a/client/widgets/lineedit.h
+++ b/client/widgets/lineedit.h
@@ -39,12 +39,25 @@ public:
   LineEdit(QDomNode &node, MacroWindow *macrowindow);
 
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
   void enable();
   void disable();
 
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  bool setKeyboardFocus();
+  void setVisibility(bool visible);
+
 public slots:
   void changed();
+  void user_changed();
+
+signals:
+  void wasChanged();
 };
 
 #endif/*__PRACRO_LINEEDIT_H__*/
diff --git a/client/widgets/listbox.cc b/client/widgets/listbox.cc
index 88a5cd2..303d288 100644
--- a/client/widgets/listbox.cc
+++ b/client/widgets/listbox.cc
@@ -71,6 +71,8 @@ static QListWidgetItem *createItem(QDomElement &elem)
 ListBox::ListBox(QDomNode &node, MacroWindow *macrowindow)
   : QListWidget(), Widget(node, macrowindow)
 {
+  valueIsChangingByComputer = false;
+
   setCommonAttributes(this, node);
 
   QDomNodeList children = node.childNodes();
@@ -80,6 +82,8 @@ ListBox::ListBox(QDomNode &node, MacroWindow *macrowindow)
     QDomElement list_elem = child.toElement();
     addItem(createItem(list_elem));
   }
+
+  connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(changed()));
 }
 
 bool ListBox::isValid()
@@ -94,8 +98,11 @@ QString ListBox::getValue()
   return value;
 }
 
-void ListBox::setValue(QString value)
+void ListBox::setValue(QString value, QString source)
 {
+  if(isUserSource(source)) emit wasChanged();
+
+  valueIsChangingByComputer = true;
   int sel = -1; // -1 is default for none selected
 
   for(int i = 0; i < count(); i++) {
@@ -104,4 +111,35 @@ void ListBox::setValue(QString value)
   }
 
   setCurrentRow(sel);
+
+  setInitialValue(value);
+  valueIsChangingByComputer = false;
+}
+
+void ListBox::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void ListBox::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+void ListBox::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
+
+void ListBox::changed()
+{
+  if(!valueIsChangingByComputer) emit wasChanged();
+}
+
+bool ListBox::setKeyboardFocus()
+{
+  setFocus();
+  return true;
 }
diff --git a/client/widgets/listbox.h b/client/widgets/listbox.h
index 627d31e..1485225 100644
--- a/client/widgets/listbox.h
+++ b/client/widgets/listbox.h
@@ -33,13 +33,32 @@
 
 class ListBox : public QListWidget, public Widget
 {
+Q_OBJECT
 public:
   ListBox(QDomNode &node, MacroWindow *macrowindow);
 
-public slots:
   bool isValid();
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  void setVisibility(bool visible);
+
+  bool setKeyboardFocus();
+
+public slots:
+  void changed();
+
+signals:
+  void wasChanged();
+
+private:
+  bool valueIsChangingByComputer;
 };
 
 #endif/*__PRACRO_LISTBOX_H__*/
diff --git a/client/widgets/metawidget.cc b/client/widgets/metawidget.cc
index 5f53153..91c62ec 100644
--- a/client/widgets/metawidget.cc
+++ b/client/widgets/metawidget.cc
@@ -29,6 +29,8 @@
 #include <QHBoxLayout>
 #include <QVBoxLayout>
 
+#include <QMessageBox>
+
 #include "widgetbuilder.h"
 #include "formatparser.h"
 
@@ -38,37 +40,21 @@ MetaWidget::MetaWidget(QDomNode &node, MacroWindow *macrowindow)
   : QFrame(), Widget(node, macrowindow)
 {
   setCommonAttributes(this, node);
-
-  QDomElement elem = node.toElement();
-  if(elem.hasAttribute("layout")) {
-    if(elem.attribute("layout") == "hbox") {
-      QHBoxLayout *layout = new QHBoxLayout();
-      setLayout(layout);
-    } else if (elem.attribute("layout") == "vbox") {
-      QVBoxLayout *layout = new QVBoxLayout();
-      setLayout(layout);      
-    }
-  } else {
-    QHBoxLayout *layout = new QHBoxLayout();
-    setLayout(layout);
-  }
-  
-  layout()->setContentsMargins(0,0,0,0);
+  setCommonLayout(this, node);
   
+  QDomElement elem = node.toElement();
+  storechildren = elem.attribute("storechildren", "false") == "true";
+
+  // Create children
   QDomNodeList children = node.childNodes();
-  
   for (int i=0; i<children.count();i++) {
     QDomNode child = children.at(i);
-    widgets += widgetBuilder(child, this, macrowindow);
-  }
-  macrowindow->addAuxWidgets(widgets);
-  
-  /* // This is done later
-  if(elem.hasAttribute("value")) {
-    setValue(elem.attribute("value"));
+    widgets += widgetBuilder(child, this, macrowindow, false);
   }
-  */
+  if(storechildren) macrowindow->addWidgets(widgets);
+  else macrowindow->addAuxWidgets(widgets);
 
+  // Setup format string
   if(elem.hasAttribute("format")) {
     format = elem.attribute("format");
   } else {
@@ -81,19 +67,18 @@ MetaWidget::MetaWidget(QDomNode &node, MacroWindow *macrowindow)
     }
   }
 
-  if(elem.hasAttribute("width")) {
-    setMinimumWidth(elem.attribute("width").toInt());
+  // Connect all children wasChanged signal, to catch changes.
+  QVector< Widget* >::iterator i = widgets.begin();
+  while (i != widgets.end()) {
+    Widget* w = *i;
+    w->connectFrom(SIGNAL(wasChanged()), this, SLOT(changed()));
+    i++;
   }
-
-  if(elem.hasAttribute("height")) {
-    setMinimumHeight(elem.attribute("height").toInt());
-  }
-
-  layout()->setContentsMargins(0,0,0,0);
 }
 
 void MetaWidget::changed()
 {
+  emit wasChanged();
 }
 
 QString MetaWidget::getValue()
@@ -101,7 +86,7 @@ QString MetaWidget::getValue()
   return format_parser(format, widgets);
 }
 
-void MetaWidget::setValue(QString /*values*/)
+void MetaWidget::setValue(QString, QString)
 {
   // Nothing reasonable we can do here.
 }
@@ -115,3 +100,65 @@ void MetaWidget::disable()
 {
   setEnabled(false);
 }
+
+bool MetaWidget::isValid()
+{
+  // If children are stored they will validate themselves.
+  if(!storechildren) {
+    QVector< Widget* >::iterator i = widgets.begin();
+    while (i != widgets.end()) {
+      Widget* w = *i;
+      if(w->isValid() == false) {
+        QMessageBox::critical(NULL, "Fejl",
+                              "Et af inputfelterne (" + w->getName()
+                              + ") er ikke udfyldt korrekt, pr�v igen.\n"
+                              , QMessageBox::Ok);
+        return false;
+      }
+      i++;
+    }
+  }
+
+  return regexpValidator() && luaValidator();
+}
+
+void MetaWidget::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void MetaWidget::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool MetaWidget::setKeyboardFocus()
+{
+  QVector< Widget* >::iterator i = widgets.begin();
+  while (i != widgets.end()) {
+    Widget* w = *i;
+    if(w->setKeyboardFocus()) return true;
+    i++;
+  }
+  return false;
+}
+
+void MetaWidget::reset()
+{
+  // If children are stored they will be reset individually.
+  if(!storechildren) {
+    QVector< Widget* >::iterator i = widgets.begin();
+    while (i != widgets.end()) {
+      Widget* w = *i;
+      w->reset();
+      i++;
+    }
+  }
+}
+
+void MetaWidget::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/metawidget.h b/client/widgets/metawidget.h
index 5d0bbc9..e920236 100644
--- a/client/widgets/metawidget.h
+++ b/client/widgets/metawidget.h
@@ -40,18 +40,36 @@ Q_OBJECT
 public:
   MetaWidget(QDomNode &node, MacroWindow *macrowindow);
 
-public slots:
-  void changed();
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
 
   void enable();
   void disable();
 
+  bool isValid();
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  bool setKeyboardFocus();
+  void setVisibility(bool visible);
+
+  void reset();
+
+public slots:
+  void changed();
+
+signals:
+  void wasChanged();
+
 private:
   QListWidget *list;
   QVector< Widget* > widgets;
   QString format;
+  bool storechildren;
 };
 
 #endif/*__PRACRO_METAWIDGET_H__*/
diff --git a/client/widgets/multilist.cc b/client/widgets/multilist.cc
index bff3ad7..5f28368 100644
--- a/client/widgets/multilist.cc
+++ b/client/widgets/multilist.cc
@@ -43,9 +43,29 @@ MultiList::MultiList(QDomNode &node, MacroWindow *macrowindow)
   QGridLayout *layout = new QGridLayout();
   setLayout(layout);
 
+  list = new QListWidget(this);
+  layout->addWidget(list, 0, 0, 1, 2, Qt::AlignTop);
+  list->installEventFilter(this);
+
+  QPushButton *add = new QPushButton(this);
+  connect(add, SIGNAL(clicked()), this, SLOT(add()));
+  add->setText("Tilf�j nedenst�ende til listen");
+  add->setIcon(QIcon(QPixmap(":icons/add.png")));
+  layout->addWidget(add, 1, 0, 1, 1, Qt::AlignTop);
+
+  QPushButton *rem = new QPushButton(this);
+  connect(rem, SIGNAL(clicked()), this, SLOT(remove()));
+  rem->setText("Fjern det markerede element fra listen");
+  rem->setIcon(QIcon(QPixmap(":icons/del.png")));
+  layout->addWidget(rem, 1, 1, 1, 1, Qt::AlignTop);
+
+  QLabel *arrows = new QLabel();
+  arrows->setPixmap(QPixmap(":icons/arrows.png"));
+  layout->addWidget(arrows, 2, 0, 1, 2, Qt::AlignHCenter);
+  
   QWidget *inputbox = new QWidget(this);
   inputbox->setContentsMargins(0,0,0,0);
-  layout->addWidget(inputbox, 0, 0, 1, 2, Qt::AlignTop);
+  layout->addWidget(inputbox, 3, 0, 1, 2, Qt::AlignTop);
 
   QDomElement elem = node.toElement();
   if(elem.hasAttribute("layout")) {
@@ -68,7 +88,7 @@ MultiList::MultiList(QDomNode &node, MacroWindow *macrowindow)
   QVector< Widget* > widgets;
   for (int i=0; i<children.count();i++) {
     QDomNode child = children.at(i);
-    widgets += widgetBuilder(child, inputbox, macrowindow);
+    widgets += widgetBuilder(child, inputbox, macrowindow, false);
   }
   macrowindow->addAuxWidgets(widgets);
 
@@ -88,35 +108,6 @@ MultiList::MultiList(QDomNode &node, MacroWindow *macrowindow)
     printf("ERROR: Missing 'innerwidget' attribute on multilist!\n");
   }
 
-  QLabel *arrows = new QLabel();
-  arrows->setPixmap(QPixmap(":icons/arrows.png"));
-  layout->addWidget(arrows, 1, 0, 1, 2, Qt::AlignHCenter);
-  
-  QPushButton *add = new QPushButton(this);
-  connect(add, SIGNAL(clicked()), this, SLOT(add()));
-  add->setText("Tilf�j ovenst�ende til listen");
-  add->setIcon(QIcon(QPixmap(":icons/add.png")));
-
-  //  layout->addWidget(add, 0, 1, Qt::AlignTop);
-  layout->addWidget(add, 2, 0, 1, 1, Qt::AlignTop);
-
-  QPushButton *rem = new QPushButton(this);
-  connect(rem, SIGNAL(clicked()), this, SLOT(remove()));
-  rem->setText("Fjern det markerede element fra listen");
-  rem->setIcon(QIcon(QPixmap(":icons/del.png")));
-  //  layout->addWidget(rem, 1, 1, Qt::AlignTop);
-  layout->addWidget(rem, 2, 1, 1, 1, Qt::AlignTop);
-
-  list = new QListWidget(this);
-  //  layout->addWidget(list, 1, 0, Qt::AlignTop);
-  layout->addWidget(list, 3, 0, 1, 2, Qt::AlignTop);
-
-  /* // This is done later
-  if(elem.hasAttribute("value")) {
-    setValue(elem.attribute("value"));
-  }
-  */
-
   layout->setContentsMargins(0,0,0,0);
 }
 
@@ -140,8 +131,10 @@ QString MultiList::getValue()
   return values;
 }
 
-void MultiList::setValue(QString values)
+void MultiList::setValue(QString values, QString source)
 {
+  if(isUserSource(source)) emit wasChanged();
+
   QString value;
 
   list->clear();
@@ -152,26 +145,28 @@ void MultiList::setValue(QString values)
     if(value != "") list->addItem(value);
     idx++;
   } while(value != "");
+
+  setInitialValue(values);
 }
 
 void MultiList::remove()
 {
-  list->takeItem(list->currentRow());
+  QListWidgetItem *item = list->currentItem();
+
+  if(item && item->isSelected()) {
+    delete item;
+    emit wasChanged();
+  }
 }
 
 void MultiList::add()
 {
-  /*
-  QVector< Widget * >::iterator i = widgets.begin();
-  while(i != widgets.end()) {
-    if(!(*i)->isValid()) return;
-    i++;
-  }
-  list->addItem(format_parser(format, widgets));
-  */
+  if(innerwidget && innerwidget->isValid()) {
+    list->addItem(innerwidget->getValue());
+    emit wasChanged();
 
-  if(innerwidget && innerwidget->isValid()) list->addItem(innerwidget->getValue());
-  
+    innerwidget->reset();
+  }
 }
 
 void MultiList::enable()
@@ -183,3 +178,35 @@ void MultiList::disable()
 {
   setEnabled(false);
 }
+
+void MultiList::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void MultiList::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool MultiList::setKeyboardFocus()
+{
+  if(innerwidget) return innerwidget->setKeyboardFocus();
+  return false;
+}
+
+bool MultiList::eventFilter(QObject *obj, QEvent *event)
+{
+  if(event->type() == QEvent::KeyPress) {
+    QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+    if(keyEvent->key() == Qt::Key_Delete) remove();
+  }
+  return QObject::eventFilter(obj, event);
+}
+
+void MultiList::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
diff --git a/client/widgets/multilist.h b/client/widgets/multilist.h
index 0a9e798..3bd7b9c 100644
--- a/client/widgets/multilist.h
+++ b/client/widgets/multilist.h
@@ -40,16 +40,31 @@ Q_OBJECT
 public:
   MultiList(QDomNode &node, MacroWindow *macrowindow);
 
-public slots:
-  void changed();
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
+
+  void enable();
+  void disable();
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
 
+  bool setKeyboardFocus();
+  void setVisibility(bool visible);
+
+public slots:
+  void changed();
   void remove();
   void add();
 
-  void enable();
-  void disable();
+signals:
+  void wasChanged();
+
+protected:
+  bool eventFilter(QObject *obj, QEvent *event);
 
 private:
   QListWidget *list;
diff --git a/client/widgets/radiobutton.h b/client/widgets/radiobutton.h
index d0cf149..6dbae7e 100644
--- a/client/widgets/radiobutton.h
+++ b/client/widgets/radiobutton.h
@@ -35,6 +35,7 @@
 
 class RadioButton : public QRadioButton
 {
+Q_OBJECT
 public:
   RadioButton(QDomNode &node);
 
diff --git a/client/widgets/radiobuttons.cc b/client/widgets/radiobuttons.cc
index 1987459..ae40677 100644
--- a/client/widgets/radiobuttons.cc
+++ b/client/widgets/radiobuttons.cc
@@ -47,6 +47,9 @@ RadioButtons::RadioButtons(QDomNode &node, MacroWindow *macrowindow)
 
     // Create radiobutton from child, insert in this
     layout()->addWidget(radiobutton);
+
+    connect(radiobutton, SIGNAL(clicked()), this, SLOT(childChanged()));
+
     radiobutton_list.push_back(radiobutton);
   }
   
@@ -79,8 +82,10 @@ QString RadioButtons::getValue()
   return value;
 }
 
-void RadioButtons::setValue(QString value)
+void RadioButtons::setValue(QString value, QString source)
 {
+  if(isUserSource(source)) emit wasChanged();
+
   QVector< RadioButton* >::iterator i;
   for (i = radiobutton_list.begin(); i != radiobutton_list.end(); ++i) {
     RadioButton *widget = *i;
@@ -90,4 +95,48 @@ void RadioButtons::setValue(QString value)
       widget->setChecked(false);
     }
   }
+
+  setInitialValue(value);
+}
+
+void RadioButtons::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void RadioButtons::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool RadioButtons::setKeyboardFocus()
+{
+  QVector< RadioButton* >::iterator i = radiobutton_list.begin();
+  while(i != radiobutton_list.end()) {
+    RadioButton *rb = *i;
+    if(rb->isChecked()) {
+      rb->setFocus();
+      return true;
+    }
+    i++;
+  }
+
+  if(radiobutton_list.size()) {
+    radiobutton_list.at(0)->setFocus();
+    return true;
+  }
+
+  return false;
+}
+
+void RadioButtons::setVisibility(bool visible)
+{
+  setVisible(visible);
+}
+
+void RadioButtons::childChanged()
+{
+  emit wasChanged();
 }
diff --git a/client/widgets/radiobuttons.h b/client/widgets/radiobuttons.h
index f1c89c4..cfdf1ef 100644
--- a/client/widgets/radiobuttons.h
+++ b/client/widgets/radiobuttons.h
@@ -36,13 +36,28 @@
 
 class RadioButtons : public QFrame, public Widget
 {
+Q_OBJECT
 public:
   RadioButtons(QDomNode &node, MacroWindow *macrowindow);
 
-public slots:
   bool isValid();
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  bool setKeyboardFocus();
+  void setVisibility(bool visible);
+
+public slots:
+  void childChanged();
+
+signals:
+  void wasChanged();
 
 private:
   QVector < RadioButton* > radiobutton_list;
diff --git a/client/widgets/textedit.cc b/client/widgets/textedit.cc
index cdbd8ad..ec7e3fc 100644
--- a/client/widgets/textedit.cc
+++ b/client/widgets/textedit.cc
@@ -49,6 +49,7 @@ TextEdit::TextEdit(QDomNode &node, MacroWindow *macrowindow)
   }
 
   connect(this, SIGNAL(textChanged()), this, SLOT(changed()));
+  installEventFilter(this); // Detect keyboard input.
 }
 
 void TextEdit::changed()
@@ -76,7 +77,41 @@ QString TextEdit::getValue()
   return QTextEdit::toPlainText();
 }
 
-void TextEdit::setValue(QString value)
+void TextEdit::setValue(QString value, QString source)
 {
+  if(isUserSource(source)) emit wasChanged();
   setText(value);
+  setInitialValue(value);
+}
+
+bool TextEdit::eventFilter(QObject *, QEvent *event)
+{
+  if (event->type() == QEvent::KeyPress) {
+    QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+    if(keyEvent->text() != "") emit wasChanged();
+  }
+  return false;
+}
+
+void TextEdit::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void TextEdit::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
+
+bool TextEdit::setKeyboardFocus()
+{
+  setFocus();
+  return true;
+}
+
+void TextEdit::setVisibility(bool visible)
+{
+  setVisible(visible);
 }
diff --git a/client/widgets/textedit.h b/client/widgets/textedit.h
index c734f38..722abcd 100644
--- a/client/widgets/textedit.h
+++ b/client/widgets/textedit.h
@@ -31,6 +31,7 @@
 #include <QTextEdit>
 #include <QWidget>
 #include <QDomNode>
+#include <QKeyEvent>
 
 class TextEdit : public QTextEdit, public Widget
 {
@@ -39,10 +40,27 @@ public:
   TextEdit(QDomNode &node, MacroWindow *macrowindow);
 
   QString getValue();
-  void setValue(QString value);
+  void setValue(QString value, QString source = "");
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
+
+  bool setKeyboardFocus();
+
+  void setVisibility(bool visible);
+
+signals:
+  void wasChanged();
 
 public slots:
   void changed();
+  //  void user_changed();
+
+protected:
+  bool eventFilter(QObject *obj, QEvent *event);
 };
 
 #endif/*__PRACRO_TEXTEDIT_H__*/
diff --git a/client/widgets/widget.cc b/client/widgets/widget.cc
index de49968..0857d67 100644
--- a/client/widgets/widget.cc
+++ b/client/widgets/widget.cc
@@ -35,8 +35,10 @@ Widget::Widget(QDomNode &node, MacroWindow *macrowindow)
   if(elem.hasAttribute("name")) {
     widget_name = elem.attribute("name");
   } else {
-    printf("XML ERROR!! Unnamed widget of type: %s\n", 
-           elem.tagName().toStdString().c_str());
+    if(elem.tagName() != "frame" && elem.tagName() != "label"
+       && elem.tagName() != "button" && elem.tagName() != "window")
+      printf("XML ERROR!! Unnamed widget of type: %s\n", 
+             elem.tagName().toStdString().c_str());
   }
 
   if(elem.hasAttribute("script")) {
@@ -52,6 +54,9 @@ Widget::Widget(QDomNode &node, MacroWindow *macrowindow)
   } else {
     hasregexpvalidator = false;
   }
+
+  has_initial_value = false;
+  initial_value = "";
 }
 
 QString Widget::getName()
@@ -59,12 +64,7 @@ QString Widget::getName()
   return widget_name;
 }
 
-QString Widget::getValue()
-{
-  return "";
-}
-
-void Widget::setValue(QString)
+void Widget::setValue(QString, QString)
 {
 }
 
@@ -85,10 +85,15 @@ bool Widget::luaValidator()
   return macrowindow->lua->run(luaprogram, getName(), getValue());
 }
 
-void Widget::disable()
+void Widget::setInitialValue(QString value)
 {
+  if(!has_initial_value) {
+    initial_value = value;
+    has_initial_value = true;
+  }
 }
 
-void Widget::enable()
+void Widget::reset()
 {
+  setValue(initial_value);
 }
diff --git a/client/widgets/widget.h b/client/widgets/widget.h
index 9b6a996..2977e3a 100644
--- a/client/widgets/widget.h
+++ b/client/widgets/widget.h
@@ -38,13 +38,35 @@ class Widget {
 public:
   Widget(QDomNode &node, MacroWindow *macrowindow);
   virtual ~Widget(){}
-  virtual QString getValue();
-  virtual void setValue(QString value);
+
+  virtual QString getValue() { return ""; }
+  virtual void setValue(QString value, QString source = "");
+
   virtual bool isValid();
-  virtual void disable();
-  virtual void enable();
+
+  virtual void disable() {}
+  virtual void enable() {}
+  virtual bool isDisabled() { return false; }
+
+  virtual void setVisibility(bool) {}
+
   QString getName();
 
+  /**
+   * Connect some signal from this object to some slot in some other object.
+   */
+  virtual void connectFrom(const char *, const QObject *, const char *) {}
+
+  /**
+   * Connect some signal from some other object to some slot in this object.
+   */
+  virtual void connectTo(const QObject *, const char *, const char *) {}
+
+  virtual bool setKeyboardFocus() { return false; }
+
+  void setInitialValue(QString value);
+  virtual void reset();
+
 protected:
   QString widget_name;
 
@@ -60,6 +82,9 @@ private:
   QString luaprogram;
   LUA *lua;
   MacroWindow *macrowindow;
+  
+  QString initial_value;
+  bool has_initial_value;
 };
 
 #endif/*__PRACRO_WIDGET_H__*/
diff --git a/client/widgets/window.cc b/client/widgets/window.cc
index fa44875..e685e79 100644
--- a/client/widgets/window.cc
+++ b/client/widgets/window.cc
@@ -51,3 +51,15 @@ Window::Window(QDomNode &node, MacroWindow *macrowindow)
     setWindowTitle(elem.attribute("caption"));
   }
 }
+
+void Window::connectFrom(const char *signal,
+                           const QObject *receiver, const char *method)
+{
+  connect(this, signal, receiver, method);
+}
+
+void Window::connectTo(const QObject *sender, const char *signal,
+                         const char *method)
+{
+  connect(sender, signal, this, method);
+}
diff --git a/client/widgets/window.h b/client/widgets/window.h
index af1e3d7..6e3d5be 100644
--- a/client/widgets/window.h
+++ b/client/widgets/window.h
@@ -35,6 +35,12 @@ class Window : public QWidget, public Widget
 {
 public:
   Window(QDomNode &node, MacroWindow *macrowindow);
+
+  void connectFrom(const char *signal,
+                   const QObject *receiver, const char *method);
+
+  void connectTo(const QObject *sender, const char *signal,
+                 const char *method);
 };
 
 #endif/*__PRACRO_WINDOW_H__*/
-- 
cgit v1.2.3