#include "MainWindow.h" #include "ui_MainWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include "CreateIndexDialog.h" #include "AboutDialog.h" #include "EditTableDialog.h" #include "ImportCsvDialog.h" #include "ExportCsvDialog.h" #include "PreferencesDialog.h" #include "EditDialog.h" #include "FindDialog.h" #include "SQLiteSyntaxHighlighter.h" #include "sqltextedit.h" #include "sqlitetablemodel.h" #include "FilterTableHeader.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), m_browseTableModel(new SqliteTableModel(this, &db)), sqliteHighlighterTabSql(0), sqliteHighlighterLogUser(0), sqliteHighlighterLogApp(0), editWin(new EditDialog(this)), findWin(0), gotoValidator(new QIntValidator(0, 0, this)) { ui->setupUi(this); init(); activateFields(false); resetBrowser(); updateRecentFileActions(); } MainWindow::~MainWindow() { delete gotoValidator; delete ui; } void MainWindow::init() { // Init the SQL log dock db.mainWindow = this; // Set up the db tree widget ui->dbTreeWidget->setColumnHidden(1, true); ui->dbTreeWidget->setColumnWidth(0, 300); // Set the validator for the goto line edit ui->editGoto->setValidator(gotoValidator); // Create the SQL sytax highlighters createSyntaxHighlighters(); // Set up DB models ui->dataTable->setModel(m_browseTableModel); queryResultListModel = new QStandardItemModel(this); ui->queryResultTableView->setModel(queryResultListModel); FilterTableHeader* tableHeader = new FilterTableHeader(ui->dataTable); connect(tableHeader, SIGNAL(filterChanged(int,QString)), m_browseTableModel, SLOT(updateFilter(int,QString))); ui->dataTable->setHorizontalHeader(tableHeader); // Create the actions for the recently opened dbs list for(int i = 0; i < MaxRecentFiles; ++i) { recentFileActs[i] = new QAction(this); recentFileActs[i]->setVisible(false); connect(recentFileActs[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); } for(int i = 0; i < MaxRecentFiles; ++i) ui->fileMenu->insertAction(ui->fileExitAction, recentFileActs[i]); recentSeparatorAct = ui->fileMenu->insertSeparator(ui->fileExitAction); // Create popup menus popupTableMenu = new QMenu(this); popupTableMenu->addAction(ui->editModifyTableAction); popupTableMenu->addSeparator(); popupTableMenu->addAction(ui->editDeleteObjectAction); // Set state of checkable menu actions ui->sqlLogAction->setChecked(!ui->dockLog->isHidden()); ui->viewDBToolbarAction->setChecked(!ui->toolbarDB->isHidden()); // Set statusbar fields statusEncodingLabel = new QLabel(ui->statusbar); statusEncodingLabel->setEnabled(false); statusEncodingLabel->setText("UTF-8"); statusEncodingLabel->setToolTip(tr("Database encoding")); ui->statusbar->addPermanentWidget(statusEncodingLabel); // Connect some more signals and slots connect(tableHeader, SIGNAL(sectionClicked(int)), this, SLOT(browseTableHeaderClicked(int))); connect(ui->dataTable->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(setRecordsetLabel())); connect(editWin, SIGNAL(goingAway()), this, SLOT(editWinAway())); connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int, QByteArray))); // Load window settings restoreGeometry(PreferencesDialog::getSettingsValue("MainWindow", "geometry").toByteArray()); restoreState(PreferencesDialog::getSettingsValue("MainWindow", "windowState").toByteArray()); ui->comboLogSubmittedBy->setCurrentIndex(ui->comboLogSubmittedBy->findText(PreferencesDialog::getSettingsValue("SQLLogDock", "Log").toString())); // Set other window settings setAcceptDrops(true); setWindowTitle(QApplication::applicationName()); // Fonts for edit fields QFont font("Monospace"); font.setStyleHint(QFont::TypeWriter); font.setPointSize(8); ui->sqlTextEdit->setFont(font); ui->editLogApplication->setFont(font); ui->editLogUser->setFont(font); } //*********************************************************** //*** Open File void MainWindow::fileOpen(const QString & fileName) { QString wFile = fileName; if (!QFile::exists(wFile)) { wFile = QFileDialog::getOpenFileName( this, tr("Choose a database file"), PreferencesDialog::getSettingsValue("db", "defaultlocation").toString()); } if(QFile::exists(wFile) ) { fileClose(); if(db.open(wFile)) { statusEncodingLabel->setText(db.getPragma("encoding")); setCurrentFile(wFile); } else { QString err = tr("An error occurred: %1").arg(db.lastErrorMessage); QMessageBox::warning(this, QApplication::applicationName(), err); } populateStructure(); resetBrowser(); if(ui->mainTab->currentIndex() == 2) loadPragmas(); } } void MainWindow::fileOpen() { fileOpen(QString()); } void MainWindow::fileNew() { QString fileName = QFileDialog::getSaveFileName(this, tr("Choose a filename to save under"), PreferencesDialog::getSettingsValue("db", "defaultlocation").toString()); if(!fileName.isEmpty()) { if(QFile::exists(fileName)) QFile::remove(fileName); db.create(fileName); setCurrentFile(fileName); statusEncodingLabel->setText(db.getPragma("encoding")); populateStructure(); resetBrowser(); createTable(); } } //** Populate DbTree Structure void MainWindow::populateStructure() { ui->dbTreeWidget->model()->removeRows(0, ui->dbTreeWidget->model()->rowCount()); ui->sqlTextEdit->clearFieldCompleterModelMap(); ui->sqlTextEdit->setDefaultCompleterModel(new QStandardItemModel()); if (!db.isOpen()){ return; } db.updateSchema(); QStringList tblnames = db.getBrowsableObjectNames(); sqliteHighlighterTabSql->setTableNames(tblnames); sqliteHighlighterLogUser->setTableNames(tblnames); sqliteHighlighterLogApp->setTableNames(tblnames); // setup models for sqltextedit autocomplete QStandardItemModel* completerModel = new QStandardItemModel(); completerModel->setRowCount(tblnames.count()); completerModel->setColumnCount(1); objectMap tab = db.getBrowsableObjects(); int row = 0; for(objectMap::ConstIterator it=tab.begin(); it!=tab.end(); ++it, ++row) { QString sName = it.value().getname(); QStandardItem* item = new QStandardItem(sName); item->setIcon(QIcon(QString(":icons/%1").arg(it.value().gettype()))); completerModel->setItem(row, 0, item); // If it is a table add the field Nodes if((*it).gettype() == "table" || (*it).gettype() == "view") { QStandardItemModel* tablefieldmodel = new QStandardItemModel(); tablefieldmodel->setRowCount((*it).fldmap.count()); tablefieldmodel->setColumnCount(1); fieldMap::ConstIterator fit; int fldrow = 0; for ( fit = (*it).fldmap.begin(); fit != (*it).fldmap.end(); ++fit, ++fldrow ) { QString fieldname = fit.value().getname(); QStandardItem* fldItem = new QStandardItem(fieldname); fldItem->setIcon(QIcon(":/icons/field")); tablefieldmodel->setItem(fldrow, 0, fldItem); } ui->sqlTextEdit->addFieldCompleterModel(sName.toLower(), tablefieldmodel); } } ui->sqlTextEdit->setDefaultCompleterModel(completerModel); // end setup models for sqltextedit autocomplete // fill the structure tab QMap typeToParentItem; QTreeWidgetItem* itemTables = new QTreeWidgetItem(ui->dbTreeWidget); itemTables->setIcon(0, QIcon(QString(":/icons/table"))); itemTables->setText(0, tr("Tables (%1)").arg(db.objMap.values("table").count())); typeToParentItem.insert("table", itemTables); QTreeWidgetItem* itemIndices = new QTreeWidgetItem(ui->dbTreeWidget); itemIndices->setIcon(0, QIcon(QString(":/icons/index"))); itemIndices->setText(0, tr("Indices (%1)").arg(db.objMap.values("index").count())); typeToParentItem.insert("index", itemIndices); QTreeWidgetItem* itemViews = new QTreeWidgetItem(ui->dbTreeWidget); itemViews->setIcon(0, QIcon(QString(":/icons/view"))); itemViews->setText(0, tr("Views (%1)").arg(db.objMap.values("view").count())); typeToParentItem.insert("view", itemViews); QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(ui->dbTreeWidget); itemTriggers->setIcon(0, QIcon(QString(":/icons/trigger"))); itemTriggers->setText(0, tr("Triggers (%1)").arg(db.objMap.values("trigger").count())); typeToParentItem.insert("trigger", itemTriggers); ui->dbTreeWidget->setItemExpanded(itemTables, true); ui->dbTreeWidget->setItemExpanded(itemIndices, true); ui->dbTreeWidget->setItemExpanded(itemViews, true); ui->dbTreeWidget->setItemExpanded(itemTriggers, true); for(objectMap::ConstIterator it=db.objMap.begin();it!=db.objMap.end();++it) { // Object node QTreeWidgetItem *tableItem = new QTreeWidgetItem(typeToParentItem.value((*it).gettype())); tableItem->setIcon(0, QIcon(QString(":/icons/%1").arg((*it).gettype()))); tableItem->setText(0, (*it).getname()); tableItem->setText(1, (*it).gettype()); tableItem->setText(3, (*it).getsql()); // If it is a table add the field Nodes if((*it).gettype() == "table" || (*it).gettype() == "view") { fieldMap::ConstIterator fit; for ( fit = (*it).fldmap.begin(); fit != (*it).fldmap.end(); ++fit ) { QTreeWidgetItem *fldItem = new QTreeWidgetItem(tableItem); fldItem->setText(0, fit.value().getname()); fldItem->setText(1, "field"); fldItem->setText(2, fit.value().gettype()); fldItem->setIcon(0, QIcon(":/icons/field")); } } } } void MainWindow::populateTable( const QString & tablename) { // Remove the model-view link if the table name is empty in order to remove any data from the view if(tablename.isEmpty()) { ui->dataTable->setModel(0); return; } QApplication::setOverrideCursor(Qt::WaitCursor); // Set model ui->dataTable->setModel(m_browseTableModel); // Set new table m_browseTableModel->setTable(tablename); ui->dataTable->setColumnHidden(0, true); // Reset sorting curBrowseOrderByIndex = 0; curBrowseOrderByMode = Qt::AscendingOrder; m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); // Get table layout db.browseTable(tablename); // Update the filter row qobject_cast(ui->dataTable->horizontalHeader())->generateFilters(m_browseTableModel->columnCount()); // Activate the add and delete record buttons and editing only if a table has been selected bool is_table = db.getObjectByName(tablename).gettype() == "table"; ui->buttonNewRecord->setEnabled(is_table); ui->buttonDeleteRecord->setEnabled(is_table); ui->dataTable->setEditTriggers(is_table ? QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); // Set the recordset label setRecordsetLabel(); //got to keep findWin in synch if(findWin) findWin->resetFields(); if(editWin) editWin->reset(); QApplication::restoreOverrideCursor(); } void MainWindow::resetBrowser() { QString sCurrentTable = ui->comboBrowseTable->currentText(); ui->comboBrowseTable->clear(); objectMap tab = db.getBrowsableObjects(); for(objectMap::ConstIterator i=tab.begin();i!=tab.end();++i) { ui->comboBrowseTable->addItem(QIcon(QString(":icons/%1").arg(i.value().gettype())), i.value().getname()); //ui->comboBrowseTable->addItems(tab); } setRecordsetLabel(); int pos = ui->comboBrowseTable->findText(sCurrentTable); pos = pos == -1 ? 0 : pos; ui->comboBrowseTable->setCurrentIndex(pos); curBrowseOrderByIndex = 0; curBrowseOrderByMode = Qt::AscendingOrder; m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); populateTable(ui->comboBrowseTable->currentText()); } void MainWindow::fileClose() { db.close(); setWindowTitle(QApplication::applicationName()); resetBrowser(); populateStructure(); loadPragmas(); activateFields(false); ui->buttonLogClear->click(); } void MainWindow::fileExit() { if (db.isOpen()) { if (db.getDirty()) { QString msg = tr("Do you want to save the changes made to the database file %1?").arg(db.curDBFilename); if(QMessageBox::question( this, QApplication::applicationName() ,msg, QMessageBox::Yes, QMessageBox::No)==QMessageBox::Yes) db.save(); else db.revert(); //not really necessary, I think... but will not hurt. } db.close(); } } void MainWindow::closeEvent( QCloseEvent* event ) { PreferencesDialog::setSettingsValue("MainWindow", "geometry", saveGeometry()); PreferencesDialog::setSettingsValue("MainWindow", "windowState", saveState()); PreferencesDialog::setSettingsValue("SQLLogDock", "Log", ui->comboLogSubmittedBy->currentText()); fileExit(); QMainWindow::closeEvent(event); } void MainWindow::addRecord() { int row = m_browseTableModel->rowCount(); if(m_browseTableModel->insertRow(row)) { selectTableLine(row); } else { QMessageBox::warning( this, QApplication::applicationName(), tr("Error adding record:\n") + db.lastErrorMessage); } } void MainWindow::deleteRecord() { if(ui->dataTable->currentIndex().isValid()) { int row = ui->dataTable->currentIndex().row(); if(m_browseTableModel->removeRow(row)) { populateTable(db.curBrowseTableName); if(row > m_browseTableModel->totalRowCount()) row = m_browseTableModel->totalRowCount(); selectTableLine(row); } else { QMessageBox::warning( this, QApplication::applicationName(), tr("Error deleting record:\n") + db.lastErrorMessage); } } else { QMessageBox::information( this, QApplication::applicationName(), tr("Please select a record first")); } } void MainWindow::selectTableLine(int lineToSelect) { ui->dataTable->clearSelection(); ui->dataTable->selectRow(lineToSelect); ui->dataTable->scrollTo(ui->dataTable->currentIndex()); } void MainWindow::navigatePrevious() { int curRow = ui->dataTable->currentIndex().row(); curRow -= 100; if(curRow < 0) curRow = 0; selectTableLine(curRow); } void MainWindow::navigateNext() { // TODO: Fetch more data from DB if necessary int curRow = ui->dataTable->currentIndex().row(); curRow += 100; if(curRow >= m_browseTableModel->totalRowCount()) curRow = m_browseTableModel->totalRowCount() - 1; selectTableLine(curRow); } void MainWindow::navigateGoto() { // TODO: Fetch more data from DB if necessary int row = ui->editGoto->text().toInt(); if(row <= 0) row = 1; if(row > m_browseTableModel->totalRowCount()) row = m_browseTableModel->totalRowCount(); selectTableLine(row - 1); ui->editGoto->setText(QString::number(row)); } void MainWindow::setRecordsetLabel() { // Get all the numbers, i.e. the number of the first row and the last row as well as the total number of rows int from = ui->dataTable->verticalHeader()->visualIndexAt(0) + 1; int to = ui->dataTable->verticalHeader()->visualIndexAt(ui->dataTable->height()) - 1; int total = m_browseTableModel->totalRowCount(); if(to == -2) to = total; // Update the validator of the goto row field gotoValidator->setRange(0, total); // Update the label showing the current position ui->labelRecordset->setText(tr("%1 - %2 of %3").arg(from).arg(to).arg(total)); } void MainWindow::browseFind(bool open) { if(open) { if(!findWin) { findWin = new FindDialog(this); connect(findWin, SIGNAL(lookfor(const QString&, const QString&, const QString&)), this, SLOT(lookfor(const QString&, const QString&, const QString&))); connect(findWin, SIGNAL(showrecord(int)),this, SLOT(selectTableLine(int))); connect(findWin, SIGNAL(goingAway()),this, SLOT(browseFindAway())); } findWin->resetFields(db.getTableFields(db.curBrowseTableName)); findWin->show(); } else { if(findWin) findWin->hide(); } } void MainWindow::browseFindAway() { ui->buttonFind->toggle(); } void MainWindow::browseRefresh() { populateTable(ui->comboBrowseTable->currentText()); } void MainWindow::lookfor( const QString & wfield, const QString & woperator, const QString & wsearchterm ) { if (!db.isOpen()){ QMessageBox::information( this, QApplication::applicationName(), tr("There is no database opened. Please open or create a new database file.")); return; } //we may need to modify woperator and wsearchterm, so use copies QString finaloperator = woperator; QString finalsearchterm = wsearchterm; //special case for CONTAINS operator: use LIKE and surround the search word with % characters if(woperator.compare(tr("contains")) == 0) { finaloperator = QString("LIKE"); QString newsearchterm = "%"; newsearchterm.append(wsearchterm); newsearchterm.append("%"); finalsearchterm = QString(newsearchterm); } QApplication::setOverrideCursor( Qt::WaitCursor ); QString statement = QString("SELECT rowid, `%1` FROM `%2` WHERE `%3` %4 ").arg(wfield).arg(db.curBrowseTableName).arg(wfield).arg(finaloperator); //searchterm needs to be quoted if it is not a number bool ok = false; finalsearchterm.toDouble(&ok); if (!ok) finalsearchterm.toInt(&ok, 10); if (!ok) {//not a number, quote it char * formSQL = sqlite3_mprintf("%Q",(const char *) finalsearchterm.toUtf8()); statement.append(formSQL); if (formSQL) sqlite3_free(formSQL); } else {//append the number, unquoted statement.append(finalsearchterm); } statement.append(" ORDER BY rowid;"); resultMap res = db.getFindResults(statement); findWin->showResults(res); QApplication::restoreOverrideCursor(); } void MainWindow::createTable() { if (!db.isOpen()){ QMessageBox::information( this, QApplication::applicationName(), tr("There is no database opened. Please open or create a new database file.")); return; } EditTableDialog dialog(&db, "", this); if(dialog.exec()) { populateStructure(); resetBrowser(); } } void MainWindow::createIndex() { if (!db.isOpen()){ QMessageBox::information( this, QApplication::applicationName(), tr("There is no database opened. Please open or create a new database file.")); return; } CreateIndexDialog dialog(&db, this); if(dialog.exec()) { populateStructure(); resetBrowser(); } } void MainWindow::compact() { QApplication::setOverrideCursor( Qt::WaitCursor ); if (!db.compact()){ QString error = tr("Error: could not compact the database file. Message from database engine: %1").arg(db.lastErrorMessage); QApplication::restoreOverrideCursor( ); QMessageBox::warning( this, QApplication::applicationName(), error ); } else { QApplication::restoreOverrideCursor( ); QMessageBox::information(this, QApplication::applicationName(), tr("Database successfully compacted.")); } db.open(db.curDBFilename); populateStructure(); resetBrowser(); } void MainWindow::deleteObject() { // Get name of table to delete QString table = ui->dbTreeWidget->currentItem()->text(0); QString type = ui->dbTreeWidget->currentItem()->text(1); // Ask user if he really wants to delete that table if(QMessageBox::warning(this, QApplication::applicationName(), tr("Are you sure you want to delete the %1 '%2'?\nAll data associated with the %1 will be lost.").arg(type).arg(table), QMessageBox::Yes, QMessageBox::No | QMessageBox::Default | QMessageBox::Escape) == QMessageBox::Yes) { // Delete the table QString statement = QString("DROP %1 `%2`;").arg(type.toUpper()).arg(table); if(!db.executeSQL( statement)) { QString error = tr("Error: could not delete the %1. Message from database engine:\n%2").arg(type).arg(db.lastErrorMessage); QMessageBox::warning(this, QApplication::applicationName(), error); } else { populateStructure(); resetBrowser(); } } } void MainWindow::editTable() { if (!db.isOpen()){ QMessageBox::information( this, QApplication::applicationName(), tr("There is no database opened.")); return; } if(!ui->dbTreeWidget->selectionModel()->hasSelection()){ return; } QString tableToEdit = ui->dbTreeWidget->currentItem()->text(0); EditTableDialog dialog(&db, tableToEdit, this); if(dialog.exec()) { populateStructure(); resetBrowser(); } } void MainWindow::helpWhatsThis() { QWhatsThis::enterWhatsThisMode (); } void MainWindow::helpAbout() { AboutDialog dialog(this); dialog.exec(); } void MainWindow::updateRecordText(int row, int col, const QByteArray& newtext) { m_browseTableModel->setData(m_browseTableModel->index(row, col), newtext); } void MainWindow::editWinAway() { editWin->hide(); activateWindow(); ui->dataTable->setCurrentIndex(ui->dataTable->currentIndex().sibling(editWin->getCurrentRow(), editWin->getCurrentCol())); } void MainWindow::editText(const QModelIndex& index) { editWin->loadText(index.data().toByteArray(), index.row(), index.column()); editWin->show(); } void MainWindow::doubleClickTable(const QModelIndex& index) { if(!index.isValid()) { qDebug("no cell selected"); return; } // Don't allow editing of other objects than tables if(db.getObjectByName(ui->comboBrowseTable->currentText()).gettype() != "table") return; editText(index); } /* * I'm still not happy how the results are represented to the user * right now you only see the result of the last executed statement. * A better experiance would be tabs on the bottom with query results * for all the executed statements. * Or at least a some way the use could see results/status message * per executed statement. */ void MainWindow::executeQuery() { // if a part of the query is selected, we will only execute this part QString query = ui->sqlTextEdit->textCursor().selectedText(); if(query.isEmpty()) query = ui->sqlTextEdit->toPlainText().trimmed(); if (query.isEmpty()) return; //log the query db.logSQL(query, kLogMsg_User); sqlite3_stmt *vm; QByteArray utf8Query = query.toUtf8(); const char *tail = utf8Query.data(); int sql3status = 0; QString statusMessage; bool modified = false; bool wasdirty = db.getDirty(); // there is no choice, we have to start a transaction before // we create the prepared statement, otherwise every executed // statement will get committed after the prepared statement // gets finalized, see http://www.sqlite.org/lang_transaction.html db.setRestorePoint(); //Accept multi-line queries, by looping until the tail is empty do { queryResultListModel->removeRows(0, queryResultListModel->rowCount()); queryResultListModel->removeColumns(0, queryResultListModel->columnCount()); queryResultListModel->setHorizontalHeaderLabels(QStringList()); queryResultListModel->setVerticalHeaderLabels(QStringList()); const char* qbegin = tail; sql3status = sqlite3_prepare_v2(db._db,tail,utf8Query.length(), &vm, &tail); QString queryPart = QString::fromUtf8(qbegin, tail - qbegin); if (sql3status == SQLITE_OK){ int rownum = 0; bool mustCreateColumns = true; sql3status = sqlite3_step(vm); switch(sql3status) { case SQLITE_ROW: { do { int ncol = sqlite3_data_count(vm); //setup num of cols here for display grid if (mustCreateColumns) { QStringList headerList; for (int e=0; esetHorizontalHeaderLabels(headerList); mustCreateColumns = false; } for (int e=0; esetItem(rownum, e, new QStandardItem(QString(firstline))); } queryResultListModel->setVerticalHeaderItem(rownum, new QStandardItem(QString::number(rownum + 1))); rownum++; } while ( sqlite3_step(vm) == SQLITE_ROW ); sql3status = SQLITE_OK; } case SQLITE_DONE: statusMessage = tr("%1 Rows returned from: %2").arg(rownum).arg(queryPart); case SQLITE_OK: { if( !queryPart.trimmed().startsWith("SELECT", Qt::CaseInsensitive) ) { modified = true; statusMessage = tr("Query executed successfully: %1").arg(queryPart); } } break; default: statusMessage = QString::fromUtf8((const char*)sqlite3_errmsg(db._db)) + ": " + queryPart; break; } sqlite3_finalize(vm); } else { statusMessage = QString::fromUtf8((const char*)sqlite3_errmsg(db._db)) + ": " + queryPart; } ui->queryErrorLineEdit->setText(statusMessage); ui->queryResultTableView->resizeColumnsToContents(); } while( tail && *tail != 0 && (sql3status == SQLITE_OK || sql3status == SQLITE_DONE)); if(!modified && !wasdirty) db.revert(); // better rollback, if the logic is not enough we can tune it. } void MainWindow::mainTabSelected(int tabindex) { if(tabindex == 0) { populateStructure(); } else if(tabindex == 1) { populateStructure(); resetBrowser(); } else if(tabindex == 2) { loadPragmas(); } } void MainWindow::importTableFromCSV() { QString wFile = QFileDialog::getOpenFileName( this, tr("Choose a text file"), PreferencesDialog::getSettingsValue("db", "defaultlocation").toString(), tr("Text files(*.csv *.txt);;All files(*)")); if (QFile::exists(wFile) ) { ImportCsvDialog dialog(wFile, &db, this); if(dialog.exec()) { populateStructure(); resetBrowser(); QMessageBox::information(this, QApplication::applicationName(), tr("Import completed")); } } } void MainWindow::exportTableToCSV() { ExportCsvDialog dialog(&db, this); dialog.exec(); } void MainWindow::dbState( bool dirty ) { ui->fileSaveAction->setEnabled(dirty); ui->fileRevertAction->setEnabled(dirty); } void MainWindow::fileSave() { if(db.isOpen()) db.save(); } void MainWindow::fileRevert() { if (db.isOpen()){ QString msg = tr("Are you sure you want to undo all changes made to the database file '%1' since the last save?").arg(db.curDBFilename); if(QMessageBox::question(this, QApplication::applicationName(), msg, QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape) == QMessageBox::Yes) { db.revert(); populateStructure(); resetBrowser(); } } } void MainWindow::exportDatabaseToSQL() { QString fileName = QFileDialog::getSaveFileName( this, tr("Choose a filename to export"), PreferencesDialog::getSettingsValue("db", "defaultlocation").toString(), tr("Text files(*.sql *.txt)")); if(fileName.size()) { if(!db.dump(fileName)) QMessageBox::warning(this, QApplication::applicationName(), tr("Export cancelled or failed.")); else QMessageBox::information(this, QApplication::applicationName(), tr("Export completed.")); } } void MainWindow::importDatabaseFromSQL() { QString fileName = QFileDialog::getOpenFileName( this, tr("Choose a file to import"), PreferencesDialog::getSettingsValue("db", "defaultlocation").toString(), tr("Text files(*.sql *.txt);;All files(*)")); if (fileName.size() > 0) { QString msg = tr("Do you want to create a new database file to hold the imported data?\nIf you answer NO we will attempt to import data in the .sql file to the current database."); if (QMessageBox::question( this, QApplication::applicationName() ,msg, QMessageBox::Yes, QMessageBox::No)==QMessageBox::Yes) { QString newDBfile = QFileDialog::getSaveFileName( this, tr("Choose a filename to save under"), PreferencesDialog::getSettingsValue("db", "defaultlocation").toString()); if (QFile::exists(newDBfile) ) { QString err = tr("File %1 already exists. Please choose a different name.").arg(newDBfile); QMessageBox::information( this, QApplication::applicationName() ,err); return; } if(!fileName.isNull()) db.create(newDBfile); } // Open, read, execute and close file QApplication::setOverrideCursor(Qt::WaitCursor); QFile f(fileName); f.open(QIODevice::ReadOnly); if(!db.executeMultiSQL(f.readAll())) QMessageBox::warning(this, QApplication::applicationName(), tr("Error importing data: %1").arg(db.lastErrorMessage)); else QMessageBox::information(this, QApplication::applicationName(), tr("Import completed.")); f.close(); QApplication::restoreOverrideCursor(); // Resfresh window populateStructure(); resetBrowser(); } } void MainWindow::openPreferences() { PreferencesDialog dialog(this); if(dialog.exec()) { createSyntaxHighlighters(); populateStructure(); resetBrowser(); } } void MainWindow::createSyntaxHighlighters() { delete sqliteHighlighterLogApp; delete sqliteHighlighterLogUser; delete sqliteHighlighterTabSql; sqliteHighlighterLogApp = new SQLiteSyntaxHighlighter(ui->editLogApplication->document()); sqliteHighlighterLogUser = new SQLiteSyntaxHighlighter(ui->editLogUser->document()); sqliteHighlighterTabSql = new SQLiteSyntaxHighlighter(ui->sqlTextEdit->document()); } //****************************************************************** //** Tree Events //****************************************************************** //** Db Tree Context Menu void MainWindow::createTreeContextMenu(const QPoint &qPoint) { if(!ui->dbTreeWidget->selectionModel()->hasSelection()) return; QTreeWidgetItem *cItem = ui->dbTreeWidget->currentItem(); if(cItem->text(1) == "table" || cItem->text(1) == "view" || cItem->text(1) == "trigger" || cItem->text(1) == "index") popupTableMenu->exec(ui->dbTreeWidget->mapToGlobal(qPoint)); } //** Tree selection changed void MainWindow::changeTreeSelection() { // Just assume first that something's selected that can not be edited at all ui->editDeleteObjectAction->setEnabled(false); ui->editModifyTableAction->setEnabled(false); if(ui->dbTreeWidget->currentItem() == 0) return; // Change the text of the actions ui->editDeleteObjectAction->setIcon(QIcon(QString(":icons/%1_delete").arg(ui->dbTreeWidget->currentItem()->text(1)))); if(ui->dbTreeWidget->currentItem()->text(1) == "view") ui->editDeleteObjectAction->setText(tr("Delete View")); else if(ui->dbTreeWidget->currentItem()->text(1) == "trigger") ui->editDeleteObjectAction->setText(tr("Delete Trigger")); else if(ui->dbTreeWidget->currentItem()->text(1) == "index") ui->editDeleteObjectAction->setText(tr("Delete Index")); else ui->editDeleteObjectAction->setText(tr("Delete Table")); // Activate actions if(ui->dbTreeWidget->currentItem()->text(1) == "table") { ui->editDeleteObjectAction->setEnabled(true); ui->editModifyTableAction->setEnabled(true); } else if(ui->dbTreeWidget->currentItem()->text(1) == "view" || ui->dbTreeWidget->currentItem()->text(1) == "trigger" || ui->dbTreeWidget->currentItem()->text(1) == "index") { ui->editDeleteObjectAction->setEnabled(true); } } void MainWindow::openRecentFile() { QAction *action = qobject_cast(sender()); if (action) fileOpen(action->data().toString()); } void MainWindow::updateRecentFileActions() { QStringList files = PreferencesDialog::getSettingsValue("General", "recentFileList").toStringList(); int numRecentFiles = qMin(files.size(), (int)MaxRecentFiles); for (int i = 0; i < numRecentFiles; ++i) { QString text = tr("&%1 %2").arg(i + 1).arg(files[i]); recentFileActs[i]->setText(text); recentFileActs[i]->setData(files[i]); recentFileActs[i]->setVisible(true); } for (int j = numRecentFiles; j < MaxRecentFiles; ++j) recentFileActs[j]->setVisible(false); recentSeparatorAct->setVisible(numRecentFiles > 0); } void MainWindow::setCurrentFile(const QString &fileName) { setWindowFilePath(fileName); setWindowTitle( QApplication::applicationName() +" - "+fileName); activateFields(true); QStringList files = PreferencesDialog::getSettingsValue("General", "recentFileList").toStringList(); files.removeAll(fileName); files.prepend(fileName); while (files.size() > MaxRecentFiles) files.removeLast(); PreferencesDialog::setSettingsValue("General", "recentFileList", files); foreach (QWidget *widget, QApplication::topLevelWidgets()) { MainWindow *mainWin = qobject_cast(widget); if (mainWin) mainWin->updateRecentFileActions(); } } void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if( event->mimeData()->hasFormat("text/uri-list") ) event->acceptProposedAction(); } void MainWindow::dropEvent(QDropEvent *event) { QList urls = event->mimeData()->urls(); if( urls.isEmpty() ) return; QString fileName = urls.first().toLocalFile(); if(!fileName.isEmpty()) fileOpen(fileName); } void MainWindow::activateFields(bool enable) { ui->fileCloseAction->setEnabled(enable); ui->fileCompactAction->setEnabled(enable); ui->fileExportCSVAction->setEnabled(enable); ui->fileExportSQLAction->setEnabled(enable); ui->fileImportCSVAction->setEnabled(enable); ui->editCreateTableAction->setEnabled(enable); ui->editCreateIndexAction->setEnabled(enable); ui->buttonNext->setEnabled(enable); ui->buttonPrevious->setEnabled(enable); ui->executeQueryButton->setEnabled(enable); ui->scrollAreaWidgetContents->setEnabled(enable); ui->buttonBoxPragmas->setEnabled(enable); ui->buttonGoto->setEnabled(enable); ui->editGoto->setEnabled(enable); ui->buttonRefresh->setEnabled(enable); ui->buttonDeleteRecord->setEnabled(enable); ui->buttonNewRecord->setEnabled(enable); } void MainWindow::browseTableHeaderClicked(int logicalindex) { // Abort if there is more than one column selected because this tells us that the user pretty sure wants to do a range selection instead of sorting data if(ui->dataTable->selectionModel()->selectedColumns().count() > 1) return; // instead of the column name we just use the column index, +2 because 'rowid, *' is the projection curBrowseOrderByIndex = logicalindex; curBrowseOrderByMode = curBrowseOrderByMode == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder; m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); // select the first item in the column so the header is bold // we might try to select the last selected item ui->dataTable->setCurrentIndex(ui->dataTable->currentIndex().sibling(0, logicalindex)); } void MainWindow::resizeEvent(QResizeEvent*) { setRecordsetLabel(); } void MainWindow::loadPragmas() { pragmaValues.autovacuum = db.getPragma("auto_vacuum").toInt(); pragmaValues.automatic_index = db.getPragma("automatic_index").toInt(); pragmaValues.checkpoint_fullsync = db.getPragma("checkpoint_fullfsync").toInt(); pragmaValues.foreign_keys = db.getPragma("foreign_keys").toInt(); pragmaValues.fullfsync = db.getPragma("fullfsync").toInt(); pragmaValues.ignore_check_constraints = db.getPragma("ignore_check_constraints").toInt(); pragmaValues.journal_mode = db.getPragma("journal_mode").toUpper(); pragmaValues.journal_size_limit = db.getPragma("journal_size_limit").toInt(); pragmaValues.locking_mode = db.getPragma("locking_mode").toUpper(); pragmaValues.max_page_count = db.getPragma("max_page_count").toInt(); pragmaValues.page_size = db.getPragma("page_size").toInt(); pragmaValues.recursive_triggers = db.getPragma("recursive_triggers").toInt(); pragmaValues.secure_delete = db.getPragma("secure_delete").toInt(); pragmaValues.synchronous = db.getPragma("synchronous").toInt(); pragmaValues.temp_store = db.getPragma("temp_store").toInt(); pragmaValues.user_version = db.getPragma("user_version").toInt(); pragmaValues.wal_autocheckpoint = db.getPragma("wal_autocheckpoint").toInt(); updatePragmaUi(); } void MainWindow::updatePragmaUi() { ui->comboboxPragmaAutoVacuum->setCurrentIndex(pragmaValues.autovacuum); ui->checkboxPragmaAutomaticIndex->setChecked(pragmaValues.automatic_index); ui->checkboxPragmaCheckpointFullFsync->setChecked(pragmaValues.checkpoint_fullsync); ui->checkboxPragmaForeignKeys->setChecked(pragmaValues.foreign_keys); ui->checkboxPragmaFullFsync->setChecked(pragmaValues.fullfsync); ui->checkboxPragmaIgnoreCheckConstraints->setChecked(pragmaValues.ignore_check_constraints); ui->comboboxPragmaJournalMode->setCurrentIndex(ui->comboboxPragmaJournalMode->findText(pragmaValues.journal_mode, Qt::MatchFixedString)); ui->spinPragmaJournalSizeLimit->setValue(pragmaValues.journal_size_limit); ui->comboboxPragmaLockingMode->setCurrentIndex(ui->comboboxPragmaLockingMode->findText(pragmaValues.locking_mode, Qt::MatchFixedString)); ui->spinPragmaMaxPageCount->setValue(pragmaValues.max_page_count); ui->spinPragmaPageSize->setValue(pragmaValues.page_size); ui->checkboxPragmaRecursiveTriggers->setChecked(pragmaValues.recursive_triggers); ui->checkboxPragmaSecureDelete->setChecked(pragmaValues.secure_delete); ui->comboboxPragmaSynchronous->setCurrentIndex(pragmaValues.synchronous); ui->comboboxPragmaTempStore->setCurrentIndex(pragmaValues.temp_store); ui->spinPragmaUserVersion->setValue(pragmaValues.user_version); ui->spinPragmaWalAutoCheckpoint->setValue(pragmaValues.wal_autocheckpoint); } void MainWindow::savePragmas() { if( db.getDirty() ) { QString msg = tr("Setting PRAGMA values will commit your current transaction.\nAre you sure?"); if(QMessageBox::question(this, QApplication::applicationName(), msg, QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape) == QMessageBox::No) { return; // abort } } db.setPragma("auto_vacuum", ui->comboboxPragmaAutoVacuum->currentIndex(), pragmaValues.autovacuum); db.setPragma("automatic_index", ui->checkboxPragmaAutomaticIndex->isChecked(), pragmaValues.automatic_index); db.setPragma("checkpoint_fullfsync", ui->checkboxPragmaCheckpointFullFsync->isChecked(), pragmaValues.checkpoint_fullsync); db.setPragma("foreign_keys", ui->checkboxPragmaForeignKeys->isChecked(), pragmaValues.foreign_keys); db.setPragma("fullfsync", ui->checkboxPragmaFullFsync->isChecked(), pragmaValues.fullfsync); db.setPragma("ignore_check_constraints", ui->checkboxPragmaIgnoreCheckConstraints->isChecked(), pragmaValues.ignore_check_constraints); db.setPragma("journal_mode", ui->comboboxPragmaJournalMode->currentText().toUpper(), pragmaValues.journal_mode); db.setPragma("journal_size_limit", ui->spinPragmaJournalSizeLimit->value(), pragmaValues.journal_size_limit); db.setPragma("locking_mode", ui->comboboxPragmaLockingMode->currentText().toUpper(), pragmaValues.locking_mode); db.setPragma("max_page_count", ui->spinPragmaMaxPageCount->value(), pragmaValues.max_page_count); db.setPragma("page_size", ui->spinPragmaPageSize->value(), pragmaValues.page_size); db.setPragma("recursive_triggers", ui->checkboxPragmaRecursiveTriggers->isChecked(), pragmaValues.recursive_triggers); db.setPragma("secure_delete", ui->checkboxPragmaSecureDelete->isChecked(), pragmaValues.secure_delete); db.setPragma("synchronous", ui->comboboxPragmaSynchronous->currentIndex(), pragmaValues.synchronous); db.setPragma("temp_store", ui->comboboxPragmaTempStore->currentIndex(), pragmaValues.temp_store); db.setPragma("user_version", ui->spinPragmaUserVersion->value(), pragmaValues.user_version); db.setPragma("wal_autocheckpoint", ui->spinPragmaWalAutoCheckpoint->value(), pragmaValues.wal_autocheckpoint); updatePragmaUi(); } void MainWindow::logSql(const QString& sql, int msgtype) { if(msgtype == kLogMsg_User) { ui->editLogUser->append(sql); ui->editLogUser->verticalScrollBar()->setValue(ui->editLogUser->verticalScrollBar()->maximum()); } else { ui->editLogApplication->append(sql); ui->editLogApplication->verticalScrollBar()->setValue(ui->editLogApplication->verticalScrollBar()->maximum()); } }