From 62622ae8e8bc916982ca2eae9d3d79364a5f53d9 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Fri, 6 Jun 2014 23:32:35 +0200 Subject: [PATCH] Add support for basic project files This add support for saving and loading of SQLiteBrowser project files. As of now these files contain a reference to the used database file, store some window and widget settings as well as the content of the SQL tabs. Note that while working this is a first draft only. Especially the parsing code still might contain a bug or two and there is a lot more information to be added to these files later (like filters, plot settings, etc.). Also note that the main intention behind these project files was to make the life of users easier while not getting into the way of those who don't need them. The program still remains a database browser and doesn't become a visual database studio thing. --- src/MainWindow.cpp | 225 ++++++++++++++++++++++++++++++++++++++- src/MainWindow.h | 2 + src/MainWindow.ui | 67 ++++++++++++ src/icons/icons.qrc | 2 + src/icons/package.png | Bin 0 -> 853 bytes src/icons/package_go.png | Bin 0 -> 898 bytes 6 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 src/icons/package.png create mode 100644 src/icons/package_go.png diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 3ac04213..472a34f4 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include MainWindow::MainWindow(QWidget* parent) @@ -179,8 +181,8 @@ bool MainWindow::fileOpen(const QString& fileName) setCurrentFile(wFile); retval = true; } else { - QString err = tr("An error occurred: %1").arg(db.lastErrorMessage); - QMessageBox::warning(this, QApplication::applicationName(), err); + // Failed opening file; so it might be a SQLiteBrowser project file + return loadProject(wFile); } loadExtensionsFromSettings(); populateStructure(); @@ -1043,6 +1045,7 @@ void MainWindow::activateFields(bool enable) ui->actionSqlOpenFile->setEnabled(enable); ui->actionSqlOpenTab->setEnabled(enable); ui->actionSqlSaveFile->setEnabled(enable); + ui->actionSaveProject->setEnabled(enable); } void MainWindow::browseTableHeaderClicked(int logicalindex) @@ -1684,3 +1687,221 @@ void MainWindow::updateBrowseDataColumnWidth(int section, int /*old_size*/, int { browseTableColumnWidths[ui->comboBrowseTable->currentText()][section] = new_size; } + +bool MainWindow::loadProject(QString filename) +{ + // Show the open file dialog when no filename was passed as parameter + if(filename.isEmpty()) + { + filename = QFileDialog::getOpenFileName(this, + tr("Choose a file to open"), + QString(), + tr("SQLiteBrowser project(*.sqbpro)")); + } + + if(!filename.isEmpty()) + { + QFile file(filename); + file.open(QFile::ReadOnly | QFile::Text); + + QXmlStreamReader xml(&file); + xml.readNext(); // token == QXmlStreamReader::StartDocument + xml.readNext(); // name == sqlb_project + if(xml.name() != "sqlb_project") + { + QMessageBox::warning(this, qApp->applicationName(), tr("Invalid file format.")); + return false; + } + + while(!xml.atEnd() && !xml.hasError()) + { + // Read next token + QXmlStreamReader::TokenType token = xml.readNext(); + + // Handle element start + if(token == QXmlStreamReader::StartElement) + { + if(xml.name() == "db") + { + // DB file + fileOpen(xml.attributes().value("path").toString()); + ui->dbTreeWidget->collapseAll(); + } else if(xml.name() == "window") { + // Window settings + while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "window") + { + // Currently selected tab + if(xml.name() == "current_tab") + ui->mainTab->setCurrentIndex(xml.attributes().value("id").toString().toInt()); + } + } else if(xml.name() == "tab_structure") { + // Database Structure tab settings + while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "tab_structure") + { + if(xml.name() == "column_width") + { + // Tree view column widths + ui->dbTreeWidget->setColumnWidth(xml.attributes().value("id").toString().toInt(), + xml.attributes().value("width").toString().toInt()); + xml.skipCurrentElement(); + } else if(xml.name() == "expanded_item") { + // Tree view expanded items + int parent = xml.attributes().value("parent").toString().toInt(); + QModelIndex idx; + if(parent == -1) + idx = ui->dbTreeWidget->model()->index(xml.attributes().value("id").toString().toInt(), 0); + else + idx = ui->dbTreeWidget->model()->index(xml.attributes().value("id").toString().toInt(), 0, ui->dbTreeWidget->model()->index(parent, 0)); + ui->dbTreeWidget->expand(idx); + xml.skipCurrentElement(); + } + } + } else if(xml.name() == "tab_browse") { + // Browse Data tab settings + while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "tab_browse") + { + if(xml.name() == "current_table") + { + // Currently selected table + ui->comboBrowseTable->setCurrentIndex(ui->comboBrowseTable->findText(xml.attributes().value("name").toString())); + xml.skipCurrentElement(); + } else if(xml.name() == "column_widths") { + // Column widths + QByteArray temp = QByteArray::fromBase64(xml.attributes().value("data").toUtf8()); + QDataStream stream(temp); + stream >> browseTableColumnWidths; + populateTable(ui->comboBrowseTable->currentText()); // Refresh view + xml.skipCurrentElement(); + } else if(xml.name() == "sort") { + // Sort order + ui->dataTable->sortByColumn(xml.attributes().value("column").toString().toInt(), + static_cast(xml.attributes().value("order").toString().toInt())); + xml.skipCurrentElement(); + } + } + } else if(xml.name() == "tab_sql") { + // Close existing tab + QWidget* w = ui->tabSqlAreas->widget(0); + ui->tabSqlAreas->removeTab(0); + delete w; + + // Execute SQL tab data + while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "tab_sql") + { + if(xml.name() == "sql") + { + // SQL editor tab + unsigned int index = openSqlTab(); + ui->tabSqlAreas->setTabText(index, xml.attributes().value("name").toString()); + qobject_cast(ui->tabSqlAreas->widget(index))->getEditor()->setPlainText(xml.readElementText()); + } else if(xml.name() == "current_tab") { + // Currently selected tab + ui->tabSqlAreas->setCurrentIndex(xml.attributes().value("id").toString().toInt()); + xml.skipCurrentElement(); + } + } + } + } + } + + file.close(); + return !xml.hasError(); + } else { + // No project was opened + return false; + } +} + +static void saveDbTreeState(const QTreeView* tree, QXmlStreamWriter& xml, QModelIndex index = QModelIndex(), int parent_row = -1) +{ + for(int i=0;imodel()->rowCount(index);i++) + { + if(tree->isExpanded(tree->model()->index(i, 0, index))) + { + xml.writeStartElement("expanded_item"); + xml.writeAttribute("id", QString::number(i)); + xml.writeAttribute("parent", QString::number(parent_row)); + xml.writeEndElement(); + } + + saveDbTreeState(tree, xml, tree->model()->index(i, 0, index), i); + } +} + +void MainWindow::saveProject() +{ + QString filename = QFileDialog::getSaveFileName(this, + tr("Choose a filename to save under"), + QString(), + tr("SQLiteBrowser project(*.sqbpro)") + ); + if(!filename.isEmpty()) + { + QFile file(filename); + file.open(QFile::WriteOnly | QFile::Text); + QXmlStreamWriter xml(&file); + xml.writeStartDocument(); + xml.writeStartElement("sqlb_project"); + + // Database file name + xml.writeStartElement("db"); + xml.writeAttribute("path", db.curDBFilename); + xml.writeEndElement(); + + // Window settings + xml.writeStartElement("window"); + xml.writeStartElement("current_tab"); // Currently selected tab + xml.writeAttribute("id", QString::number(ui->mainTab->currentIndex())); + xml.writeEndElement(); + xml.writeEndElement(); + + // Database Structure tab settings + xml.writeStartElement("tab_structure"); + for(int i=0;idbTreeWidget->model()->columnCount();i++) // Widths of tree view columns + { + xml.writeStartElement("column_width"); + xml.writeAttribute("id", QString::number(i)); + xml.writeAttribute("width", QString::number(ui->dbTreeWidget->columnWidth(i))); + xml.writeEndElement(); + } + saveDbTreeState(ui->dbTreeWidget, xml); // Expanded tree items + xml.writeEndElement(); + + // Browse Data tab settings + xml.writeStartElement("tab_browse"); + xml.writeStartElement("current_table"); // Currently selected table + xml.writeAttribute("name", ui->comboBrowseTable->currentText()); + xml.writeEndElement(); + { // Column widths + QByteArray temp; + QDataStream stream(&temp, QIODevice::WriteOnly); + stream << browseTableColumnWidths; + xml.writeStartElement("column_widths"); + xml.writeAttribute("data", temp.toBase64()); + xml.writeEndElement(); + } + xml.writeStartElement("sort"); // Sort order + xml.writeAttribute("column", QString::number(curBrowseOrderByIndex)); + xml.writeAttribute("order", QString::number(curBrowseOrderByMode)); + xml.writeEndElement(); + xml.writeEndElement(); + + // Execute SQL tab data + xml.writeStartElement("tab_sql"); + for(int i=0;itabSqlAreas->count();i++) // All SQL tabs content + { + xml.writeStartElement("sql"); + xml.writeAttribute("name", ui->tabSqlAreas->tabText(i)); + xml.writeCharacters(qobject_cast(ui->tabSqlAreas->widget(i))->getSql()); + xml.writeEndElement(); + } + xml.writeStartElement("current_tab"); // Currently selected tab + xml.writeAttribute("id", QString::number(ui->tabSqlAreas->currentIndex())); + xml.writeEndElement(); + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndDocument(); + file.close(); + } +} diff --git a/src/MainWindow.h b/src/MainWindow.h index 6ab967c6..8eed5c2d 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -174,6 +174,8 @@ private slots: void on_actionBug_report_triggered(); void on_actionWebsite_triggered(); void updateBrowseDataColumnWidth(int section, int /*old_size*/, int new_size); + bool loadProject(QString filename = QString()); + void saveProject(); }; #endif diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 5ad492eb..ba9d49f1 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -797,6 +797,9 @@ + + + @@ -1441,6 +1444,36 @@ Web&site... + + + + :/icons/project_save:/icons/project_save + + + Save Project + + + Save the current session to a file + + + Save the current session to a file + + + + + + :/icons/project_open:/icons/project_open + + + Open Project + + + Load a working session from a file + + + Load a working session from a file + + @@ -2179,6 +2212,38 @@ + + actionOpenProject + triggered() + MainWindow + loadProject() + + + -1 + -1 + + + 399 + 299 + + + + + actionSaveProject + triggered() + MainWindow + saveProject() + + + -1 + -1 + + + 399 + 299 + + + fileOpen() @@ -2225,5 +2290,7 @@ openSqlFile() saveSqlFile() loadExtension() + loadProject() + saveProject() diff --git a/src/icons/icons.qrc b/src/icons/icons.qrc index c4f374c5..9e9a83ae 100644 --- a/src/icons/icons.qrc +++ b/src/icons/icons.qrc @@ -37,5 +37,7 @@ bullet_arrow_up.png sqlitebrowser.png internet-web-browser.png + package.png + package_go.png diff --git a/src/icons/package.png b/src/icons/package.png new file mode 100644 index 0000000000000000000000000000000000000000..da3c2a2d74bab159ba0f65d7db601768258afcb2 GIT binary patch literal 853 zcmV-b1FHOqP)5TQ^(M5v$(QKVE?W+9X! z*o}&~6c?_FreF)9NJB7b5Nbn{G0n4+%uJhR9(V5R|NFTpb|HgjefT!tIhLx@DR+N) zV+fHiR5Yt19}k|KnCsND{tH-`IMJ)3AE?OtyZ4>Un|6(d%h#JK`i&a7^xW9>`yBy` zS4SOHeOpC7$?hH5-#7Rswiue_8Ju*2N@$58=a#2OTA3png`w3v->gWif7t%e$ z$NLVS!tFT#8WL|Wa&K~+{%4P2cRfwesYV1_!F=3OaRVHl(>=`%&{x*s30c}#CNE@&;ItrAv!f!)Oy$Q9t$uS=(sD$-J{T*^(8Eez1E-l3}} zPrfHZ1`qsIFe&gipuL8-IZbo2Yg{lFGKs?ZZWcOaOdk*3`5T;$?AjbG1#`B510Er^h2)2r3Y{!8_2Gj=$KzuN5 zaErtW8W_Y2iJJjY)5pmTVJoPJYpanPOEuYHclM^C1F>${hFRpdi8a<2H|Xudf78bm(zwJ9`K%6I?q*Ua~ fW9JvIbn5*B+_J)rUMBs>00000NkvXXu0mjfH&TkY literal 0 HcmV?d00001 diff --git a/src/icons/package_go.png b/src/icons/package_go.png new file mode 100644 index 0000000000000000000000000000000000000000..aace63ad6f91537268eb6e9bf328743da7c631c6 GIT binary patch literal 898 zcmV-|1AY97P)yqPWur#3F*- zi1-H*6GJgONgz~1E;+_Oe@~@VF-1Iy-=*@ zV9T!Kgk#%59lVt}Zj7B_)9wTKYBy6YEwPxKqOdeGuEw0%cVhe}Yj*hPSBMNYZ5yz{ z4STT*%d9TVV4NauDND$z(%QKb>=kMr>ckh0lFuesOioc=Nq^^8BQNYYbk1@M%M`O? zh?6H&UZR}Ol3%#RzJX5(MAktmg_e?7`2>w^4^!6w)4$9==U0)EV$}u1A)*bPRF?jv zHdar4EJB1b*f+rh!M+8R159x8%Qjd0I{0j+|69pUC)f(>!P(OTs8c~;XuVtXzfO>s`m zrjW9YI38*Yel_NvP&FVfNEx)sn-kxIx;WzEcpe*LJBYXLr(llS6I0o+c9O z0L_4N7u$0%Dx~ks;fjYRF0OIOR}1uPI!MtibK=J3pihiBTv)jD>R%(Llj(_XFa#mG z6Wg=#j7Q7*PFmM*W@B9!ftm;#qU}sCT;|&KtBZ%Fe#3()Pk+9@Sw&8%k=NQD>92qN zkFT*E2S*%i&!MYvH;;DpF?sU}zb4Mlls)au3~BX$XZr12Z?_sbts>8Fec~0Xl1q`9 zxyH%T#p<5UCqrY2(J4oGEHk9ens22BN3dYXATM07*qoM6N<$g3xBAZ~y=R literal 0 HcmV?d00001