start drafting ODF editor
authorKonrad Rosenbaum <konrad@silmor.de>
Mon, 30 Jan 2012 19:44:17 +0000 (20:44 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Mon, 30 Jan 2012 19:44:17 +0000 (20:44 +0100)
src/templates/odfedit.cpp [new file with mode: 0644]
src/templates/odfedit.h [new file with mode: 0644]
src/templates/odtrender.cpp
src/templates/odtrender.h
src/templates/templatedlg.cpp
src/templates/templates.pri

diff --git a/src/templates/odfedit.cpp b/src/templates/odfedit.cpp
new file mode 100644 (file)
index 0000000..faed04e
--- /dev/null
@@ -0,0 +1,883 @@
+//
+// C++ Implementation: ticket template editor
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2010-2011
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#include "odfedit.h"
+#include "centbox.h"
+#include "misc.h"
+#include "ticketrender.h"
+
+#include "MOTicket"
+#include "MOVoucher"
+
+#include <DPtr>
+#include <QAction>
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QCoreApplication>
+#include <QDateTimeEdit>
+#include <QDebug>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QDoubleSpinBox>
+#include <QFileDialog>
+#include <QFontDatabase>
+#include <QHeaderView>
+#include <QInputDialog>
+#include <QItemDelegate>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMenu>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QPointF>
+#include <QPushButton>
+#include <QScrollArea>
+#include <QSettings>
+#include <QSignalMapper>
+#include <QSizeF>
+#include <QSplitter>
+#include <QStandardItemModel>
+#include <QStatusBar>
+#include <QTableView>
+#include <QTemporaryFile>
+#include <QUnZip>
+#include <QZip>
+
+#define HRECT 1
+#define HFONT 2
+#define HFILE 4
+#define HALIGN 8
+#define HSMOOTH 0x10
+#define HCONTENT 0x20
+
+class MOEExampleDelegate:public QItemDelegate
+{
+       public:
+               MOEExampleDelegate(QObject *parent = 0):QItemDelegate(parent){}
+
+               QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
+                       const QModelIndex &index) const
+               {
+                       if(index.column()!=1)return 0;
+                       QString tp=index.model()->data(index,Qt::UserRole).toString();
+                       if(tp=="string")return new QLineEdit(parent);
+                       if(tp=="money")return new MCentSpinBox(parent);
+                       if(tp=="date")return new QDateTimeEdit(parent);
+                       return 0;
+               }
+
+               void setEditorData(QWidget *editor, const QModelIndex &index) const
+               {
+                       const QAbstractItemModel*model=index.model();
+                       QString tp=index.model()->data(index,Qt::UserRole).toString();
+                       if(tp=="string")
+                               ((QLineEdit*)editor)->setText(model->data(index).toString());
+                       if(tp=="money")
+                               ((MCentSpinBox*)editor)->setValue(str2cent(model->data(index).toString()));
+                       if(tp=="date")
+                               ((QDateTimeEdit*)editor)->setDateTime(QDateTime::fromString(model->data(index).toString(),Qt::ISODate));
+                       
+               }
+               void setModelData(QWidget *editor, QAbstractItemModel *model,
+                       const QModelIndex &index) const
+               {
+                       QString tp=index.model()->data(index,Qt::UserRole).toString();
+                       if(tp=="string")
+                               model->setData(index, ((QLineEdit*)editor)->text());
+                       if(tp=="money")
+                               model->setData(index, cent2str(((MCentSpinBox*)editor)->value()));
+                       if(tp=="date")
+                               model->setData(index, ((QDateTimeEdit*)editor)->dateTime().toString(Qt::ISODate));
+               }
+
+               void updateEditorGeometry(QWidget *editor,
+                       const QStyleOptionViewItem &option, const QModelIndex &) const
+               {editor->setGeometry(option.rect);}
+};
+
+class DPTR_CLASS_NAME(MOdfEditor)
+{
+       public:
+               //template file
+               QString mFileName;
+               //file contents
+               QMap<QString,QByteArray>mFiles;
+               QString mUnit;
+               QSizeF mSize;
+               enum Type{
+                       Unknown=0,
+                       LoadFont=0x100|HFILE,
+                       Picture=0x200|HFILE|HRECT|HSMOOTH,
+                       Text=0x300|HFONT|HRECT|HALIGN|HCONTENT,
+                       Barcode=0x400|HRECT,
+               };
+               struct Line_s{
+                       Line_s(){type=Unknown;smooth=false;fontsize=0;align=Qt::AlignCenter;}
+                       Line_s(int t){type=(Type)t;smooth=false;fontsize=0;align=Qt::AlignCenter;}
+                       Type type;
+                       QSizeF size;
+                       QPointF offset;
+                       QString file;
+                       bool smooth;
+                       QString font;
+                       double fontsize;
+                       Qt::Alignment align;
+                       QString content;
+               };
+               QList<Line_s>mLines;
+               //widgets
+               QDoubleSpinBox*labelwidth,*labelheight;
+               QComboBox*unitbox;
+               QTableView*itemtable,*filetable,*expltable;
+               QStandardItemModel*itemmodel,*filemodel,*explmodel;
+               QLabel*ticketpicture;
+               QSlider*zoomslide;
+};
+
+DEFINE_DPTR(MOdfEditor);
+
+MOdfEditor::MOdfEditor(QWidget* parent, Qt::WindowFlags f): QMainWindow(parent, f)
+{
+       setWindowTitle(tr("Label Editor"));
+       //menu
+       QMenuBar*mb=menuBar();
+       QMenu*m=mb->addMenu(tr("&File"));
+       m->addAction(tr("&Open File..."), this, SLOT(openFile()), tr("Ctrl+O","open file shortcut"));
+       m->addAction(tr("&Save"), this, SLOT(saveFile()), tr("Ctrl+S","save file shortcut"));
+       m->addAction(tr("Save &as..."),this,SLOT(saveFileAs()));
+       m->addSeparator();
+       m->addAction(tr("&Close"),this,SLOT(close()));
+       m=mb->addMenu(tr("&Edit"));
+       QMenu *aim=m->addMenu(tr("&Add Item"));
+       m->addAction(tr("&Remove Item"),this,SLOT(delItem()));
+       m->addSeparator();
+       m->addAction(tr("Add &File"),this,SLOT(addFile()));
+       m->addAction(tr("Remove F&ile"),this,SLOT(delFile()));
+       QSignalMapper*map=new QSignalMapper(this);
+       map->setMapping(aim->addAction(tr("Add &Text Item"),map,SLOT(map())),(int)Private::Text);
+       map->setMapping(aim->addAction(tr("Add &Picture Item"),map,SLOT(map())),(int)Private::Picture);
+       map->setMapping(aim->addAction(tr("Add &Barcode Item"),map,SLOT(map())),(int)Private::Barcode);
+       map->setMapping(aim->addAction(tr("Add &Load Font Item"),map,SLOT(map())),(int)Private::LoadFont);
+       connect(map,SIGNAL(mapped(int)),this,SLOT(addItem(int)));
+       
+       //central
+       QSplitter*central=new QSplitter(Qt::Vertical);
+       setCentralWidget(central);
+       QWidget*w=new QWidget;
+       central->addWidget(w);
+       QVBoxLayout*vl,*vl2;
+       QHBoxLayout*hl,*hl2;
+       QPushButton*p;
+       w->setLayout(vl=new QVBoxLayout);
+       vl->addLayout(hl=new QHBoxLayout,0);
+       //basic data line
+       hl->addWidget(new QLabel(tr("Label Size:")));
+       hl->addWidget(d->labelwidth=new QDoubleSpinBox);
+       hl->addWidget(new QLabel("x"));
+       hl->addWidget(d->labelheight=new QDoubleSpinBox);
+       hl->addWidget(d->unitbox=new QComboBox);
+       d->unitbox->addItem(tr("Millimeter"),"mm");
+       d->unitbox->addItem(tr("Inch"),"in");
+       hl->addStretch(1);
+       //item table
+       vl->addWidget(w=new QWidget);
+       w->setLayout(vl2=new QVBoxLayout);
+       vl2->addWidget(d->itemtable=new QTableView,1);
+       d->itemtable->setModel(d->itemmodel=new QStandardItemModel(this));
+       d->itemtable->horizontalHeader()->show();
+       d->itemtable->verticalHeader()->hide();
+       d->itemtable->setItemDelegate(new MOELabelDelegate(this));
+       vl2->addLayout(hl2=new QHBoxLayout,0);
+       hl2->addStretch(1);
+       hl2->addWidget(p=new QPushButton(QIcon(":/arrowup.png"),tr("Move up")),0);
+       connect(p,SIGNAL(clicked()),this,SLOT(upItem()));
+       hl2->addWidget(p=new QPushButton(QIcon(":/arrowdown.png"),tr("Move down")),0);
+       connect(p,SIGNAL(clicked()),this,SLOT(downItem()));
+       hl2->addSpacing(10);
+       hl2->addWidget(p=new QPushButton(tr("Add Item")),0);
+       p->setMenu(aim);
+       hl2->addWidget(p=new QPushButton(tr("Remove Item")),0);
+       connect(p,SIGNAL(clicked()),this,SLOT(delItem()));
+       //bottom
+       QSplitter*split;
+       central->addWidget(split=new QSplitter);
+       //file table
+       split->addWidget(w=new QWidget);
+       w->setLayout(vl2=new QVBoxLayout);
+       vl2->addWidget(d->filetable=new QTableView,1);
+       d->filetable->setModel(d->filemodel=new QStandardItemModel(this));
+       d->filetable->horizontalHeader()->show();
+       d->filetable->verticalHeader()->hide();
+       d->filetable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+       vl2->addLayout(hl2=new QHBoxLayout,0);
+       hl2->addStretch(1);
+       hl2->addWidget(p=new QPushButton(tr("Add File")),0);
+       connect(p,SIGNAL(clicked()),this,SLOT(addFile()));
+       hl2->addWidget(p=new QPushButton(tr("Remove File")),0);
+       connect(p,SIGNAL(clicked()),this,SLOT(delFile()));
+       //label display
+       QTabWidget*tab;
+       split->addWidget(tab=new QTabWidget);
+       tab->addTab(w=new QWidget,tr("As Label"));
+       w->setLayout(vl=new QVBoxLayout);
+       vl->addLayout(hl=new QHBoxLayout,0);
+       hl->addWidget(new QLabel(tr("Zoom:")),0);
+       hl->addWidget(d->zoomslide=new QSlider,1);
+       d->zoomslide->setOrientation(Qt::Horizontal);
+       d->zoomslide->setRange(-10,10);
+       d->zoomslide->setTickPosition(QSlider::TicksBothSides);
+       connect(d->zoomslide,SIGNAL(valueChanged(int)),this,SLOT(rerender()));
+       hl->addWidget(p=new QPushButton(tr("Refresh")),0);
+       connect(p,SIGNAL(clicked()),this,SLOT(rerender()));
+       QScrollArea*sa;
+       vl->addWidget(sa=new QScrollArea,1);
+       sa->setWidget(d->ticketpicture=new QLabel);
+       sa->setWidgetResizable(true);
+       d->ticketpicture->setScaledContents(false);
+       d->ticketpicture->setAlignment(Qt::AlignCenter);
+       tab->addTab(d->expltable=new QTableView,tr("Example Data"));
+       d->expltable->setModel(d->explmodel=new QStandardItemModel(this));
+       d->expltable->setItemDelegate(new MOEExampleDelegate(this));
+       d->expltable->horizontalHeader()->show();
+       d->expltable->verticalHeader()->hide();
+       
+       //statusbar
+       statusBar()->setSizeGripEnabled(true);
+       setAttribute(Qt::WA_DeleteOnClose);
+       loadExampleData();
+}
+
+const QString example(
+"BARCODE\tABCDEFGHIJK\tstring\n"
+"PRICE\t12.34\tmoney\n"
+"ROOM\tThe invisible Cabinet\tstring\n"
+"TITLE\tSome title, this is.\tstring\n"
+"DATETIME\t2023-01-23T19:54\tdate\n"
+"ARTIST\tHenry the Drycleaner\tstring\n"
+"PRICECATEGORY\tExpensive\tstring\n"
+"PRICECATEGORYABBR\texp\tstring\n"
+"VALUE\t12.00\tmoney");
+
+void MOdfEditor::loadExampleData()
+{
+       d->explmodel->clear();
+       d->explmodel->setHorizontalHeaderLabels(QStringList()<<tr("Variable")<<tr("Content"));
+       QStringList ex1=example.split("\n");
+       d->explmodel->insertRows(0,ex1.size());
+       QSettings set;
+       set.beginGroup("labeleditor/examples");
+       for(int i=0;i<ex1.size();i++){
+               QStringList ex2=ex1[i].split("\t");
+               d->explmodel->setData(d->explmodel->index(i,0),ex2[0]);
+               d->explmodel->setData(d->explmodel->index(i,1), set.value(ex2[0],ex2[1]));
+               d->explmodel->setData(d->explmodel->index(i,1), ex2[2], Qt::UserRole);
+       }
+}
+
+
+void MOdfEditor::loadFile(QString fn)
+{
+       //try to open the file
+       if(fn=="")return;
+       QFile fd(fn);
+       if(!fd.open(QIODevice::ReadOnly)){
+               QMessageBox::warning(this, tr("Error"), tr("Unable to open file '%1' for reading.").arg(fn));
+               return;
+       }
+       QUnZip unz;
+       if(!unz.open(&fd)){
+               QMessageBox::warning(this, tr("Error"), tr("Unable to interpret file '%1'.").arg(fn));
+               return;
+       }
+       //clear cache
+       d->mFiles.clear();
+       d->mLines.clear();
+       //read data
+       unz.gotoFirstFile();
+       do{
+               QString ifn;
+               unz.getCurrentFileInfo(ifn,0);
+               QBuffer buf;buf.open(QIODevice::ReadWrite);
+               unz.getCurrentFile(buf);
+               if(ifn=="template.xml")
+                       parseTemplate(buf.data());
+               else
+                       d->mFiles.insert(ifn,buf.data());
+       }while(unz.gotoNextFile());
+       //update display
+       updateDisplay();
+}
+
+static inline QSizeF str2size(QString s)
+{
+       QStringList sl=s.trimmed().split(" ");
+       if(sl.size()!=2)return QSizeF();
+       return QSizeF(sl[0].toDouble(),sl[1].toDouble());
+}
+
+static inline QString size2str(QSizeF s)
+{
+       return QString("%1 %2").arg(s.width()).arg(s.height());
+}
+
+static inline QPointF str2point(QString s)
+{
+       QStringList sl=s.trimmed().split(" ");
+       if(sl.size()!=2)return QPointF();
+       return QPointF(sl[0].toDouble(),sl[1].toDouble());
+}
+
+static inline QString point2str(QPointF p)
+{
+       return QString("%1 %2").arg(p.x()).arg(p.y());
+}
+
+static inline QString align2str(Qt::Alignment a)
+{
+       switch(a){
+               case Qt::AlignTop:
+                       return QCoreApplication::translate("MOdfEditor","top");
+               case Qt::AlignBottom:
+                       return QCoreApplication::translate("MOdfEditor","bottom");
+               case Qt::AlignHCenter:
+               case Qt::AlignVCenter:
+                       return QCoreApplication::translate("MOdfEditor","center");
+               case Qt::AlignLeft:
+                       return QCoreApplication::translate("MOdfEditor","left");
+               case Qt::AlignRight:
+                       return QCoreApplication::translate("MOdfEditor","right");
+               default:
+                       return QCoreApplication::translate("MOdfEditor","align (%1)").arg((int)a);
+       }
+}
+
+static inline QString align2xml(Qt::Alignment a)
+{
+       switch(a){
+               case Qt::AlignTop:
+                       return "top";
+               case Qt::AlignBottom:
+                       return "bottom";
+               case Qt::AlignHCenter:
+               case Qt::AlignVCenter:
+                       return "center";
+               case Qt::AlignLeft:
+                       return "left";
+               case Qt::AlignRight:
+                       return "right";
+               default:
+                       return "";
+       }
+}
+
+void MOdfEditor::parseTemplate(QByteArray bytes)
+{
+       QDomDocument doc;
+       if(!doc.setContent(bytes)){
+               QMessageBox::warning(this,tr("Warning"),tr("Unable to interpret template data."));
+               return;
+       }
+       QDomElement root=doc.documentElement();
+       d->mSize=str2size(root.attribute("size"));
+       d->mUnit=root.attribute("unit","mm");
+       QDomNodeList nl=root.childNodes();
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               QString tn=el.tagName();
+               Private::Line_s line;
+               if(tn=="LoadFont")line.type=Private::LoadFont;else
+               if(tn=="Picture")line.type=Private::Picture;else
+               if(tn=="Text")line.type=Private::Text;else
+               if(tn=="Barcode")line.type=Private::Barcode;
+               else line.type=Private::Unknown;
+               line.file=el.attribute("file");
+               line.font=el.attribute("font");
+               line.fontsize=el.attribute("fontsize","10").toDouble();
+               line.offset=str2point(el.attribute("offset"));
+               line.size=str2size(el.attribute("size"));
+               line.smooth=el.attribute("smoot")=="1";
+               line.align=0;
+               tn=el.attribute("align");
+               if(tn=="left")line.align=Qt::AlignLeft;else
+               if(tn=="right")line.align=Qt::AlignRight;else
+               if(tn=="center")line.align=Qt::AlignHCenter;
+               tn=el.attribute("valign");
+               if(tn=="top")line.align|=Qt::AlignTop;else
+               if(tn=="bottom")line.align|=Qt::AlignBottom;else
+               if(tn=="center")line.align|=Qt::AlignVCenter;
+               line.content=el.text();
+               d->mLines.append(line);
+       }
+}
+
+void MOdfEditor::openFile()
+{
+       QString fn=QFileDialog::getOpenFileName(this,tr("Open Ticket Template"));
+       if(fn!=""){
+               d->mFileName=fn;
+               loadFile(fn);
+       }
+}
+
+void MOdfEditor::saveFile()
+{
+       if(d->mFileName=="" || d->mFileName.left(2)==":/")
+               saveFileAs();
+       else
+               saveFile(d->mFileName);
+}
+void MOdfEditor::saveFile ( QString fn)
+{
+       QFile fd(fn);
+       if(!fd.open(QIODevice::ReadWrite|QIODevice::Truncate)){
+               QMessageBox::warning(this,tr("Warning"),tr("Unable to write to file %1").arg(fn));
+               return;
+       }
+       saveTemplate(fd);
+       fd.close();
+}
+void MOdfEditor::saveFileAs()
+{
+       QString fn=QFileDialog::getSaveFileName(this,tr("Save Ticket Template"),d->mFileName);
+       if(fn!=""){
+               d->mFileName=fn;
+               saveFile(fn);
+       }
+}
+
+void MOdfEditor::updateDisplay()
+{
+       if(d->mFileName=="")
+               setWindowTitle(tr("Label Template Editor"));
+       else
+               setWindowTitle(tr("Label Template Editor [%1]").arg(d->mFileName));
+       //basics
+       d->labelwidth->setValue(d->mSize.width());
+       d->labelheight->setValue(d->mSize.height());
+       for(int i=0;i<d->unitbox->count();i++)
+               if(d->unitbox->itemData(i).toString()==d->mUnit)
+                       d->unitbox->setCurrentIndex(i);
+       //files
+       d->filemodel->clear();
+       if(d->filemodel->columnCount()<2)
+               d->filemodel->insertColumns(0,2);
+       d->filemodel->insertRows(0,d->mFiles.size());
+       d->filemodel->setHorizontalHeaderLabels(QStringList()<<tr("File Name")<<tr("Size"));
+       QStringList fnames=d->mFiles.keys();
+       for(int i=0;i<d->mFiles.size();i++){
+               d->filemodel->setData(d->filemodel->index(i,0),fnames[i]);
+               d->filemodel->setData(d->filemodel->index(i,1),d->mFiles[fnames[i]].size());
+       }
+       d->filetable->resizeColumnsToContents();
+       //items
+       d->itemmodel->clear();
+       d->itemmodel->insertRows(0,d->mLines.size());
+       d->itemmodel->setHorizontalHeaderLabels(QStringList()
+               <<tr("Type")
+               <<tr("Offset")
+               <<tr("Size")
+               <<tr("File/Font")
+               <<tr("Font Size")
+               <<tr("Scaling")
+               <<tr("Horiz. Alignment")
+               <<tr("Vert. Alignment")
+               <<tr("Text Data")
+       );
+       for(int i=0;i<d->mLines.size();i++){
+               QString tp;
+               Private::Type tpe=d->mLines[i].type;
+               switch(tpe){
+                       case Private::LoadFont:tp=tr("Load Font File");break;
+                       case Private::Picture:tp=tr("Show Picture");break;
+                       case Private::Text:tp=tr("Show Text Line");break;
+                       case Private::Barcode:tp=tr("Show Barcode");break;
+                       default:tp=tr("Unknown");break;
+               }
+               d->itemmodel->setData(d->itemmodel->index(i,0),tp);
+               d->itemmodel->setData(d->itemmodel->index(i,0),(int)tpe,Qt::UserRole);
+               if(tpe&HRECT){
+                       d->itemmodel->setData(d->itemmodel->index(i,2), size2str(d->mLines[i].size));
+                       d->itemmodel->setData(d->itemmodel->index(i,1), point2str(d->mLines[i].offset));
+               }
+               if(tpe&HFONT){
+                       d->itemmodel->setData(d->itemmodel->index(i,3),d->mLines[i].font);
+                       d->itemmodel->setData(d->itemmodel->index(i,4),d->mLines[i].fontsize);
+               }
+               if(tpe&HFILE)
+                       d->itemmodel->setData(d->itemmodel->index(i,3),d->mLines[i].file);
+               if(tpe&HSMOOTH)
+                       d->itemmodel->setData(d->itemmodel->index(i,5), d->mLines[i].smooth?tr("smooth"):tr("edged"));
+               if(tpe&HALIGN){
+                       d->itemmodel->setData(d->itemmodel->index(i,6), align2str(d->mLines[i].align&Qt::AlignHorizontal_Mask));
+                       d->itemmodel->setData(d->itemmodel->index(i,7), align2str(d->mLines[i].align&Qt::AlignVertical_Mask));
+               }
+               if(tpe&HCONTENT)
+                       d->itemmodel->setData(d->itemmodel->index(i,8), d->mLines[i].content);
+       }
+       d->itemtable->resizeColumnsToContents();
+       //do rendering
+       rerender();
+}
+
+QWidget* MOELabelDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& , const QModelIndex& index) const
+{
+       //get type
+       int tp=index.model()->data(index.model()->index(index.row(),0),Qt::UserRole).toInt();
+       switch(index.column()){
+               case 0:
+                       return 0;
+               case 1:case 2:
+                       if(tp&HRECT){
+                               QLineEdit*ed=new QLineEdit(parent);
+                               ed->setValidator(new QRegExpValidator(QRegExp("^(-?)[0-9\\.]+ (-?)[0-9\\.]+"),ed));
+                               return ed;
+                       }else return 0;
+               case 3:
+                       if(tp&HFONT || tp&HFILE)
+                               return new QComboBox(parent);
+                       else return 0;
+               case 4:
+                       if(tp&HFONT)
+                               return new QDoubleSpinBox(parent);
+                       else return 0;
+               case 5:
+                       if(tp&HSMOOTH)
+                               return new QComboBox(parent);
+                       else return 0;
+               case 6:case 7:
+                       if(tp&HALIGN)
+                               return new QComboBox(parent);
+                       else return 0;
+               case 8:
+                       if(tp&HCONTENT)
+                               return new QLineEdit(parent);
+                       else return 0;
+               default: return 0;
+       }
+}
+
+void MOELabelDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
+{
+       const QAbstractItemModel*model=index.model();
+       int tp=model->data(model->index(index.row(),0),Qt::UserRole).toInt();
+       int line=index.row();
+       QComboBox*cb;
+       switch(index.column()){
+               case 1:case 2:case 8:
+                       ((QLineEdit*)editor)->setText(model->data(index).toString());
+                       break;
+               case 3:{
+                       cb=(QComboBox*)editor;
+                       QStringList k=mParent->d->mFiles.keys();
+                       qSort(k);
+                       QString c=model->data(index).toString();
+                       if(tp&HFONT){
+                               for(int i=0;i<k.size();i++)
+                                       cb->addItem("@"+k[i]);
+                               k=QFontDatabase().families();
+                               qSort(k);
+                               cb->addItems(k);
+                               for(int i=0;i<cb->count();i++)
+                                       if(cb->itemText(i)==c)
+                                               cb->setCurrentIndex(i);
+                       }else{
+                               cb->addItems(k);
+                               for(int i=0;i<k.size();i++)
+                                       if(c==k[i])
+                                               cb->setCurrentIndex(i);
+                       }
+                       break;
+               }
+               case 4:
+                       ((QDoubleSpinBox*)editor)->setValue(model->data(index).toDouble());
+                       break;
+               case 5:
+                       cb=(QComboBox*)editor;
+                       cb->addItem(tr("edged"));
+                       cb->addItem(tr("smooth"));
+                       cb->setCurrentIndex(mParent->d->mLines[line].smooth?1:0);
+                       break;
+               case 6:
+                       cb=(QComboBox*)editor;
+                       cb->addItem(align2str(Qt::AlignLeft),Qt::AlignLeft);
+                       cb->addItem(align2str(Qt::AlignHCenter),Qt::AlignHCenter);
+                       cb->addItem(align2str(Qt::AlignRight),Qt::AlignRight);
+                       switch(mParent->d->mLines[line].align&Qt::AlignHorizontal_Mask){
+                               case Qt::AlignHCenter:cb->setCurrentIndex(1);break;
+                               case Qt::AlignRight:cb->setCurrentIndex(2);break;
+                               default:break;
+                       }
+                       break;
+               case 7:
+                       cb=(QComboBox*)editor;
+                       cb->addItem(align2str(Qt::AlignTop),Qt::AlignTop);
+                       cb->addItem(align2str(Qt::AlignVCenter),Qt::AlignVCenter);
+                       cb->addItem(align2str(Qt::AlignBottom),Qt::AlignBottom);
+                       switch(mParent->d->mLines[line].align&Qt::AlignVertical_Mask){
+                               case Qt::AlignVCenter:cb->setCurrentIndex(1);break;
+                               case Qt::AlignBottom:cb->setCurrentIndex(2);break;
+                               default:break;
+                       }
+                       break;
+               default:break;
+       }
+}
+
+void MOELabelDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
+{
+       int tp=index.model()->data(index.model()->index(index.row(),0),Qt::UserRole).toInt();
+       QString s;
+       int line=index.row();
+       QComboBox*cb;
+       switch(index.column()){
+               case 1:
+                       s=((QLineEdit*)editor)->text();
+                       mParent->d->mLines[line].offset=str2point(s);
+                       model->setData(index,point2str(mParent->d->mLines[line].offset));
+                       break;
+               case 2:
+                       s=((QLineEdit*)editor)->text();
+                       mParent->d->mLines[line].size=str2size(s);
+                       model->setData(index,size2str(mParent->d->mLines[line].size));
+                       break;
+               case 3:
+                       cb=(QComboBox*)editor;
+                       if(tp&HFONT){
+                               mParent->d->mLines[line].font=cb->currentText();
+                       }else{
+                               mParent->d->mLines[line].file=cb->currentText();
+                       }
+                       model->setData(index,cb->currentText());
+                       break;
+               case 4:
+                       mParent->d->mLines[line].fontsize=((QDoubleSpinBox*)editor)->value();
+                       model->setData(index,mParent->d->mLines[line].fontsize);
+                       break;
+               case 5:
+                       mParent->d->mLines[line].smooth=((QComboBox*)editor)->currentIndex()!=0;
+                       model->setData(index,((QComboBox*)editor)->currentText());
+                       break;
+               case 6:
+                       cb=((QComboBox*)editor);
+                       mParent->d->mLines[line].align&=Qt::AlignVertical_Mask;
+                       mParent->d->mLines[line].align|=Qt::AlignmentFlag(cb->itemData(cb->currentIndex()).toInt());
+                       model->setData(index,cb->currentText());
+                       break;
+               case 7:
+                       cb=((QComboBox*)editor);
+                       mParent->d->mLines[line].align&=Qt::AlignHorizontal_Mask;
+                       mParent->d->mLines[line].align|=Qt::AlignmentFlag(cb->itemData(cb->currentIndex()).toInt());
+                       model->setData(index,cb->currentText());
+                       break;
+               case 8:
+                       s=((QLineEdit*)editor)->text();
+                       mParent->d->mLines[line].content=s;
+                       model->setData(index,s);
+                       break;
+               default:break;
+       }
+       mParent->rerender();
+}
+
+
+class MTELabel:public MLabel
+{
+       QMap<QString,QString>exdata;
+       public:
+               MTELabel(QMap<QString,QString>dt)
+               {
+                       exdata=dt;
+                       exdata.insert("TICKETID",dt["BARCODE"]);
+                       exdata.insert("VOUCHERID",dt["BARCODE"]);
+               }
+               QString getVariable(QString s)const
+               {
+                       QString ret=exdata.value(s);
+                       if(s=="PRICE"||s=="VALUE")
+                               ret=cent2str(str2cent(ret));
+                       else if(s=="DATETIME")
+                               ret=unix2dateTime(QDateTime::fromString(ret,Qt::ISODate).toTime_t());
+                       return ret;
+               }
+};
+
+void MOdfEditor::rerender()
+{
+       QMap<QString,QString>exdata;
+       for(int i=0;i<d->explmodel->rowCount();i++){
+               exdata.insert(
+                       d->explmodel->data(d->explmodel->index(i,0)).toString(),
+                       d->explmodel->data(d->explmodel->index(i,1)).toString()
+               );
+       }
+       MTELabel label(exdata);
+       //calculate scaling factor
+       int zfi=d->zoomslide->value();
+       qreal zff=1.0;
+       if(zfi!=0){
+               for(int i=0;i<zfi;i++)zff*=1.2;
+               for(int i=0;i>zfi;i--)zff/=1.2;
+       }
+       //render ticket
+       QTemporaryFile tfile(QDir::tempPath()+"/templateXXXXXX.xtt");
+       tfile.open();
+       saveTemplate(tfile);
+       MLabelRenderer rend(tfile.fileName());
+       QSize size(rend.labelSize(*d->ticketpicture).toSize());
+       QImage tick=QPixmap(size*zff).toImage();
+       tick.setDotsPerMeterX(tick.dotsPerMeterX()*zff);
+       tick.setDotsPerMeterY(tick.dotsPerMeterY()*zff);
+       tick.fill(0xffffffff);
+       if(!rend.render(label,tick))
+               qDebug("unable to render");
+       //scale and display
+       d->ticketpicture->setPixmap(QPixmap::fromImage(tick));
+}
+
+void MOdfEditor::saveTemplate(QFile& fd)
+{
+       QZip zip;
+       zip.open(&fd);
+       {QBuffer buf;buf.open(QIODevice::ReadWrite);
+       saveXmlPart(buf);buf.seek(0);
+       zip.storeFile("template.xml",buf);}
+       QStringList k=d->mFiles.keys();
+       for(int i=0;i<k.size();i++){
+               QBuffer buf;
+               buf.open(QIODevice::ReadWrite);
+               buf.write(d->mFiles[k[i]]);
+               buf.seek(0);
+               zip.storeFile(k[i],buf);
+       }
+       zip.close();
+       fd.flush();
+}
+
+void MOdfEditor::saveXmlPart(QIODevice& fd)
+{
+       QDomDocument doc;
+       QDomElement root=doc.createElement("LabelTemplate");
+       root.setAttribute("size",size2str(d->mSize));
+       root.setAttribute("unit",d->mUnit);
+       for(int i=0;i<d->mLines.size();i++){
+               Private::Line_s ln=d->mLines[i];
+               QDomElement el;
+               switch(ln.type){
+                       case Private::LoadFont:el=doc.createElement("LoadFont");break;
+                       case Private::Picture:el=doc.createElement("Picture");break;
+                       case Private::Text:el=doc.createElement("Text");break;
+                       case Private::Barcode:el=doc.createElement("Barcode");break;
+                       default:continue;
+               }
+               if(ln.type&HRECT){
+                       el.setAttribute("size",size2str(ln.size));
+                       el.setAttribute("offset",point2str(ln.offset));
+               }
+               if(ln.type&HFILE)
+                       el.setAttribute("file",ln.file);
+               if(ln.type&HFONT){
+                       el.setAttribute("font",ln.font);
+                       el.setAttribute("fontsize",ln.fontsize);
+               }
+               if(ln.type&HSMOOTH)
+                       el.setAttribute("smooth",ln.smooth?"1":"0");
+               if(ln.type&HALIGN){
+                       el.setAttribute("align",align2xml(ln.align&Qt::AlignHorizontal_Mask));
+                       el.setAttribute("valign",align2xml(ln.align&Qt::AlignVertical_Mask));
+               }
+               if(ln.type&HCONTENT)
+                       el.appendChild(doc.createTextNode(ln.content));
+               root.appendChild(el);
+       }
+       doc.appendChild(root);
+       //save
+       fd.write(doc.toByteArray());
+}
+
+
+void MOdfEditor::addFile()
+{
+       QString fn=QFileDialog::getOpenFileName(this,tr("Add File to Label"));
+       if(fn=="")return;
+       //try to read it
+       QFile fd(fn);
+       if(!fd.open(QIODevice::ReadOnly)){
+               QMessageBox::warning(this,tr("Warning"),tr("Unable to read file %1").arg(fn));
+               return;
+       }
+       QByteArray fdata=fd.readAll();
+       fd.close();
+       //ask for internal name
+       fn=QFileInfo(fn).fileName();
+       fn=QInputDialog::getText(this,tr("File Name"),tr("Please enter the internal file name:"),QLineEdit::Normal,fn);
+       if(fn=="")return;
+       fn=QFileInfo(fn).fileName();
+       if(fn=="")return;
+       //append
+       d->mFiles.insert(fn,fdata);
+       updateDisplay();
+}
+
+void MOdfEditor::delFile()
+{
+       //get file name
+       QModelIndex idx=d->filetable->currentIndex();
+       if(!idx.isValid())return;
+       QString fn=d->filemodel->data(d->filemodel->index(idx.row(),0)).toString();
+       if(fn=="")return;
+       //ask
+       if(QMessageBox::question(this,tr("Really delete?"),tr("Really remove file '%1' from the label?").arg(fn),QMessageBox::No|QMessageBox::Yes)!=QMessageBox::Yes)
+               return;
+       //delete
+       d->mFiles.remove(fn);
+       updateDisplay();
+}
+
+void MOdfEditor::addItem(int type)
+{
+       d->mLines.append(Private::Line_s(type));
+       updateDisplay();
+}
+
+void MOdfEditor::delItem()
+{
+       QModelIndex idx=d->itemtable->currentIndex();
+       if(!idx.isValid())return;
+       d->mLines.removeAt(idx.row());
+       updateDisplay();
+}
+
+void MOdfEditor::downItem()
+{
+       QModelIndex idx=d->itemtable->currentIndex();
+       if(!idx.isValid())return;
+       int r=idx.row();
+       if(r>=(d->mLines.size()-1))return;
+       Private::Line_s tmp=d->mLines[r];
+       d->mLines[r]=d->mLines[r+1];
+       d->mLines[r+1]=tmp;
+       updateDisplay();
+       d->itemtable->setCurrentIndex(d->itemmodel->index(r+1,idx.column()));
+}
+void MOdfEditor::upItem()
+{
+       QModelIndex idx=d->itemtable->currentIndex();
+       if(!idx.isValid())return;
+       int r=idx.row();
+       if(r<=0)return;
+       Private::Line_s tmp=d->mLines[r];
+       d->mLines[r]=d->mLines[r-1];
+       d->mLines[r-1]=tmp;
+       updateDisplay();
+       d->itemtable->setCurrentIndex(d->itemmodel->index(r-1,idx.column()));
+}
diff --git a/src/templates/odfedit.h b/src/templates/odfedit.h
new file mode 100644 (file)
index 0000000..f7c9d1c
--- /dev/null
@@ -0,0 +1,89 @@
+//
+// C++ Interface: ODF template editor
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2010-2012
+//
+// Copyright: See README/COPYING.GPL files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_ODFEDIT_H
+#define MAGICSMOKE_ODFEDIT_H
+
+#include <QMainWindow>
+#include <QMap>
+#include <QItemDelegate>
+
+#include <DPtrBase>
+
+class QFile;
+class QIODevice;
+/**An editor for ticket templates.*/
+class MOdfEditor:public QMainWindow
+{
+       Q_OBJECT
+       DECLARE_DPTR(d);
+       friend class MOELabelDelegate;
+       public:
+               ///instantiates the editor
+               MOdfEditor(QWidget* parent = 0, Qt::WindowFlags f = 0);
+               
+       public slots:
+               ///loads a template file, this is a helper for openFile and download
+               void loadFile(QString);
+               ///shows a file open dialog and then opens the selected file
+               void openFile();
+               ///saves the file - if it is local, otherwise calls saveFileAs
+               void saveFile();
+               ///asks for a new file name
+               void saveFileAs();
+               ///helper for saveFile and upload
+               void saveFile(QString);
+       private slots:
+               ///renders the label
+               void rerender();
+               ///parses the XML part of the template file and fills internal ressources
+               void parseTemplate(QByteArray);
+               ///pushes all data from internal ressources to the display
+               void updateDisplay();
+               ///add a specific kind of item line
+               void addItem(int);
+               ///delete currently selected item line
+               void delItem();
+               ///move selected item up
+               void upItem();
+               ///move selected item down
+               void downItem();
+               ///add a file (image/font) to the template
+               void addFile();
+               ///delete the selected file from the template
+               void delFile();
+               ///used by constructor to load example data for the example label
+               void loadExampleData();
+               ///saves all data to the file (must be open ReadWrite)
+               void saveTemplate(QFile&);
+               ///helper: saves the XML part of the template (device must be writable)
+               void saveXmlPart(QIODevice&);
+};
+
+/// \internal helper for the ticket editor
+class MOELabelDelegate:public QItemDelegate
+{
+       Q_OBJECT
+       MOdfEditor*mParent;
+       public:
+               MOELabelDelegate(MOdfEditor *parent):QItemDelegate(parent) 
+               {mParent=parent;}
+               QWidget *createEditor(QWidget *, const QStyleOptionViewItem &,
+                       const QModelIndex &) const;
+               void setEditorData(QWidget *, const QModelIndex &) const;
+               void setModelData(QWidget *, QAbstractItemModel *,
+                       const QModelIndex &) const ;
+               void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, 
+                         const QModelIndex &) const {editor->setGeometry(option.rect);}
+};
+
+#endif
index 86363fb..655df4c 100644 (file)
 #include "odtrender.h"
 #include "office.h"
 
