mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-19 18:40:13 -06:00
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.
This commit is contained in:
@@ -36,6 +36,8 @@
|
||||
#include <QMimeData>
|
||||
#include <QColorDialog>
|
||||
#include <QDesktopServices>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
|
||||
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<Qt::SortOrder>(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<SqlExecutionArea*>(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;i<tree->model()->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;i<ui->dbTreeWidget->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;i<ui->tabSqlAreas->count();i++) // All SQL tabs content
|
||||
{
|
||||
xml.writeStartElement("sql");
|
||||
xml.writeAttribute("name", ui->tabSqlAreas->tabText(i));
|
||||
xml.writeCharacters(qobject_cast<SqlExecutionArea*>(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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -797,6 +797,9 @@
|
||||
<addaction name="menuImport"/>
|
||||
<addaction name="menuExport"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionOpenProject"/>
|
||||
<addaction name="actionSaveProject"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="fileExitAction"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="editMenu">
|
||||
@@ -1441,6 +1444,36 @@
|
||||
<string>Web&site...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveProject">
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/project_save</normaloff>:/icons/project_save</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save Project</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Save the current session to a file</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Save the current session to a file</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpenProject">
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/project_open</normaloff>:/icons/project_open</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open Project</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Load a working session from a file</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Load a working session from a file</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
@@ -2179,6 +2212,38 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionOpenProject</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>loadProject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>299</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionSaveProject</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>saveProject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>299</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>fileOpen()</slot>
|
||||
@@ -2225,5 +2290,7 @@
|
||||
<slot>openSqlFile()</slot>
|
||||
<slot>saveSqlFile()</slot>
|
||||
<slot>loadExtension()</slot>
|
||||
<slot>loadProject()</slot>
|
||||
<slot>saveProject()</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
|
||||
@@ -37,5 +37,7 @@
|
||||
<file alias="up">bullet_arrow_up.png</file>
|
||||
<file alias="appicon">sqlitebrowser.png</file>
|
||||
<file alias="browser_open">internet-web-browser.png</file>
|
||||
<file alias="project_save">package.png</file>
|
||||
<file alias="project_open">package_go.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
BIN
src/icons/package.png
Normal file
BIN
src/icons/package.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 853 B |
BIN
src/icons/package_go.png
Normal file
BIN
src/icons/package_go.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 898 B |
Reference in New Issue
Block a user