00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "latexeditor.h"
00022 #include "latexhelpers.h"
00023 #include "pageitem_latexframe.h"
00024 #include "prefsmanager.h"
00025
00026 #include <QDebug>
00027 #include <QFile>
00028 #include <QFrame>
00029 #include <QFontComboBox>
00030 #include <QLineEdit>
00031 #include <QPushButton>
00032 #include <QListWidget>
00033 #include <QMessageBox>
00034 #include <math.h>
00035
00036 LatexEditor::LatexEditor(PageItem_LatexFrame *frame):QDialog(), frame(frame)
00037 {
00038 setupUi(this);
00039
00040
00041 programComboBox->clear();
00042 QStringList configs = PrefsManager::instance()->latexConfigs();
00043 foreach (QString config, configs) {
00044 QString name = LatexConfigCache::instance()->parser(config)->description();
00045 programComboBox->addItem(name, config);
00046 QString iconname = LatexConfigCache::instance()->parser(config)->icon();
00047 if (!iconname.isEmpty()) {
00048 programComboBox->setItemIcon(programComboBox->count()-1,
00049 icon(config, iconname));
00050 }
00051 }
00052
00053 highlighter = new LatexHighlighter(sourceTextEdit->document());
00054 connect(buttonBox, SIGNAL(accepted()),
00055 this, SLOT(okClicked()));
00056 connect(buttonBox, SIGNAL(rejected()),
00057 this, SLOT(cancelClicked()));
00058 connect(updatePushButton, SIGNAL(clicked(bool)),
00059 this, SLOT(updateClicked(bool)));
00060 connect(revertPushButton, SIGNAL(clicked(bool)),
00061 this, SLOT(revertClicked(bool)));
00062 connect(killPushButton, SIGNAL(clicked(bool)),
00063 frame, SLOT(killProcess()));
00064 connect(frame, SIGNAL(formulaAutoUpdate(QString, QString)),
00065 this, SLOT(formulaChanged(QString, QString)));
00066 connect(frame, SIGNAL(latexFinished()),
00067 this, SLOT(latexFinished()));
00068 connect(frame, SIGNAL(stateChanged(QProcess::ProcessState)),
00069 this, SLOT(stateChanged(QProcess::ProcessState)));
00070 connect(frame, SIGNAL(applicationChanged()),
00071 this, SLOT(updateConfigFile()));
00072 updateConfigFile();
00073 }
00074
00075 LatexEditor::~LatexEditor()
00076 {
00077 buttonBox->disconnect();
00078 exitEditor();
00079 delete highlighter;
00080 }
00081
00082 void LatexEditor::startEditor()
00083 {
00084 revert();
00085 initialize();
00086 show();
00087 }
00088
00089 void LatexEditor::exitEditor()
00090 {
00091 hide();
00092 }
00093
00094 void LatexEditor::revert()
00095 {
00096 sourceTextEdit->setPlainText(frame->formula());
00097 }
00098
00099 void LatexEditor::initialize()
00100 {
00101 preambleCheckBox->setChecked(frame->usePreamble());
00102 dpiSpinBox->setValue(frame->dpi());
00103 stateChanged(frame->state());
00104 messagesTextEdit->setPlainText(frame->output());
00105 }
00106
00107 void LatexEditor::apply(bool force)
00108 {
00109 bool changed = frame->setFormula(sourceTextEdit->toPlainText());
00110
00111 QString newConfig = programComboBox->itemData(programComboBox->currentIndex()).toString();
00112 if (newConfig != frame->configFile()) {
00113 changed = true;
00114 frame->setConfigFile(newConfig);
00115 }
00116
00117 if (frame->usePreamble() != preambleCheckBox->isChecked() ||
00118 frame->dpi() != dpiSpinBox->value()) {
00119 changed = true;
00120 frame->setUsePreamble(preambleCheckBox->isChecked());
00121 frame->setDpi(dpiSpinBox->value());
00122 }
00123 QString key;
00124 QString value;
00125
00126 QMapIterator<QString, XmlWidget *> i(widgetMap);
00127 while (i.hasNext()) {
00128 i.next();
00129 key = i.key();
00130 value = i.value()->toString();
00131 if (frame->editorProperties[key] != value) {
00132 changed = true;
00133 frame->editorProperties[key] = value;
00134 }
00135 }
00136
00137
00138 if (changed || force) {
00139 frame->rerunApplication(true);
00140 }
00141 }
00142
00143 void LatexEditor::formulaChanged(QString oldText, QString newText)
00144 {
00145 sourceTextEdit->setPlainText(newText);
00146 }
00147
00148 void LatexEditor::okClicked()
00149 {
00150 apply();
00151 exitEditor();
00152 }
00153
00154 void LatexEditor::cancelClicked()
00155 {
00156 revert();
00157 exitEditor();
00158 }
00159
00160 void LatexEditor::revertClicked(bool unused)
00161 {
00162 revert();
00163 }
00164
00165 void LatexEditor::updateClicked(bool unused)
00166 {
00167 apply(true);
00168 }
00169
00170 void LatexEditor::latexFinished()
00171 {
00172 messagesTextEdit->setPlainText(frame->output());
00173 }
00174
00175 void LatexEditor::stateChanged(QProcess::ProcessState state)
00176 {
00177 if (state == QProcess::Starting) {
00178 messagesTextEdit->setPlainText("");
00179 }
00180 QString text = tr("Status: ");
00181 if (state == QProcess::NotRunning) {
00182 if (frame->error()) {
00183 text += tr("Error");
00184 } else {
00185 text += tr("Finished");
00186 }
00187 } else {
00188 text += tr("Running");
00189 }
00190 statusLabel->setText(text);
00191 killPushButton->setEnabled(state != QProcess::NotRunning);
00192 }
00193
00194 QIcon LatexEditor::icon(QString config, QString fn)
00195 {
00196 QFileInfo fiConfig(config);
00197 QFileInfo fiIcon(fiConfig.path()+"/"+fn);
00198 if (fiIcon.exists() && fiIcon.isReadable()) {
00199 return QIcon(fiConfig.path()+"/"+fn);
00200 } else {
00201 QIcon *tmp = IconBuffer::instance()->icon(
00202 fiConfig.path() + "/" + fiConfig.completeBaseName() + ".tar", fn);
00203 if (tmp) return *tmp; else return QIcon();
00204 }
00205 }
00206
00207
00208 QString LatexEditor::iconFile(QString config)
00209 {
00210 QFileInfo fiConfig(config);
00211 return fiConfig.path() + "/" + fiConfig.completeBaseName() + ".tar";
00212 }
00213
00214 void LatexEditor::updateConfigFile()
00215 {
00216 if (currentConfigFile == frame->configFile()) return;
00217 currentConfigFile = frame->configFile();
00218 currentIconFile = iconFile(currentConfigFile);
00219 QFileInfo fi(currentConfigFile);
00220
00221 if (!fi.exists() || !fi.isReadable()) {
00222 QMessageBox::critical(0, QObject::tr("Error"), "<qt>" +
00223 QObject::tr("Configfile %1 not found or the file is not readable").
00224 arg(currentConfigFile) + "</qt>", 1, 0, 0);
00225 return;
00226 }
00227
00228 loadSettings();
00229
00230 QMapIterator<QString, XmlWidget *> i(widgetMap);
00231 while (i.hasNext()) {
00232 i.next();
00233 QString key = i.key();
00234 XmlWidget *value = i.value();
00235 if (frame->editorProperties.contains(key)) {
00236 value->fromString(frame->editorProperties[key]);
00237 }
00238 }
00239 }
00240
00241 #define xmlError() qDebug() << "XML-ERROR:" << xml->lineNumber() \
00242 << ":" << xml->columnNumber() << ":"
00243
00244 void LatexEditor::loadSettings()
00245 {
00246 while (tabWidget->count()>1) {
00247 QWidget *widget = tabWidget->widget(1);
00248 tabWidget->removeTab(1);
00249 delete widget;
00250 }
00251 widgetMap.clear();
00252
00253 qDebug() << "Loading editor GUI from " << frame->configFile();
00254 QFile f(frame->configFile());
00255 f.open(QIODevice::ReadOnly);
00256 I18nXmlStreamReader xml(&f);
00257 while (!xml.atEnd()) {
00258 xml.readNext();
00259 if (xml.isWhitespace() || xml.isComment()) continue;
00260 if (xml.isStartElement() && xml.name() == "tab") {
00261 if (xml.attributes().value("type") == "settings") {
00262 createNewSettingsTab(&xml);
00263 } else if (xml.attributes().value("type") == "items") {
00264 createNewItemsTab(&xml);
00265 } else {
00266 qDebug() << "XML-ERROR: " << xml.lineNumber() << ":"
00267 << xml.columnNumber() << ":" << "Unknow tab type"
00268 << xml.attributes().value("type").toString();
00269 }
00270 }
00271 }
00272 if (xml.hasError()) {
00273 qDebug() << "XML-ERROR: " << xml.lineNumber() << ":"
00274 << xml.columnNumber() << ":" << xml.errorString();
00275 }
00276 f.close();
00277 }
00278
00279 void LatexEditor::createNewSettingsTab(I18nXmlStreamReader *xml)
00280 {
00281 qDebug() << "Creating new tab";
00282 QStringRef tagname;
00283 QFrame *newTab = new QFrame();
00284 newTab->setFrameShape(QFrame::NoFrame);
00285 QGridLayout *layout = new QGridLayout(newTab);
00286 layout->setColumnStretch(1, 10);
00287 QString type = xml->attributes().value("type").toString();
00288
00289 QString title = "No Title";
00290
00291 while (!xml->atEnd()) {
00292 xml->readNext();
00293 if (xml->isWhitespace() || xml->isComment()) continue;
00294 tagname = xml->name();
00295 if (xml->isEndElement() && (tagname == "tab")) {
00296 break;
00297 }
00298 if (!xml->isStartElement()) {
00299 xmlError() << "Unexpected element (not a start element)!";
00300 continue;
00301 }
00302
00303 if (tagname == "comment") {
00304 QLabel *label = new QLabel(xml->readI18nText());
00305 int row = layout->rowCount();
00306 label->setWordWrap(true);
00307 layout->addWidget(label, row, 0, 1, 3);
00308 } else if (tagname == "title") {
00309 title = xml->readI18nText();
00310 } else {
00311 XmlWidget *widget = XmlWidget::fromXml(xml);
00312 if (dynamic_cast<QWidget *>(widget)) {
00313 QLabel *label = new QLabel(widget->description());
00314 label->setWordWrap(true);
00315 QString name = widget->name();
00316
00317 int row = layout->rowCount();
00318 layout->addWidget(label, row, 0);
00319 layout->addWidget(dynamic_cast<QWidget *>(widget), row, 1);
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330 if (widgetMap.contains(name)) {
00331 xmlError() << "There is already an widget with name" <<
00332 name << "!";
00333 }
00334 widgetMap[name] = widget;
00335 } else {
00336 xmlError() << "Unexpected tag" << tagname.toString() <<
00337 "in settings tab";
00338 }
00339 }
00340 }
00341 layout->setRowStretch(layout->rowCount(), 10);
00342 tabWidget->addTab(newTab, title);
00343 }
00344
00345 void LatexEditor::createNewItemsTab(I18nXmlStreamReader *xml)
00346 {
00347 QString title = "No Title!";
00348
00349
00350 QFrame *newTab = new QFrame();
00351 newTab->setFrameShape(QFrame::NoFrame);
00352 QVBoxLayout *vLayout = new QVBoxLayout(newTab);
00353
00354 QListWidget *iconList = new QListWidget();
00355 iconList->setViewMode(QListView::IconMode);
00356 iconList->setGridSize(QSize(55, 55));
00357 iconList->setMovement(QListView::Static);
00358 iconList->setFlow(QListView::LeftToRight);
00359 iconList->setWrapping(true);
00360 iconList->setResizeMode(QListView::Adjust);
00361
00362 connect(iconList, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
00363 this, SLOT (newItemSelected (QListWidgetItem *, QListWidgetItem *)));
00364 connect(iconList, SIGNAL(itemDoubleClicked (QListWidgetItem *)),
00365 this, SLOT (itemDoubleClicked (QListWidgetItem *)));
00366
00367 QHBoxLayout *hLayout = new QHBoxLayout();
00368 QLabel *statusLabel = new QLabel(tr("No item selected!"));
00369 DataPushButton *insertPushButton = new DataPushButton(
00370 tr("Insert symbol"), iconList);
00371 connect(insertPushButton, SIGNAL(clickedWithData(QObject *)),
00372 this, SLOT(insertButtonClicked(QObject *)));
00373 hLayout->addWidget(statusLabel, 100);
00374 hLayout->addWidget(insertPushButton, 0);
00375
00376 vLayout->addWidget(iconList, 100);
00377 vLayout->addLayout(hLayout, 0);
00378
00379 QStringRef tagname;
00380 while (!xml->atEnd()) {
00381 xml->readNext();
00382 if (xml->isWhitespace() || xml->isComment()) continue;
00383 tagname = xml->name();
00384 if (xml->isEndElement() && (tagname == "tab")) {
00385 break;
00386 }
00387 if (!xml->isStartElement()) {
00388 xmlError() << "Unexpected end element "
00389 <<tagname.toString()<<"in item tab";
00390 continue;
00391 }
00392 if (tagname == "title") {
00393 title = xml->readI18nText();
00394 } else if (tagname == "item") {
00395 QString value = xml->attributes().value("value").toString();
00396 QString img = xml->attributes().value("image").toString();
00397 QString text = xml->readI18nText();
00398
00399 QString status = value;
00400 if (text.isEmpty()) {
00401 text = value;
00402 } else if (text != value) {
00403 status = text + "(" + value +")";
00404 }
00405
00406 QIcon *icon = 0;
00407 if (!img.isEmpty()) {
00408 icon = IconBuffer::instance()->icon(currentIconFile, img);
00409 }
00410 QListWidgetItem *item;
00411 if (!icon) {
00412 item = new QListWidgetItem(text, iconList);
00413 } else {
00414 item = new QListWidgetItem(*icon, "", iconList);
00415 }
00416 item->setData(Qt::UserRole, value);
00417 item->setData(Qt::UserRole + 1,
00418 qVariantFromValue((void *) statusLabel));
00419 item->setToolTip(text);
00420 item->setStatusTip(status);
00421 } else {
00422 xmlError() << "Unexpected tag" << tagname.toString() <<
00423 "in item tab!";
00424 continue;
00425 }
00426 }
00427 tabWidget->addTab(newTab, title);
00428 }
00429
00430 void LatexEditor::tagButtonClicked(QString tagname)
00431 {
00432 sourceTextEdit->insertPlainText("$scribus_"+tagname+"$");
00433 }
00434
00435 void LatexEditor::newItemSelected(QListWidgetItem *newItem, QListWidgetItem *)
00436 {
00437 QLabel *label = (QLabel *)(newItem->data(Qt::UserRole + 1).value<void *>());
00438 label->setText(newItem->statusTip());
00439 }
00440
00441 void LatexEditor::itemDoubleClicked(QListWidgetItem *item)
00442 {
00443 sourceTextEdit->insertPlainText(item->data(Qt::UserRole).toString());
00444 }
00445
00446 void LatexEditor::insertButtonClicked(QObject *widget)
00447 {
00448 QListWidget *list = dynamic_cast<QListWidget*>(widget);
00449 Q_ASSERT(list);
00450 sourceTextEdit->insertPlainText(
00451 list->currentItem()->data(Qt::UserRole).toString());
00452 }
00453
00454
00455 class SCRIBUS_API XmlFontComboBox : public XmlWidget, public QFontComboBox
00456 {
00457 public:
00458 XmlFontComboBox(I18nXmlStreamReader *xml) : XmlWidget(xml), QFontComboBox()
00459 {
00460 fromString(m_defaultValue);
00461 }
00462
00463 QString toString() const {
00464 return currentFont().toString();
00465 }
00466
00467 void fromString(QString str) {
00468 QFont font;
00469 font.fromString(str);
00470 this->setCurrentFont(font);
00471 }
00472 };
00473
00474 class SCRIBUS_API XmlSpinBox : public XmlWidget, public QSpinBox
00475 {
00476 public:
00477 XmlSpinBox(I18nXmlStreamReader *xml) : XmlWidget(xml, false), QSpinBox() {
00478 setRange(
00479 xml->attributes().value("min").toString().toInt(),
00480 xml->attributes().value("max").toString().toInt()
00481 );
00482 setSingleStep(xml->attributes().value("step").toString().toInt());
00483 setSpecialValueText(xml->attributes().value("special").toString());
00484 fromString(m_defaultValue);
00485 m_description = xml->readI18nText();
00486 }
00487
00488 QString toString() const {
00489 if (value() == minimum() && !specialValueText().isEmpty()) {
00490 return specialValueText();
00491 } else {
00492 return QString::number(value());
00493 }
00494 }
00495
00496 void fromString(QString str) {
00497 if (str == specialValueText()) {
00498 setValue(minimum());
00499 } else {
00500 setValue(str.toInt());
00501 }
00502 }
00503 };
00504
00505 class SCRIBUS_API XmlDoubleSpinBox : public XmlWidget, public QDoubleSpinBox
00506 {
00507 public:
00508 XmlDoubleSpinBox(I18nXmlStreamReader *xml) :
00509 XmlWidget(xml, false), QDoubleSpinBox() {
00510 setRange(
00511 xml->attributes().value("min").toString().toDouble(),
00512 xml->attributes().value("max").toString().toDouble()
00513 );
00514 setSingleStep(
00515 xml->attributes().value("step").toString().toDouble());
00516 setSpecialValueText(xml->attributes().value("special").toString());
00517 fromString(m_defaultValue);
00518 m_description = xml->readI18nText();
00519 }
00520
00521 QString toString() const {
00522 if (value() == minimum() && !specialValueText().isEmpty()) {
00523 return specialValueText();
00524 } else {
00525 return QString::number(value());
00526 }
00527 }
00528
00529 void fromString(QString str) {
00530 if (str == specialValueText()) {
00531 setValue(minimum());
00532 } else {
00533 setValue(str.toDouble());
00534 }
00535 }
00536 };
00537
00538 class SCRIBUS_API XmlLineEdit : public XmlWidget, public QLineEdit
00539 {
00540 public:
00541 XmlLineEdit(I18nXmlStreamReader *xml) : XmlWidget(xml), QLineEdit() {
00542 fromString(m_defaultValue);
00543 }
00544
00545 QString toString() const {
00546 return text();
00547 }
00548
00549 void fromString(QString str) {
00550 setText(str);
00551 }
00552 };
00553
00554 class SCRIBUS_API XmlTextEdit : public XmlWidget, public QTextEdit
00555 {
00556 public:
00557 XmlTextEdit(I18nXmlStreamReader *xml) : XmlWidget(xml), QTextEdit() {
00558 fromString(m_defaultValue);
00559 }
00560
00561 QString toString() const {
00562 return toPlainText();
00563 }
00564
00565 void fromString(QString str) {
00566 setPlainText(str);
00567 }
00568 };
00569
00570 class SCRIBUS_API XmlColorPicker : public XmlWidget, public QLabel
00571 {
00572 public:
00573 XmlColorPicker(I18nXmlStreamReader *xml) : XmlWidget(xml),
00574 QLabel("Colorpickers are not implemented yet!")
00575 {
00576 setWordWrap(true);
00577 fromString(m_defaultValue);
00578 }
00579
00580 QString toString() const {
00581 return "Not implemented!";
00582 }
00583
00584 void fromString(QString str) {
00585 qDebug() << "Color pickers are not implemented yet!";
00586 }
00587 };
00588
00589 class SCRIBUS_API XmlComboBox : public XmlWidget, public QComboBox
00590 {
00591 public:
00592 XmlComboBox(I18nXmlStreamReader *xml) : XmlWidget(xml, false), QComboBox()
00593 {
00594 QStringRef tagname;
00595 while (!xml->atEnd()) {
00596 xml->readNext();
00597 if (xml->isWhitespace() || xml->isComment()) continue;
00598 tagname = xml->name();
00599 if (xml->isEndElement() && (tagname == "list")) {
00600 fromString(m_defaultValue);
00601 return;
00602 }
00603 if (xml->isEndElement()) {
00604 xmlError() << "Unexpected end element" << tagname.toString();
00605 continue;
00606 }
00607 if (tagname == "title") {
00608 m_description = xml->readI18nText();
00609 } else if (tagname == "option") {
00610 QString value = xml->attributes().value("value").toString();
00611 QString text = xml->readI18nText();
00612 addItem(text, value);
00613 } else {
00614 xmlError() << "Unexpected tag" << tagname.toString() <<
00615 "in list!";
00616 }
00617 }
00618 }
00619
00620 QString toString() const {
00621 return itemData(currentIndex()).toString();
00622 }
00623
00624 void fromString(QString str) {
00625 setCurrentIndex(findData(str));
00626 }
00627 };
00628
00629 XmlWidget* XmlWidget::fromXml(I18nXmlStreamReader *xml)
00630 {
00631 QStringRef tagname = xml->name();
00632 if (tagname == "font") {
00633 return new XmlFontComboBox(xml);
00634 }
00635 if (tagname == "spinbox") {
00636 if (xml->attributes().value("type") == "double")
00637 return new XmlDoubleSpinBox(xml);
00638 else
00639 return new XmlSpinBox(xml);
00640 }
00641 if (tagname == "color") {
00642 return new XmlColorPicker(xml);
00643 }
00644 if (tagname == "text") {
00645 if (xml->attributes().value("type") == "long")
00646 return new XmlTextEdit(xml);
00647 else
00648 return new XmlLineEdit(xml);
00649 }
00650 if (tagname == "list") {
00651 return new XmlComboBox(xml);
00652 }
00653 return 0;
00654 }
00655
00656 XmlWidget::XmlWidget(I18nXmlStreamReader *xml, bool readDescription)
00657 {
00658 m_name = xml->attributes().value("name").toString();
00659 m_defaultValue = xml->attributes().value("default").toString();
00660 if (readDescription)
00661 m_description = xml->readI18nText();
00662 }
00663
00664 void IconBuffer::loadFile(QString filename)
00665 {
00666 if (loadedFiles.contains(filename)) return;
00667 loadedFiles << filename;
00668 qDebug() << "Loading icon file: " << filename;
00669 file = new QFile(filename);
00670 file->open(QIODevice::ReadOnly);
00671 basePos = 0;
00672 while (!file->atEnd()) {
00673 QString name = readHeader();
00674 if (name.isEmpty()) break;
00675 if (!len) continue;
00676 icons.insert(filename + ":" + name, readData());
00677 }
00678 file->close();
00679 delete file;
00680 file = 0;
00681 }
00682
00683 QIcon *IconBuffer::icon(QString filename, QString name)
00684 {
00685 loadFile(filename);
00686 QString cname = filename + ":" + name;
00687 if (icons.contains(cname)) {
00688 return &(icons[cname]);
00689 } else {
00690 qDebug() << "Icon" << cname << "not found!";
00691 return 0;
00692 }
00693 }
00694
00695 QString IconBuffer::readHeader()
00696 {
00697
00698 Q_ASSERT(file);
00699 char buffer[101];
00700 file->seek(basePos);
00701 file->read(buffer, 100);
00702 buffer[100] = 0;
00703 QString name = QString::fromAscii(buffer);
00704 file->seek(basePos + 124);
00705 file->read(buffer, 12);
00706 buffer[12] = 0;
00707 bool ok;
00708 len = QString::fromAscii(buffer).toInt(&ok, 8);
00709 basePos += 512;
00710 return name;
00711 }
00712
00713 QIcon IconBuffer::readData()
00714 {
00715 file->seek(basePos);
00716 QByteArray data = file->read(len);
00717 QPixmap pixmap;
00718 pixmap.loadFromData(data);
00719 basePos += static_cast<int>(ceil(len/512.0) * 512);
00720 return QIcon(pixmap);
00721 }
00722
00723 IconBuffer *IconBuffer::_instance = 0;
00724 IconBuffer *IconBuffer::instance()
00725 {
00726 if (!_instance) {
00727 _instance = new IconBuffer();
00728 }
00729 return _instance;
00730 }