+#include <QDebug>
 #include <QDir>
+#include <QDomDocument>
+#include <QDomElement>
 #include <QFile>
 #include <QProcess>
 #include <QSettings>
@@ -59,6 +62,7 @@ class MOdtRendererPrivate
                QFile tfile;
                QString newline;
                bool inif,iftrue;
+               QDomDocument cdoc;
                
                struct LocalVar{
                        MOdtRenderer::VarType type;
@@ -73,6 +77,7 @@ MOdtRenderer::MOdtRenderer(MTemplate file)
        d->extension=file.targetExtension();
 }
 
+const QString OdfTemplateNS("http://smoke.silmor.de/odftemplate/namespace");
 MOdtRendererPrivate::MOdtRendererPrivate(QString file,MOdtRenderer*p)
        :tfile(file)
 {
@@ -81,7 +86,44 @@ MOdtRendererPrivate::MOdtRendererPrivate(QString file,MOdtRenderer*p)
        inif=iftrue=true;
        //open ZIP
        if(tfile.open(QIODevice::ReadOnly))temp.open(&tfile);
+       else {
+               qDebug()<<"Error opening ODF template file"<<file;
+               return;
+       }
        //TODO: make sure this is a valid ZIP file, preferably with some ODT content
+       //check what version it is, convert if necessary
+       if(!temp.locateFile("content.xml")){
+               qDebug()<<file<<"is not an ODF template. aborting.";
+               return;
+       }
+       //get content
+       QBuffer buffer;
+       buffer.open(QBuffer::ReadWrite);
+       temp.getCurrentFile(buffer);
+       QString err;int errln,errcl;
+       bool dov1=false;
+       if(!cdoc.setContent(&buffer,true,&err,&errln,&errcl)){
+               qDebug()<<"Hmm, not XML, trying version 1 converter...";
+               qDebug()<<" Info: line ="<<errln<<"column ="<<errcl<<"error ="<<err;
+               dov1=true;
+       }else{
+               //check for template element
+               QDomElement de=cdoc.documentElement();
+               if(de.isNull()||de.tagName()!="Template"||de.namespaceURI()!=OdfTemplateNS){
+                       qDebug()<<"Template is not v2, trying to convert...";
+                       dov1=true;
+                       cdoc.clear();
+               }
+       }
+       //conversion process
+       if(!dov1)return;
+       buffer.setData(MOdtRenderer::convertV1toV2(buffer.data()));
+       //try again
+       if(!cdoc.setContent(&buffer,true,&err,&errln,&errcl)){
+               qDebug()<<"Hmm, definitely not XML - even after conversion, aborting...";
+               qDebug()<<" Info: line ="<<errln<<"column ="<<errcl<<"error ="<<err;
+               return;
+       }
 }
 
 MOdtRenderer::~MOdtRenderer()
