-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathMainWindow.h
More file actions
222 lines (188 loc) · 8.72 KB
/
Copy pathMainWindow.h
File metadata and controls
222 lines (188 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#pragma once
#include <QAction>
#include <QLabel>
#include <QMainWindow>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSqlDatabase>
#include <QStackedWidget>
#include <QTableWidget>
#include <QThread>
#include <QTreeWidget>
#include <QVariant>
#include <QVBoxLayout>
#include <QVector>
#include "DiagramView.h"
#include "QueryModel.h"
#include "QueryWorker.h"
#include "Schema.h"
#include "SqlEditor.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow() override;
// Loads a database file, populates the table tree, and focuses the
// first table that has at least one relation (falls back to the
// first table if none do).
bool openDatabase(const QString& path);
signals:
// Crosses onto the query thread via a queued connection to kick off
// execute() there - see startQueryThread().
void requestQuery(const QString& sql);
private slots:
void onTableActivated(const QString& tableName);
void onTreeItemClicked(QTreeWidgetItem* item, int column);
// Query builder step management.
void onAddStepClicked();
void onRemoveJoinClicked(int index);
void onRemoveConditionClicked(int index);
// SQL mode / execution.
void onSqlModeToggled(bool checked);
void onExecuteClicked();
void onSqlTextChanged();
// Delivered from QueryWorker, running on m_queryThread.
void onQuerySucceeded(QStringList headers, ResultRows rows);
void onQueryFailed(QString message);
// Database menu.
void onNewDatabaseTriggered();
void onOpenDatabaseTriggered();
void onExportDatabaseTriggered();
void onImportDatabaseTriggered();
// Settings menu.
void onSettingsTriggered();
// Schema menu - each of these edits the live database directly (DDL
// runs synchronously on the GUI thread's m_db, since these are quick
// one-off statements, not the potentially-slow queries the worker
// thread is for) and then reloads the schema.
void onNewTableTriggered();
void onNewIndexTriggered();
void onDropTableTriggered();
void onDropIndexTriggered();
// Data menu / results grid editing.
void onInsertRowTriggered();
void onResultCellEdited(QTableWidgetItem* item);
void onExportResultsTriggered();
private:
void buildUi();
void buildMenus();
void applyDarkTheme();
void refreshQueryPanel();
void populateTableTree();
// Preferences (Settings dialog), persisted via QSettings.
// loadSettings() runs before buildUi()/applyDarkTheme() so the very
// first paint already reflects saved values.
void loadSettings();
void saveSettings();
// Updates m_defaultDialogDir to filePath's containing folder (and
// persists it) if it changed - called after every file dialog that
// returns a real path, so later dialogs default to wherever the
// person last saved/opened something.
void rememberDialogDir(const QString& filePath);
// Query builder "+ Add step" flows.
void addJoinStep();
void addConditionStep();
// Execution / results.
void startQueryThread(const QString& dbPath);
void stopQueryThread();
void runQuery(const QString& sql);
void showResults(const QStringList& headers, const ResultRows& rows);
void showError(const QString& message);
void showRunning();
// Schema editing (New/Drop Table/Index). Runs sql directly against
// m_db and reports failures via a message box; returns whether it
// succeeded so callers can decide whether to reload the schema.
bool execSchemaChange(const QString& sql, const QString& context);
// Convenience wrapper: execSchemaChange() + reload schema + refresh
// every schema-dependent part of the UI (tree, autocomplete, diagram).
void applySchemaChange(const QString& sql, const QString& context);
// The reload+refresh half of applySchemaChange(), factored out so
// onImportDatabaseTriggered() (which runs many statements at once,
// not a single one) can reuse it too.
void reloadSchemaAndFocus();
void setSchemaActionsEnabled(bool enabled);
// Refreshes everything that depends on m_schema but doesn't depend on
// which table is focused: the table tree and raw-SQL autocomplete.
void syncSchemaUi();
// The table a fresh view of the schema should default to: the first
// table with at least one relation, else just the first table, else
// empty if the schema has no tables at all.
QString defaultFocusTable() const;
// Re-centers the diagram on tableName and rebuilds the query model
// from scratch (FROM = tableName, JOIN = its first relation, if any).
// No-ops if tableName is already focused. Unlike onTableActivated(),
// this ignores SQL mode - it's for schema refreshes, not table clicks.
void focusTable(const QString& tableName);
void resetUiForNewDatabase();
// Case-insensitively resolves a table name as typed in raw SQL to its
// canonical name in the schema, or an empty string if it doesn't exist.
QString resolveTableName(const QString& rawName) const;
// Renders a single cell value for an exported INSERT statement:
// NULL, an unquoted number, or a quoted/escaped string.
QString quoteForExport(const QVariant& value) const;
// Writers behind "Data > Export Results…" / the results panel's
// Export button - each writes the full grid (headers + rows, as
// currently displayed) to path in one format. Return false (and
// leave any partial file behind) only if the file couldn't be
// opened for writing.
bool writeResultsAsDelimited(const QString& path, const QStringList& headers, const ResultRows& rows,
QChar delimiter);
bool writeResultsAsJson(const QString& path, const QStringList& headers, const ResultRows& rows);
// May prompt for a table name (see m_editableTable) - not const.
bool writeResultsAsSql(const QString& path, const QStringList& headers, const ResultRows& rows);
Schema m_schema;
QueryModel m_queryModel;
QString m_focusTable;
QSqlDatabase m_db;
// Preferences edited via the Settings dialog, loaded from/saved to
// QSettings. m_uiFontSize/m_editorFontSize feed applyDarkTheme()'s
// stylesheet; m_confirmDestructiveActions gates the Drop Table/Index
// confirmation prompts; m_defaultDialogDir seeds every Open/New/
// Export/Import file dialog and is kept up to date automatically.
int m_uiFontSize = 12;
int m_editorFontSize = 11;
bool m_confirmDestructiveActions = true;
QString m_defaultDialogDir;
// Query execution happens on this dedicated thread/worker so a slow
// query never blocks the UI. The worker has its own SQLite
// connection, independent of m_db above (which stays on the GUI
// thread for schema loading and autocomplete).
QThread* m_queryThread = nullptr;
QueryWorker* m_queryWorker = nullptr;
bool m_queryRunning = false;
// True while the right-hand panel shows the raw, user-editable SQL
// text box instead of the step-based builder view.
bool m_sqlModeActive = false;
QTreeWidget* m_tableTree = nullptr;
DiagramView* m_diagramView = nullptr;
// Top bar title, showing the currently open database's file name.
QLabel* m_titleLabel = nullptr;
// Actions that only make sense with a database open: New/Drop
// Table/Index, Insert Row, Export, Import. All disabled until then.
QVector<QAction*> m_schemaActions;
// Right panel: builder page (steps + read-only preview) vs raw SQL page.
QStackedWidget* m_rightStack = nullptr;
QVBoxLayout* m_stepsLayout = nullptr;
QPlainTextEdit* m_sqlPreview = nullptr; // read-only, mirrors the builder
SqlEditor* m_sqlEditor = nullptr; // editable, used in SQL mode, with autocomplete
QPushButton* m_sqlModeBtn = nullptr;
QPushButton* m_executeBtn = nullptr;
QPushButton* m_addStepBtn = nullptr;
// Bottom panel: query results.
QTableWidget* m_resultsTable = nullptr;
QLabel* m_statusLabel = nullptr;
// Exports the currently-displayed grid to CSV/TSV/JSON/SQL. Enabled
// only while the grid actually has columns (see showResults()).
QPushButton* m_exportResultsBtn = nullptr;
// Direct in-place editing of the results grid is only enabled when
// the last successful query was exactly "SELECT * FROM table" (no
// WHERE/JOIN) for a table that has a primary key - only then is it
// safe to map an edited cell back to a specific database row.
// Empty m_editableTable means the grid is read-only.
QString m_editableTable;
QStringList m_editablePkColumns;
// True while showResults() is populating cells, so those
// programmatic changes don't get mistaken for user edits.
bool m_populatingResults = false;
};