Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
<!--
SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>

SPDX-License-Identifier: CC0-1.0
-->

<!--
2023 ppvan phuclaplace@gmail.com
-->
<h1 align="center">
<img
src="data/icons/hicolor/scalable/apps/me.ppvan.psequel.svg" alt="PSequel"
Expand All @@ -23,7 +32,7 @@
</p>


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.
Expand Down
27 changes: 27 additions & 0 deletions res/gtk/connection-form.blp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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";
}
}
}
4 changes: 4 additions & 0 deletions res/gtk/icons/text-sql-symbolic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 27 additions & 1 deletion res/gtk/query-view.blp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -259,4 +265,24 @@ template $PsequelQueryView : Adw.Bin {
}
}
}
}
}


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";
}
}
}
9 changes: 2 additions & 7 deletions res/gtk/table-data-view.blp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

}


Expand Down
1 change: 1 addition & 0 deletions res/psequel.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
<file preprocess="xml-stripblanks" alias="category-search-symbolic.svg">gtk/icons/category-search-symbolic.svg</file>
<file preprocess="xml-stripblanks" alias="left-large-symbolic.svg">gtk/icons/left-large-symbolic.svg</file>
<file preprocess="xml-stripblanks" alias="right-large-symbolic.svg">gtk/icons/right-large-symbolic.svg</file>
<file preprocess="xml-stripblanks" alias="text-sql-symbolic.svg">gtk/icons/text-sql-symbolic.svg</file>
</gresource>
</gresources>
36 changes: 36 additions & 0 deletions src/application.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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", {"<primary>q"});
}

public override void activate () {
base.activate ();
var win = this.active_window;
Expand Down Expand Up @@ -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");
}
}
}
11 changes: 3 additions & 8 deletions src/services/query_service.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);

Expand Down
116 changes: 19 additions & 97 deletions src/ui/query/table_data_view.vala
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ namespace Psequel {

private ObservableArrayList<Relation.Row> 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; }
Expand All @@ -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;
Expand All @@ -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";
}
Expand All @@ -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<int> ("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<int> ("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]
Expand Down Expand Up @@ -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;
Expand Down