diff --git a/README.md b/README.md index 0a9e971..9fff76f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ + + +

PSequel -Small tool for quick text transformations such as checksums, encoding, decoding and so on. Written in Vala for GNOME desktop in the hope to be useful. +Small tool for quick sql query, specialized in PostgresSQL. Written in Vala for GNOME desktop in the hope to be useful. # Features - Load and save connections. diff --git a/res/gtk/connection-form.blp b/res/gtk/connection-form.blp index ca9997d..c38921c 100644 --- a/res/gtk/connection-form.blp +++ b/res/gtk/connection-form.blp @@ -13,6 +13,14 @@ template $PsequelConnectionForm : Gtk.Box { Label { label: ""; } + + // Dupplicate primary_menu in query-view.blp. + // Update both if you change something + [end] + MenuButton { + icon-name: "open-menu-symbolic"; + menu-model: primary_menu; + } } Adw.Clamp { @@ -214,4 +222,23 @@ template $PsequelConnectionForm : Gtk.Box { } } +} + +menu primary_menu { + section { + item { + label: _("_Preferences"); + action: "app.preferences"; + } + + item { + label: _("_Keyboard Shortcuts"); + // action: "win.show-help-overlay"; + } + + item { + label: _("_About"); + action: "app.about"; + } + } } \ No newline at end of file diff --git a/res/gtk/icons/text-sql-symbolic.svg b/res/gtk/icons/text-sql-symbolic.svg new file mode 100644 index 0000000..1075496 --- /dev/null +++ b/res/gtk/icons/text-sql-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/gtk/query-view.blp b/res/gtk/query-view.blp index 7b66d61..37a64de 100644 --- a/res/gtk/query-view.blp +++ b/res/gtk/query-view.blp @@ -223,6 +223,12 @@ template $PsequelQueryView : Adw.Bin { policy: wide; stack: stack; } + + [end] + MenuButton { + icon-name: "open-menu-symbolic"; + menu-model: primary_menu; + } } Adw.ViewStack stack { @@ -259,4 +265,24 @@ template $PsequelQueryView : Adw.Bin { } } } -} \ No newline at end of file +} + + +menu primary_menu { + section { + item { + label: _("_Preferences"); + action: "app.preferences"; + } + + item { + label: _("_Keyboard Shortcuts"); + // action: "win.show-help-overlay"; + } + + item { + label: _("_About"); + action: "app.about"; + } + } +} \ No newline at end of file diff --git a/res/gtk/table-data-view.blp b/res/gtk/table-data-view.blp index 18be54f..f7e3b7a 100644 --- a/res/gtk/table-data-view.blp +++ b/res/gtk/table-data-view.blp @@ -32,13 +32,8 @@ template $PsequelTableData : Gtk.Box { } } - ScrolledWindow { - ColumnView data_view { - show-row-separators: true; - show-column-separators: true; - styles ["data-table"] - vexpand: true; - } + $PsequelQueryResults query_results { + } diff --git a/res/psequel.gresource.xml b/res/psequel.gresource.xml index 4409a06..d6f9868 100644 --- a/res/psequel.gresource.xml +++ b/res/psequel.gresource.xml @@ -33,5 +33,6 @@ gtk/icons/category-search-symbolic.svg gtk/icons/left-large-symbolic.svg gtk/icons/right-large-symbolic.svg + gtk/icons/text-sql-symbolic.svg diff --git a/src/application.vala b/src/application.vala index 45f9c4a..35eecf8 100644 --- a/src/application.vala +++ b/src/application.vala @@ -30,6 +30,16 @@ namespace Psequel { Object (application_id: "me.ppvan.psequel", flags: ApplicationFlags.DEFAULT_FLAGS); } + construct { + ActionEntry[] action_entries = { + { "about", this.on_about_action }, + { "preferences", this.on_preferences_action }, + { "quit", this.quit } + }; + this.add_action_entries (action_entries, this); + this.set_accels_for_action ("app.quit", {"q"}); + } + public override void activate () { base.activate (); var win = this.active_window; @@ -102,5 +112,31 @@ namespace Psequel { typeof (Psequel.TableIndexInfo).ensure (); typeof (Psequel.TableFKInfo).ensure (); } + + private void on_about_action () { + string[] developers = { "ppvan" }; + + var about = new Adw.AboutWindow () { + transient_for = this.get_active_window (), + application_name = Config.APP_NAME, + application_icon = Config.APP_ID, + developer_name = "Phạm Văn Phúc", + version = Config.VERSION, + developers = developers, + copyright = "© 2023 ppvan", + license_type = Gtk.License.GPL_3_0_ONLY, + issue_url = "https://github.com/ppvan/psequel/issues", + + developers = { + "ppvan https://ppvan.me", + }, + }; + + about.present (); + } + + private void on_preferences_action () { + debug ("Setting"); + } } } \ No newline at end of file diff --git a/src/services/query_service.vala b/src/services/query_service.vala index 938c94d..5eb34a2 100644 --- a/src/services/query_service.vala +++ b/src/services/query_service.vala @@ -13,14 +13,9 @@ namespace Psequel { public async Relation select (string schema, string table_name, int offset = 0, int limit = 500, string where_clause = "") throws PsequelError { string escape_tbname = active_db.escape_identifier (table_name); - if (where_clause == "") { - string stmt = @"SELECT * FROM $escape_tbname LIMIT $limit OFFSET $offset"; - return yield exec_query (stmt); - } else { - string stmt = @"SELECT * FROM $escape_tbname WHERE $where_clause LIMIT $limit OFFSET $offset"; - return yield exec_query (stmt); - } + string stmt = @"SELECT * FROM $escape_tbname $where_clause LIMIT $limit OFFSET $offset"; + return yield exec_query (stmt); } public async string db_version () throws PsequelError { @@ -57,7 +52,7 @@ namespace Psequel { } public async Relation exec_query (string query, out int64 exec_ms = null) throws PsequelError { - + int64 begin = GLib.get_real_time (); var result = yield exec_query_internal (query); diff --git a/src/ui/query/table_data_view.vala b/src/ui/query/table_data_view.vala index 3b9b05b..69d7604 100644 --- a/src/ui/query/table_data_view.vala +++ b/src/ui/query/table_data_view.vala @@ -9,11 +9,6 @@ namespace Psequel { private ObservableArrayList model; - private Gtk.SortListModel sort_model; - private Gtk.SelectionModel selection_model; - - public int64 acc = 0; - public int query_limit { get; set; } public Schema schema { get; set; } @@ -32,51 +27,29 @@ namespace Psequel { var setting = ResourceManager.instance ().settings; setting.bind ("query-limit", this, "query-limit", SettingsBindFlags.DEFAULT); connect_signal (); - alloc_columns (); } public async void load_data (string schema, string table_name, int page = 0, string where = "") { - Idle.add (load_data.callback, Priority.DEFAULT_IDLE); - yield; - - var columns = data_view.columns; - uint n = columns.get_n_items (); - int offset = page * query_limit; + var offset = current_page * query_limit; try { - var relation = yield query_service.select (schema, table_name, offset, query_limit, where); - - debug ("Begin add rows to views"); - for (int i = 0; i < n; i++) { - var col = columns.get_item (i) as Gtk.ColumnViewColumn; - if (i >= relation.cols) { - col.set_visible (false); - continue; - } - auto_set_sorter (col, relation.get_column_type (i), i); - col.set_title (relation.get_header (i)); - col.set_visible (true); - } + query_results.show_loading (); - this.selection_model.unselect_all (); - model.clear (); + var relation = yield query_service.select (schema, table_name, offset, query_limit, where); - model.batch_add (relation.iterator ()); + query_results.show_result (relation); + update_status_label (relation); + update_pagination_btn (relation); } catch (PsequelError err) { - create_dialog ("Query Fail", err.message).present (); + query_results.show_error (err); } - - this.sort_model.set_sorter (data_view.get_sorter ()); - debug ("Load %u records from %s", model.get_n_items (), table_name); - update_status_label (); - update_pagination_btn (); } - private void update_pagination_btn () { - if (model.get_n_items () < (uint) query_limit) { + private void update_pagination_btn (Relation relation) { + if (relation.rows < query_limit) { right_page.sensitive = false; } else { right_page.sensitive = true; @@ -89,9 +62,9 @@ namespace Psequel { } } - private void update_status_label () { + private void update_status_label (Relation relation) { uint begin = query_limit * current_page + 1; - uint end = begin + model.get_n_items () - 1; + uint end = begin + relation.rows - 1; status_label.label = @"Rows $begin - $end"; } @@ -114,67 +87,16 @@ namespace Psequel { }); } - private void alloc_columns () { - for (int i = 0; i < ResourceManager.MAX_COLUMNS; i++) { - var factory = new Gtk.SignalListItemFactory (); - factory.set_data ("index", i); - - factory.setup.connect ((_fact, _item) => { - var label = new Gtk.Label (null); - label.halign = Gtk.Align.START; - label.margin_start = 8; - _item.child = label; - }); - - factory.bind.connect ((_fact, _item) => { - var row = _item.item as Relation.Row; - var label = _item.child as Gtk.Label; - int index = _fact.get_data ("index"); - label.label = row[index]; - }); - - Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn ("", factory); - column.set_expand (true); - // column.fixed_width = 200; - column.set_visible (false); - - data_view.append_column (column); - } - - this.sort_model = new Gtk.SortListModel (model, null); - this.sort_model.incremental = true; - - this.selection_model = new Gtk.SingleSelection (sort_model); - - data_view.set_model (this.selection_model); - } - - private void auto_set_sorter (Gtk.ColumnViewColumn col, Type type, int col_index) { - switch (type) { - case Type.BOOLEAN, Type.INT64, Type.FLOAT, Type.DOUBLE: - var constexprs = new Gtk.ConstantExpression (Type.INT, col_index); - var expresion = new Gtk.CClosureExpression (Type.INT64, null, { constexprs }, (Callback) get_col_by_index_int, null, null); - - var sorter = new Gtk.NumericSorter (expresion); - - col.set_sorter (sorter); - break; - - default: - var constexprs = new Gtk.ConstantExpression (Type.INT, col_index); - var expresion = new Gtk.CClosureExpression (Type.STRING, null, { constexprs }, (Callback) get_col_by_index, null, null); - - var sorter = new Gtk.StringSorter (expresion); - - col.set_sorter (sorter); - break; - } - } - [GtkCallback] private void filter_query (Gtk.Button btn) { var where_clause = filter_entry.get_text (); - load_data.begin (schema.name, tbname, current_page, where_clause); + + if (!where_clause.up ().has_prefix ("WHERE")) { + var dialog = create_dialog ("Filter Failed", "WHERE clause should starts with 'WHERE'"); + dialog.present (); + } else { + load_data.begin (schema.name, tbname, current_page, where_clause); + } } [GtkCallback] @@ -202,7 +124,7 @@ namespace Psequel { } [GtkChild] - private unowned Gtk.ColumnView data_view; + private unowned QueryResults query_results; [GtkChild] private unowned Gtk.Entry filter_entry;