@@ -386,6 +428,18 @@ QString MOdtRendererPrivate::renderLine(QString line,QString loop,int lpos)
        return ret + "\n";
 }
 
+QByteArray MOdtRenderer::convertV1toV2(const QByteArray& old)
+{
+       QByteArray nba;
+       QList<QByteArray>olst=old.split('\n');
+       foreach(const QByteArray&line,olst){
+               nba+=line;
+               nba+='\n';
+       }
+       qDebug()<<"conversion test"<<nba;
+       return nba;
+}
+
 static inline QString formatVar(QVariant r,MOdtRenderer::VarType tp,bool loc,int offset)
 {
        switch(tp){
index 8a87d2b..dc17e42 100644 (file)
@@ -21,6 +21,8 @@
 class MOdtRendererPrivate;
 class QFile;
 
+extern const QString OdfTemplateNS;
+
 /**abstract base class for all ODT rendering classes*/
 class MOdtRenderer
 {
@@ -55,6 +57,9 @@ class MOdtRenderer
                        DateTimeVar
                };
                
+               ///helper routine: converts a V1 template to V2
+               static QByteArray convertV1toV2(const QByteArray&);
+               
        protected:
                friend class MOdtRendererPrivate;
                /**implement this to return the value of a variable during rendering; should return empty string if the variable does not exist*/
index b236540..4a7f72b 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "templatedlg.h"
 #include "ticketedit.h"
+#include "odfedit.h"
 #include "flagedit.h"
 
 #include <QBoxLayout>
@@ -269,7 +270,10 @@ void MTemplateEditor::editItem()
                tp=MTemplate::legalTypes(base);
        }
        if(tp&MTemplate::OdfTemplateMask){
-               QMessageBox::warning(this,"Sorry","Sorry, no ODF editor yet.");
+               MOdfEditor *ed=new MOdfEditor(this);
+               if(fn!="")
+                       ed->loadFile(tmp.cacheFileName());
+               ed->show();
        }else
        if(tp==MTemplate::LabelTemplate){
                MTicketEditor *ed=new MTicketEditor(this);
index 5ef18a9..9790050 100644 (file)
@@ -1,19 +1,21 @@
 HEADERS += \
-       templates/odtrender.h \
-       templates/ticketrender.h \
-       templates/office.h \
-       templates/labeldlg.h \
-       templates/templates.h \
-       templates/templatedlg.h \
-       templates/ticketedit.h
+       $$PWD/odtrender.h \
+       $$PWD/ticketrender.h \
+       $$PWD/office.h \
+       $$PWD/labeldlg.h \
+       $$PWD/templates.h \
+       $$PWD/templatedlg.h \
+       $$PWD/ticketedit.h \
+       $$PWD/odfedit.h
 
 SOURCES += \
-       templates/odtrender.cpp \
-       templates/ticketrender.cpp \
-       templates/office.cpp \
-       templates/labeldlg.cpp \
-       templates/templates.cpp \
-       templates/templatedlg.cpp \
-       templates/ticketedit.cpp
+       $$PWD/odtrender.cpp \
+       $$PWD/ticketrender.cpp \
+       $$PWD/office.cpp \
+       $$PWD/labeldlg.cpp \
+       $$PWD/templates.cpp \
+       $$PWD/templatedlg.cpp \
+       $$PWD/ticketedit.cpp \
+       $$PWD/odfedit.cpp
 
-INCLUDEPATH += ./templates
\ No newline at end of file
+INCLUDEPATH += $$PWD
\ No newline at end of file