From 2c91d92909a01f5f980a84168dda239389a603a1 Mon Sep 17 00:00:00 2001 From: ppvan Date: Thu, 20 Jul 2023 05:37:06 +0700 Subject: [PATCH 01/12] Table data UI --- res/gtk/icons/left-large-symbolic.svg | 2 + res/gtk/icons/right-large-symbolic.svg | 2 + res/gtk/query-view.blp | 30 +++++------ res/gtk/table-data.blp | 75 ++++++++++++++++++++++++++ res/meson.build | 1 + res/psequel.gresource.xml | 3 ++ src/application.vala | 1 + src/meson.build | 1 + src/services/query_service.vala | 1 - src/ui/table_data.vala | 7 +++ 10 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 res/gtk/icons/left-large-symbolic.svg create mode 100644 res/gtk/icons/right-large-symbolic.svg create mode 100644 src/ui/table_data.vala diff --git a/res/gtk/icons/left-large-symbolic.svg b/res/gtk/icons/left-large-symbolic.svg new file mode 100644 index 0000000..7061d6d --- /dev/null +++ b/res/gtk/icons/left-large-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/res/gtk/icons/right-large-symbolic.svg b/res/gtk/icons/right-large-symbolic.svg new file mode 100644 index 0000000..c8d83ea --- /dev/null +++ b/res/gtk/icons/right-large-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/res/gtk/query-view.blp b/res/gtk/query-view.blp index 82b461b..fc5cc87 100644 --- a/res/gtk/query-view.blp +++ b/res/gtk/query-view.blp @@ -175,18 +175,13 @@ template $PsequelQueryView : Adw.Bin { Box { spacing: 4; - margin-bottom: 4; + margin-bottom: 8; margin-end: 8; - Button logout { + Button { styles ["flat"] - icon-name: "step-out-symbolic"; - tooltip-text: "Logout"; - clicked => $on_logout_clicked(); - } - - Label { - hexpand: true; + icon-name: "plus-large-symbolic"; + tooltip-text: "Create new table"; } Button reload { @@ -197,12 +192,17 @@ template $PsequelQueryView : Adw.Bin { clicked => $on_reload_clicked(); } - Button { + Label { + hexpand: true; + } + + Button logout { styles ["flat"] - icon-name: "plus-large-symbolic"; - tooltip-text: "Create new table"; + icon-name: "step-out-symbolic"; + tooltip-text: "Logout"; + clicked => $on_logout_clicked(); } - + } } @@ -235,8 +235,8 @@ template $PsequelQueryView : Adw.Bin { name: "Data"; title: "Data"; icon-name: "object-rows-symbolic"; - child: Label { - label: "Data"; + child: $PsequelTableData { + }; } diff --git a/res/gtk/table-data.blp b/res/gtk/table-data.blp index e69de29..ba8c2e6 100644 --- a/res/gtk/table-data.blp +++ b/res/gtk/table-data.blp @@ -0,0 +1,75 @@ +using Gtk 4.0; + +using Adw 1; + +template $PsequelTableData : Gtk.Box { + + width-request: 900; + height-request: 600; + + orientation: vertical; + spacing: 4; + margin-start: 8; + margin-top: 8; + margin-end: 8; + margin-bottom: 8; + + Box { + spacing: 12; + margin-top: 8; + margin-bottom: 8; + Entry { + hexpand: true; + placeholder-text: "WHERE clause"; + } + + Button { + styles ["suggested-action"] + label: "Filter"; + + } + } + + ColumnView { + vexpand: true; + } + + + Box { + spacing: 8; + // height-request: 40; + Button { + styles ["flat"] + icon-name: "plus-large-symbolic"; + tooltip-text: "Insert Row"; + } + + Button { + styles ["flat"] + icon-name: "refresh-large-symbolic"; + tooltip-text: "Reload Data"; + } + + Separator {} + + Label { + label: "Rows 1 - 500"; + hexpand: true; + halign: start; + } + + Separator {} + + Button { + styles ["flat"] + icon-name: "left-large-symbolic"; + tooltip-text: "Last Page"; + } + + Button { + styles ["flat"] + icon-name: "right-large-symbolic"; + tooltip-text: "Next Page"; + } + } +} \ No newline at end of file diff --git a/res/meson.build b/res/meson.build index 69d0116..044847d 100644 --- a/res/meson.build +++ b/res/meson.build @@ -12,6 +12,7 @@ blueprints = custom_target('blueprints', 'gtk/connection-form.blp', 'gtk/query-view.blp', 'gtk/table-structure.blp', + 'gtk/table-data.blp', ), build_by_default: true, # This does not run if you use an dot (.) diff --git a/res/psequel.gresource.xml b/res/psequel.gresource.xml index 55ba3bd..907438a 100644 --- a/res/psequel.gresource.xml +++ b/res/psequel.gresource.xml @@ -5,6 +5,7 @@ gtk/welcome.ui gtk/query-view.ui gtk/table-structure.ui + gtk/table-data.ui gtk/recent-connection.ui gtk/connection-form.ui gtk/window.ui @@ -23,5 +24,7 @@ gtk/icons/step-out-symbolic.svg gtk/icons/arrow-into-box-symbolic.svg gtk/icons/category-search-symbolic.svg + gtk/icons/left-large-symbolic.svg + gtk/icons/right-large-symbolic.svg diff --git a/src/application.vala b/src/application.vala index 2d12dde..adb08b9 100644 --- a/src/application.vala +++ b/src/application.vala @@ -94,6 +94,7 @@ namespace Psequel { typeof (Psequel.ConnectionForm).ensure (); typeof (Psequel.QueryView).ensure (); typeof (Psequel.TableStructure).ensure (); + typeof (Psequel.TableData).ensure (); } } } \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 381c916..03da880 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,6 +7,7 @@ psequel_sources = [ 'ui/window.vala', 'ui/query_view.vala', 'ui/table_structure.vala', + 'ui/table_data.vala', 'models/connection.vala', 'models/schema.vala', 'models/table.vala', diff --git a/src/services/query_service.vala b/src/services/query_service.vala index febefc8..dd58144 100644 --- a/src/services/query_service.vala +++ b/src/services/query_service.vala @@ -64,7 +64,6 @@ namespace Psequel { debug ("Regex not match: %s", fk_def); assert_not_reached (); } - debug ("%s", new_row.to_string ()); return new_row; }); diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala new file mode 100644 index 0000000..3baf771 --- /dev/null +++ b/src/ui/table_data.vala @@ -0,0 +1,7 @@ +namespace Psequel { + + [GtkTemplate (ui = "/me/ppvan/psequel/gtk/table-data.ui")] + public class TableData : Gtk.Box { + + } +} \ No newline at end of file From 7279cd729201556d67c1e7c3c2a0fc8e07db7994 Mon Sep 17 00:00:00 2001 From: ppvan Date: Thu, 20 Jul 2023 13:28:50 +0700 Subject: [PATCH 02/12] Load data to view --- res/gtk/table-data.blp | 9 +++- src/models/table.vala | 13 ++++- src/services/query_service.vala | 6 +++ src/ui/table_data.vala | 87 +++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/res/gtk/table-data.blp b/res/gtk/table-data.blp index ba8c2e6..5ff46d4 100644 --- a/res/gtk/table-data.blp +++ b/res/gtk/table-data.blp @@ -30,8 +30,13 @@ template $PsequelTableData : Gtk.Box { } } - ColumnView { - vexpand: true; + ScrolledWindow { + ColumnView data_view { + show-row-separators: true; + show-column-separators: true; + styles ["data-table"] + vexpand: true; + } } diff --git a/src/models/table.vala b/src/models/table.vala index 654476e..9aa73bf 100644 --- a/src/models/table.vala +++ b/src/models/table.vala @@ -58,6 +58,14 @@ namespace Psequel { return new Relation.from_data (new_headers, new_rows); } + public string get_header (int index) { + if (index >= cols) { + return ""; + } + + return headers.get (index); + } + public string to_string () { return @"Table ($rows x $cols)"; } @@ -104,7 +112,10 @@ namespace Psequel { data.remove_at (index); } - public new string @get (int index) { + public new string? @get (int index) { + if (index >= size) { + return null; + } return data.get (index); } diff --git a/src/services/query_service.vala b/src/services/query_service.vala index dd58144..d0a7b37 100644 --- a/src/services/query_service.vala +++ b/src/services/query_service.vala @@ -23,6 +23,12 @@ namespace Psequel { return res; } + public async Relation select (string schema, string table_name, int limit = 500, string where_clause = "") throws PsequelError { + string stmt = @"SELECT * FROM $schema.$table_name $where_clause LIMIT $limit"; + + return yield exec_query (stmt); + } + public async Relation db_table_fk (string schema, string table_name) throws PsequelError { string stmt = """ SELECT conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index 3baf771..1748f68 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -1,7 +1,94 @@ +using Gee; namespace Psequel { [GtkTemplate (ui = "/me/ppvan/psequel/gtk/table-data.ui")] public class TableData : Gtk.Box { + private AppSignals signals; + private QueryService query_service; + + private ObservableArrayList model; + + private ArrayList backup; + + public TableData () { + Object (); + } + + construct { + query_service = ResourceManager.instance ().query_service; + signals = ResourceManager.instance ().signals; + + model = new ObservableArrayList (); + backup = new ArrayList (); + + signals.table_selected_changed.connect ((schema, tbname) => { + load_data.begin (schema, tbname); + }); + + int created = 0; + int binded = 0; + + for (int i = 0; i < 20; 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; + created++; + + debug ("Create %d", created); + }); + + 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]; + binded++; + + debug ("Bind %d", binded); + }); + + factory.unbind.connect ((_fact, _item) => { + binded--; + }); + + + factory.teardown.connect ((_fact, _item) => { + created--; + }); + + Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn ("", factory); + column.set_expand (true); + data_view.append_column (column); + + var selection_model = new Gtk.SingleSelection (model); + data_view.set_model (selection_model); + } + } + + + public void table_double_clicked () { + debug ("Activated"); + } + + public async void load_data (string schema, string table_name) { + Relation relation = yield query_service.select (schema, table_name, 1000); + + debug (relation.to_string ()); + + model.clear (); + foreach (var item in relation) { + model.add (item); + } + } + + [GtkChild] + private unowned Gtk.ColumnView data_view; + } } \ No newline at end of file From ce82b5599839ea3e271cfacb85229612b7e9fa3f Mon Sep 17 00:00:00 2001 From: ppvan Date: Thu, 20 Jul 2023 15:04:24 +0700 Subject: [PATCH 03/12] Optimize performance --- res/gtk/query-view.blp | 2 ++ src/models/utils.vala | 21 +++++++++++++++++++++ src/services/signal.vala | 1 + src/ui/query_view.vala | 16 ++++++++++++++-- src/ui/table_data.vala | 39 +++++++++++++++++++++++---------------- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/res/gtk/query-view.blp b/res/gtk/query-view.blp index fc5cc87..6c36715 100644 --- a/res/gtk/query-view.blp +++ b/res/gtk/query-view.blp @@ -108,6 +108,8 @@ template $PsequelQueryView : Adw.Bin { ListBox table_list { styles ["navigation-sidebar"] selected-rows-changed => $table_selected(); + row-activated => $table_activated(); + activate-on-single-click: false; } } diff --git a/src/models/utils.vala b/src/models/utils.vala index 6ab2196..e22e01e 100644 --- a/src/models/utils.vala +++ b/src/models/utils.vala @@ -4,6 +4,27 @@ namespace Psequel { public delegate void Job (); + /* Model specialize for storeing query result. */ + public class BinddingArray: ListModel, Object { + private Array _data; + + public BinddingArray (int capacity) { + _data = new Array.sized (true, false, sizeof (string), capacity); + } + + public GLib.Object? get_item (uint position) { + return (GLib.Object)_data.index (position); + } + + public GLib.Type get_item_type () { + return Type.STRING; + } + + public uint get_n_items () { + return _data.length; + } + } + public class ObservableArrayList: ListModel, Object { private ArrayList _data; diff --git a/src/services/signal.vala b/src/services/signal.vala index f6f7a92..82ee878 100644 --- a/src/services/signal.vala +++ b/src/services/signal.vala @@ -13,6 +13,7 @@ namespace Psequel { public signal void table_selected_changed (string schema, string table); + public signal void table_activated (string schema, string table); public signal void database_connected (); /** diff --git a/src/ui/query_view.vala b/src/ui/query_view.vala index eea0a7e..5a3728a 100644 --- a/src/ui/query_view.vala +++ b/src/ui/query_view.vala @@ -85,7 +85,6 @@ namespace Psequel { signals.views_list_changed (); } - [GtkCallback] private void table_selected () { var row = table_list.get_selected_row (); @@ -102,6 +101,19 @@ namespace Psequel { signals.table_selected_changed (cur_schema.string, label.get_label ()); } } + + [GtkCallback] + private void table_activated (Gtk.ListBoxRow row) { + + var cur_schema = schema.get_selected_item () as Gtk.StringObject; + assert_nonnull (cur_schema); + + var label = row.child.get_last_child () as Gtk.Label; + + debug ("Emit table_activated"); + signals.table_activated (cur_schema.string, label.get_label ()); + } + /** * Filter table name base on seach entry. */ @@ -118,7 +130,7 @@ namespace Psequel { /** * Filter table name base on seach entry. */ - private bool view_filter_func (Object item) { + private bool view_filter_func (Object item) { assert (item is Relation.Row); var row = item as Relation.Row; diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index 1748f68..5c87912 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -22,14 +22,14 @@ namespace Psequel { model = new ObservableArrayList (); backup = new ArrayList (); - signals.table_selected_changed.connect ((schema, tbname) => { + signals.table_activated.connect ((schema, tbname) => { load_data.begin (schema, tbname); }); int created = 0; int binded = 0; - for (int i = 0; i < 20; i++) { + for (int i = 0; i < 13; i++) { var factory = new Gtk.SignalListItemFactory (); factory.set_data ("index", i); @@ -39,8 +39,6 @@ namespace Psequel { label.margin_start = 8; _item.child = label; created++; - - debug ("Create %d", created); }); factory.bind.connect ((_fact, _item) => { @@ -49,21 +47,11 @@ namespace Psequel { int index = _fact.get_data ("index"); label.label = row[index]; binded++; - - debug ("Bind %d", binded); - }); - - factory.unbind.connect ((_fact, _item) => { - binded--; - }); - - - factory.teardown.connect ((_fact, _item) => { - created--; }); Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn ("", factory); column.set_expand (true); + col.set_visible (false); data_view.append_column (column); var selection_model = new Gtk.SingleSelection (model); @@ -72,19 +60,38 @@ namespace Psequel { } + public void table_double_clicked () { debug ("Activated"); } public async void load_data (string schema, string table_name) { - Relation relation = yield query_service.select (schema, table_name, 1000); + Relation relation = yield query_service.select (schema, table_name, 500); + // Show error model. debug (relation.to_string ()); + var columns = data_view.columns; + uint n = data_view.columns.get_n_items (); + for (uint i = 0; i < n; i++) { + var col = columns.get_item (i) as Gtk.ColumnViewColumn; + if (i >= relation.cols) { + col.set_visible (false); + continue; + } + + + col.set_title (relation.get_header ((int)i)); + col.set_visible (true); + } + + TimePerf.begin (); model.clear (); foreach (var item in relation) { model.add (item); } + + TimePerf.end (); } [GtkChild] From d84baebe37263af43cfe724bec0c0d7baaf970c5 Mon Sep 17 00:00:00 2001 From: ppvan Date: Thu, 20 Jul 2023 15:04:53 +0700 Subject: [PATCH 04/12] hotfix --- src/ui/table_data.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index 5c87912..54c08a9 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -51,7 +51,7 @@ namespace Psequel { Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn ("", factory); column.set_expand (true); - col.set_visible (false); + column.set_visible (false); data_view.append_column (column); var selection_model = new Gtk.SingleSelection (model); From 8167bbcd16b9ba92d9be632c71173f7f6549217d Mon Sep 17 00:00:00 2001 From: ppvan Date: Thu, 20 Jul 2023 17:35:27 +0700 Subject: [PATCH 05/12] refactor and some ui improvement --- res/gtk/query-view.blp | 11 ++++---- src/application.vala | 2 +- src/models/utils.vala | 23 +++++++++++++++ src/services/query_service.vala | 5 +++- src/ui/connection_form.vala | 2 +- src/ui/query_view.vala | 20 +++++++++---- src/ui/table_data.vala | 50 ++++++++++++++++++--------------- 7 files changed, 77 insertions(+), 36 deletions(-) diff --git a/res/gtk/query-view.blp b/res/gtk/query-view.blp index 6c36715..9b0a908 100644 --- a/res/gtk/query-view.blp +++ b/res/gtk/query-view.blp @@ -37,8 +37,7 @@ template $PsequelQueryView : Adw.Bin { } - - Separator {} + Adw.ViewSwitcher tables_views_switcher { @@ -46,12 +45,14 @@ template $PsequelQueryView : Adw.Bin { height-request: 40; margin-start: 8; margin-end: 8; - margin-top: 8; - // margin-bottom: 8; + margin-top: 4; + margin-bottom: 8; policy: wide; stack: sql_views; } + Separator {} + Adw.ViewStack sql_views { hexpand: true; vexpand: true; @@ -234,7 +235,7 @@ template $PsequelQueryView : Adw.Bin { } Adw.ViewStackPage { - name: "Data"; + name: "data-view"; title: "Data"; icon-name: "object-rows-symbolic"; child: $PsequelTableData { diff --git a/src/application.vala b/src/application.vala index adb08b9..722aca2 100644 --- a/src/application.vala +++ b/src/application.vala @@ -51,7 +51,7 @@ namespace Psequel { try { background = new ThreadPool.with_owned_data ((worker) => { - worker.task (); + worker.run (); }, ResourceManager.POOL_SIZE, false); } catch (ThreadError err) { debug (err.message); diff --git a/src/models/utils.vala b/src/models/utils.vala index e22e01e..7650b1b 100644 --- a/src/models/utils.vala +++ b/src/models/utils.vala @@ -4,6 +4,16 @@ namespace Psequel { public delegate void Job (); + public Adw.MessageDialog create_dialog (string heading, string body) { + var window = ResourceManager.instance ().app.active_window; + var dialog = new Adw.MessageDialog (window, heading, body); + + dialog.close_response = "okay"; + dialog.add_response ("okay", "OK"); + + return dialog; + } + /* Model specialize for storeing query result. */ public class BinddingArray: ListModel, Object { private Array _data; @@ -85,6 +95,19 @@ namespace Psequel { this.thread_name = name; this.task = (owned)task; } + + public void run () { + + // Thread.usleep ((ulong)1e6); + this.task (); + } + } + + namespace Views { + public const string CONNECTION = "connection-view"; + public const string QUERY = "query-view"; + public const string TABLE_STRUCTURE = "structure-view"; + public const string TABLE_DATA = "data-view"; } public errordomain PsequelError { diff --git a/src/services/query_service.vala b/src/services/query_service.vala index d0a7b37..66b381c 100644 --- a/src/services/query_service.vala +++ b/src/services/query_service.vala @@ -16,7 +16,10 @@ namespace Psequel { } public async Relation db_schemas () throws PsequelError { - var stmt = "select schema_name from information_schema.schemata;"; + var stmt = """ + SELECT schema_name FROM information_schema.schemata + WHERE schema_name NOT IN ('pg_catalog', 'information_schema'); + """; var res = yield exec_query (stmt); diff --git a/src/ui/connection_form.vala b/src/ui/connection_form.vala index 89d279d..530691b 100644 --- a/src/ui/connection_form.vala +++ b/src/ui/connection_form.vala @@ -77,7 +77,7 @@ namespace Psequel { signals.database_connected (); var window = (Window) ResourceManager.instance ().app.get_active_window (); - window.navigate_to ("query-view"); + window.navigate_to (Views.QUERY); } catch (PsequelError err) { var dialog = create_err_dialog ("Connection error", err.message); dialog.present (); diff --git a/src/ui/query_view.vala b/src/ui/query_view.vala index 5a3728a..a97769e 100644 --- a/src/ui/query_view.vala +++ b/src/ui/query_view.vala @@ -95,10 +95,10 @@ namespace Psequel { var cur_schema = schema.get_selected_item () as Gtk.StringObject; assert_nonnull (cur_schema); - var label = row.child.get_last_child () as Gtk.Label; + var tbname = table_names.get_item (row.get_index ()) as Relation.Row; debug ("Emit table_selected_changed"); - signals.table_selected_changed (cur_schema.string, label.get_label ()); + signals.table_selected_changed (cur_schema.string, tbname[0]); } } @@ -108,10 +108,10 @@ namespace Psequel { var cur_schema = schema.get_selected_item () as Gtk.StringObject; assert_nonnull (cur_schema); - var label = row.child.get_last_child () as Gtk.Label; + var tbname = table_names.get_item (row.get_index ()) as Relation.Row; debug ("Emit table_activated"); - signals.table_activated (cur_schema.string, label.get_label ()); + signals.table_activated (cur_schema.string, tbname[0]); } /** @@ -158,7 +158,7 @@ namespace Psequel { schema_model.append (item[0]); } - debug ("Schema loaded."); + debug ("Schema reloaded."); } /** @@ -210,6 +210,7 @@ namespace Psequel { box.append (label); row.child = box; + row.tooltip_text = "Double click to load data"; return row; } @@ -269,6 +270,12 @@ namespace Psequel { reload_schema.begin (); }); + signals.table_activated.connect (() => { + debug ("handle table_activated"); + + stack.set_visible_child_name (Views.TABLE_DATA); + }); + schema.notify["selected"].connect (schema_changed); } @@ -286,5 +293,8 @@ namespace Psequel { [GtkChild] private unowned Gtk.DropDown schema; + + [GtkChild] + private unowned Adw.ViewStack stack; } } \ No newline at end of file diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index 54c08a9..7475f9d 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -66,36 +66,40 @@ namespace Psequel { } public async void load_data (string schema, string table_name) { - Relation relation = yield query_service.select (schema, table_name, 500); - - // Show error model. - debug (relation.to_string ()); - - var columns = data_view.columns; - uint n = data_view.columns.get_n_items (); - for (uint i = 0; i < n; i++) { - var col = columns.get_item (i) as Gtk.ColumnViewColumn; - if (i >= relation.cols) { - col.set_visible (false); - continue; - } + try { + Relation relation = yield query_service.select (schema, table_name, 500); - col.set_title (relation.get_header ((int)i)); - col.set_visible (true); - } + // Show error model. + debug (relation.to_string ()); - TimePerf.begin (); - model.clear (); - foreach (var item in relation) { - model.add (item); - } + var columns = data_view.columns; + uint n = data_view.columns.get_n_items (); + for (uint i = 0; i < n; i++) { + var col = columns.get_item (i) as Gtk.ColumnViewColumn; + if (i >= relation.cols) { + col.set_visible (false); + continue; + } + + + col.set_title (relation.get_header ((int) i)); + col.set_visible (true); + } + + TimePerf.begin (); + model.clear (); + foreach (var item in relation) { + model.add (item); + } - TimePerf.end (); + TimePerf.end (); + } catch (PsequelError.QUERY_FAIL err) { + create_dialog ("Query Fail", err.message).present (); + } } [GtkChild] private unowned Gtk.ColumnView data_view; - } } \ No newline at end of file From 70478bf6e403bc6cbd04b0d0e2825aa13c7bde55 Mon Sep 17 00:00:00 2001 From: ppvan Date: Thu, 20 Jul 2023 18:07:38 +0700 Subject: [PATCH 06/12] implement view load data --- res/gtk/query-view.blp | 3 ++- src/services/resource_manager.vala | 1 + src/services/signal.vala | 1 + src/ui/query_view.vala | 12 ++++++++++++ src/ui/table_data.vala | 8 ++++++-- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/res/gtk/query-view.blp b/res/gtk/query-view.blp index 9b0a908..abb7943 100644 --- a/res/gtk/query-view.blp +++ b/res/gtk/query-view.blp @@ -144,7 +144,7 @@ template $PsequelQueryView : Adw.Bin { ToggleButton search_views_btn { styles ["flat"] - tooltip-text: "Search tables"; + tooltip-text: "Search views"; icon-name: "loupe-large-symbolic"; toggled => $on_show_view_search(); @@ -170,6 +170,7 @@ template $PsequelQueryView : Adw.Bin { ListBox views_list { styles ["navigation-sidebar"] selected-rows-changed => $view_selected(); + row-activated => $view_activated(); } } }; diff --git a/src/services/resource_manager.vala b/src/services/resource_manager.vala index 1d2086c..84d2833 100644 --- a/src/services/resource_manager.vala +++ b/src/services/resource_manager.vala @@ -21,6 +21,7 @@ namespace Psequel { // Should not be > 1 because libpq can't create many query in 1 connection. public const int POOL_SIZE = 1; + public const int MAX_COLUMNS = 100; public ThreadPool background; /** diff --git a/src/services/signal.vala b/src/services/signal.vala index 82ee878..c8517bd 100644 --- a/src/services/signal.vala +++ b/src/services/signal.vala @@ -14,6 +14,7 @@ namespace Psequel { public signal void table_selected_changed (string schema, string table); public signal void table_activated (string schema, string table); + public signal void view_activated (string schema, string view); public signal void database_connected (); /** diff --git a/src/ui/query_view.vala b/src/ui/query_view.vala index a97769e..66f9f6d 100644 --- a/src/ui/query_view.vala +++ b/src/ui/query_view.vala @@ -114,6 +114,18 @@ namespace Psequel { signals.table_activated (cur_schema.string, tbname[0]); } + [GtkCallback] + private void view_activated (Gtk.ListBoxRow row) { + + var cur_schema = schema.get_selected_item () as Gtk.StringObject; + assert_nonnull (cur_schema); + + + var vname = views_names.get_item (row.get_index ()) as Relation.Row; + debug ("Emit view_activated"); + signals.view_activated (cur_schema.string, vname[0]); + } + /** * Filter table name base on seach entry. */ diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index 7475f9d..fc74e6f 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -22,14 +22,18 @@ namespace Psequel { model = new ObservableArrayList (); backup = new ArrayList (); - signals.table_activated.connect ((schema, tbname) => { + signals.table_activated.connect_after ((schema, tbname) => { load_data.begin (schema, tbname); }); + signals.view_activated.connect_after ((schema, vname) => { + load_data.begin (schema, vname); + }); + int created = 0; int binded = 0; - for (int i = 0; i < 13; i++) { + for (int i = 0; i < ResourceManager.MAX_COLUMNS; i++) { var factory = new Gtk.SignalListItemFactory (); factory.set_data ("index", i); From 93f81f16788ca65a0931eed65374a3ccc77535e3 Mon Sep 17 00:00:00 2001 From: ppvan Date: Fri, 21 Jul 2023 10:37:22 +0700 Subject: [PATCH 07/12] minor chages to UI and code --- res/gtk/table-structure.blp | 4 ++++ src/services/resource_manager.vala | 1 + src/ui/query_view.vala | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/res/gtk/table-structure.blp b/res/gtk/table-structure.blp index 896f8d0..e507b24 100644 --- a/res/gtk/table-structure.blp +++ b/res/gtk/table-structure.blp @@ -20,6 +20,8 @@ template $PsequelTableStructure : Gtk.Box { } ScrolledWindow { ColumnView columns { + + styles ["data-table"] show-column-separators: true; show-row-separators: true; vexpand: true; @@ -35,6 +37,7 @@ template $PsequelTableStructure : Gtk.Box { ScrolledWindow { ColumnView indexes { + styles ["data-table"] show-column-separators: true; show-row-separators: true; vexpand: true; @@ -50,6 +53,7 @@ template $PsequelTableStructure : Gtk.Box { ScrolledWindow { ColumnView foreign_key { + styles ["data-table"] show-column-separators: true; show-row-separators: true; vexpand: true; diff --git a/src/services/resource_manager.vala b/src/services/resource_manager.vala index 84d2833..a811c13 100644 --- a/src/services/resource_manager.vala +++ b/src/services/resource_manager.vala @@ -83,6 +83,7 @@ namespace Psequel { }); } catch (Error err) { debug (err.message); + recent_connections.clear (); } debug ("User setting loaded"); diff --git a/src/ui/query_view.vala b/src/ui/query_view.vala index 66f9f6d..30a07be 100644 --- a/src/ui/query_view.vala +++ b/src/ui/query_view.vala @@ -288,6 +288,12 @@ namespace Psequel { stack.set_visible_child_name (Views.TABLE_DATA); }); + signals.view_activated.connect (() => { + debug ("handle table_activated"); + + stack.set_visible_child_name (Views.TABLE_DATA); + }); + schema.notify["selected"].connect (schema_changed); } From ad5c2a7fd12f2f7896a4442c9547c60ad2245d98 Mon Sep 17 00:00:00 2001 From: ppvan Date: Sat, 22 Jul 2023 10:48:52 +0700 Subject: [PATCH 08/12] defeat the sorter --- src/models/table.vala | 3 +++ src/ui/query_view.vala | 2 +- src/ui/table_data.vala | 40 +++++++++++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/models/table.vala b/src/models/table.vala index 9aa73bf..c169292 100644 --- a/src/models/table.vala +++ b/src/models/table.vala @@ -40,6 +40,7 @@ namespace Psequel { for (int i = 0; i < rows; i++) { data.add (new Row ()); + for (int j = 0; j < cols; j++) { data[i].add_field (result.get_value (i, j)); } @@ -86,6 +87,7 @@ namespace Psequel { */ public class Row : Object { + public string ahihi {get; set;} private ArrayList data; @@ -95,6 +97,7 @@ namespace Psequel { internal Row () { this.data = new ArrayList (); + ahihi = "Hello"; } public void add_field (string item) { diff --git a/src/ui/query_view.vala b/src/ui/query_view.vala index 30a07be..aba89ff 100644 --- a/src/ui/query_view.vala +++ b/src/ui/query_view.vala @@ -203,7 +203,7 @@ namespace Psequel { views_names.add (item); } - debug ("Views list reloaded, got %d views in schema %s", table_names.size, cur_schema); + debug ("Views list reloaded, got %d views in schema %s", views_names.size, cur_schema); } /** Create row widget from query result. diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index fc74e6f..9101beb 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -11,6 +11,8 @@ namespace Psequel { private ArrayList backup; + private Gtk.SortListModel sort_model; + public TableData () { Object (); } @@ -30,8 +32,7 @@ namespace Psequel { load_data.begin (schema, vname); }); - int created = 0; - int binded = 0; + Gtk.Expression[] numbers = new Gtk.Expression[ResourceManager.MAX_COLUMNS]; for (int i = 0; i < ResourceManager.MAX_COLUMNS; i++) { var factory = new Gtk.SignalListItemFactory (); @@ -42,7 +43,6 @@ namespace Psequel { label.halign = Gtk.Align.START; label.margin_start = 8; _item.child = label; - created++; }); factory.bind.connect ((_fact, _item) => { @@ -50,15 +50,26 @@ namespace Psequel { var label = _item.child as Gtk.Label; int index = _fact.get_data ("index"); label.label = row[index]; - binded++; }); + // Focus point, just ignore every thing else. + + + // Create a expression hold it + // expectation: MAX_COLUMNS obj is created + // actual: seems only one, print ("%p", expresion) looks the same + Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn ("", factory); column.set_expand (true); column.set_visible (false); + data_view.append_column (column); - var selection_model = new Gtk.SingleSelection (model); + this.sort_model = new Gtk.SortListModel (model, null); + + // assert_nonnull (model); + + var selection_model = new Gtk.SingleSelection (sort_model); data_view.set_model (selection_model); } } @@ -86,11 +97,21 @@ namespace Psequel { continue; } + var constexprs = new Gtk.ConstantExpression (Type.INT, i); + var expresion = new Gtk.CClosureExpression (Type.STRING, null, { constexprs }, (Callback)get_col_by_index, null , null); + // create sorter from expresion + // expectation: when evaluate, func is called with i = ? + // actual: always receive i = MAX_COLUMNS - 1. + var sorter = new Gtk.StringSorter (expresion); + + col.set_sorter (sorter); col.set_title (relation.get_header ((int) i)); col.set_visible (true); } + this.sort_model.set_sorter (data_view.get_sorter ()); + TimePerf.begin (); model.clear (); foreach (var item in relation) { @@ -106,4 +127,13 @@ namespace Psequel { [GtkChild] private unowned Gtk.ColumnView data_view; } + + /* + */ + public string get_col_by_index (Relation.Row row, int index) { + debug ("Access index: %d", index); + + return row[index]; + } + } \ No newline at end of file From 82302fe504039ef1bfe18cee2127924f7dd7ff29 Mon Sep 17 00:00:00 2001 From: ppvan Date: Sat, 22 Jul 2023 11:38:26 +0700 Subject: [PATCH 09/12] add numric sorting --- src/models/table.vala | 45 ++++++++++++++++++++++++++++++++++++++++-- src/ui/table_data.vala | 40 +++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/models/table.vala b/src/models/table.vala index c169292..f6f6705 100644 --- a/src/models/table.vala +++ b/src/models/table.vala @@ -12,6 +12,7 @@ namespace Psequel { private ArrayList data; private ArrayList headers; + private ArrayList cols_type; public Relation (owned Result res) { Object (); @@ -23,6 +24,16 @@ namespace Psequel { this.data = data; this.rows = data.size; this.cols = headers.size; + + this.cols_type = new ArrayList (); + // Fix me in the future + for (int i = 0; i < headers.size; i++) { + this.cols_type.add (Type.STRING); + } + } + + public Type get_column_type (int index) { + return this.cols_type[index]; } private void load_data (owned Result result) { @@ -32,7 +43,39 @@ namespace Psequel { cols = result.get_n_fields (); this.headers = new ArrayList (); + this.cols_type = new ArrayList (); for (int i = 0; i < cols; i++) { + + // Oid, should have enum for value type in VAPI but no. + switch ((uint)result.get_field_type (i)) { + case 20, 21, 23: + // int + this.cols_type.add (Type.INT64); + break; + case 16: + // bool + this.cols_type.add (Type.BOOLEAN); + break; + case 700, 701: + // real + this.cols_type.add (Type.DOUBLE); + break; + case 25, 1043, 18, 19, 1700: + // string + this.cols_type.add (Type.STRING); + break; + case 1114: + // timestamp + this.cols_type.add (Type.STRING); + break; + + default: + debug ("Programming errors, unhandled Oid: %u", (uint)result.get_field_type (i)); + this.cols_type.add (Type.STRING); + break; + // assert_not_reached (); + } + headers.add (result.get_field_name (i)); } @@ -87,7 +130,6 @@ namespace Psequel { */ public class Row : Object { - public string ahihi {get; set;} private ArrayList data; @@ -97,7 +139,6 @@ namespace Psequel { internal Row () { this.data = new ArrayList (); - ahihi = "Hello"; } public void add_field (string item) { diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index 9101beb..a5c76fc 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -52,13 +52,6 @@ namespace Psequel { label.label = row[index]; }); - // Focus point, just ignore every thing else. - - - // Create a expression hold it - // expectation: MAX_COLUMNS obj is created - // actual: seems only one, print ("%p", expresion) looks the same - Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn ("", factory); column.set_expand (true); column.set_visible (false); @@ -97,15 +90,27 @@ namespace Psequel { continue; } - var constexprs = new Gtk.ConstantExpression (Type.INT, i); - var expresion = new Gtk.CClosureExpression (Type.STRING, null, { constexprs }, (Callback)get_col_by_index, null , null); - - // create sorter from expresion - // expectation: when evaluate, func is called with i = ? - // actual: always receive i = MAX_COLUMNS - 1. - var sorter = new Gtk.StringSorter (expresion); + switch (relation.get_column_type ((int)i)) { + case Type.BOOLEAN, Type.INT64, Type.FLOAT, Type.DOUBLE: + var constexprs = new Gtk.ConstantExpression (Type.INT, i); + 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, i); + 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; + } - col.set_sorter (sorter); + col.set_title (relation.get_header ((int) i)); col.set_visible (true); } @@ -136,4 +141,9 @@ namespace Psequel { return row[index]; } + public int64 get_col_by_index_int (Relation.Row row, int index) { + debug ("Access index: %d", index); + + return int64.parse (row[index], 10); + } } \ No newline at end of file From eefa3b8aafffbdedec36b6f9c81eb9229bf16096 Mon Sep 17 00:00:00 2001 From: ppvan Date: Sat, 22 Jul 2023 20:05:54 +0700 Subject: [PATCH 10/12] add query limit setting --- data/me.ppvan.psequel.gschema.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/me.ppvan.psequel.gschema.xml b/data/me.ppvan.psequel.gschema.xml index ab32c73..8e90cda 100644 --- a/data/me.ppvan.psequel.gschema.xml +++ b/data/me.ppvan.psequel.gschema.xml @@ -26,5 +26,13 @@ + + 200 + Select query limit + + The limit of select query in the query, get 500 rows max for each query + + + From 485ec104ba60c5e4a81b34a43d9e414e32b30512 Mon Sep 17 00:00:00 2001 From: ppvan Date: Sat, 22 Jul 2023 20:06:15 +0700 Subject: [PATCH 11/12] Add filter and pagination --- res/gtk/table-data.blp | 15 ++++-- src/models/table.vala | 4 ++ src/services/query_service.vala | 13 +++-- src/ui/table_data.vala | 92 ++++++++++++++++++++++++--------- 4 files changed, 91 insertions(+), 33 deletions(-) diff --git a/res/gtk/table-data.blp b/res/gtk/table-data.blp index 5ff46d4..a145d4b 100644 --- a/res/gtk/table-data.blp +++ b/res/gtk/table-data.blp @@ -18,15 +18,17 @@ template $PsequelTableData : Gtk.Box { spacing: 12; margin-top: 8; margin-bottom: 8; - Entry { + Entry filter_entry { hexpand: true; placeholder-text: "WHERE clause"; + activate => $on_entry_activated(); } - Button { + Button filter_btn { styles ["suggested-action"] label: "Filter"; + clicked => $filter_query(); } } @@ -49,10 +51,11 @@ template $PsequelTableData : Gtk.Box { tooltip-text: "Insert Row"; } - Button { + Button reload { styles ["flat"] icon-name: "refresh-large-symbolic"; tooltip-text: "Reload Data"; + clicked => $reload_data(); } Separator {} @@ -65,16 +68,18 @@ template $PsequelTableData : Gtk.Box { Separator {} - Button { + Button left_page { styles ["flat"] icon-name: "left-large-symbolic"; tooltip-text: "Last Page"; + clicked => $load_previous_page(); } - Button { + Button right_page { styles ["flat"] icon-name: "right-large-symbolic"; tooltip-text: "Next Page"; + clicked => $load_next_page(); } } } \ No newline at end of file diff --git a/src/models/table.vala b/src/models/table.vala index f6f6705..be6fb81 100644 --- a/src/models/table.vala +++ b/src/models/table.vala @@ -68,6 +68,10 @@ namespace Psequel { // timestamp this.cols_type.add (Type.STRING); break; + case 1082: + // date + this.cols_type.add (Type.STRING); + break; default: debug ("Programming errors, unhandled Oid: %u", (uint)result.get_field_type (i)); diff --git a/src/services/query_service.vala b/src/services/query_service.vala index 66b381c..9b8f9e2 100644 --- a/src/services/query_service.vala +++ b/src/services/query_service.vala @@ -26,10 +26,17 @@ namespace Psequel { return res; } - public async Relation select (string schema, string table_name, int limit = 500, string where_clause = "") throws PsequelError { - string stmt = @"SELECT * FROM $schema.$table_name $where_clause LIMIT $limit"; + public async Relation select (string schema, string table_name, int offset = 0, int limit = 500, string where_clause = "") throws PsequelError { + + if (where_clause == "") { + string stmt = @"SELECT * FROM $schema.$table_name LIMIT $limit OFFSET $offset"; + return yield exec_query (stmt); + } else { + string stmt = @"SELECT * FROM $schema.$table_name WHERE $where_clause LIMIT $limit OFFSET $offset"; + return yield exec_query (stmt); + } + - return yield exec_query (stmt); } public async Relation db_table_fk (string schema, string table_name) throws PsequelError { diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index a5c76fc..5789233 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -13,6 +13,13 @@ namespace Psequel { private Gtk.SortListModel sort_model; + + public int query_limit { get; set; } + + public string schema {get; private set; default = "public";} + public string tbname {get; private set;} + public int current_page { get; private set; default = 0; } + public TableData () { Object (); } @@ -20,19 +27,26 @@ namespace Psequel { construct { query_service = ResourceManager.instance ().query_service; signals = ResourceManager.instance ().signals; + var setting = ResourceManager.instance ().settings; + setting.bind ("query-limit", this, "query-limit", SettingsBindFlags.DEFAULT); model = new ObservableArrayList (); backup = new ArrayList (); signals.table_activated.connect_after ((schema, tbname) => { + this.schema = schema; + this.tbname = tbname; + this.filter_entry.set_text (""); load_data.begin (schema, tbname); }); signals.view_activated.connect_after ((schema, vname) => { + this.schema = schema; + this.tbname = tbname; + this.filter_entry.set_text (""); load_data.begin (schema, vname); }); - Gtk.Expression[] numbers = new Gtk.Expression[ResourceManager.MAX_COLUMNS]; for (int i = 0; i < ResourceManager.MAX_COLUMNS; i++) { var factory = new Gtk.SignalListItemFactory (); @@ -60,23 +74,49 @@ namespace Psequel { this.sort_model = new Gtk.SortListModel (model, null); - // assert_nonnull (model); + // assert_nonnull (model); var selection_model = new Gtk.SingleSelection (sort_model); data_view.set_model (selection_model); } } + [GtkCallback] + private void filter_query (Gtk.Button btn) { + var where_clause = filter_entry.get_text (); + load_data.begin (schema, tbname, current_page, where_clause); + } + + [GtkCallback] + private void on_entry_activated (Gtk.Entry entry) { + filter_btn.clicked (); + } + [GtkCallback] + private void load_next_page (Gtk.Button btn) { + load_data.begin (schema, tbname, ++current_page); + } + + [GtkCallback] + private void load_previous_page (Gtk.Button btn) { + load_data.begin (schema, tbname, --current_page); + } + + [GtkCallback] + private void reload_data (Gtk.Button btn) { + load_data.begin (schema, tbname, current_page); + } + public void table_double_clicked () { debug ("Activated"); } - public async void load_data (string schema, string table_name) { + public async void load_data (string schema, string table_name, int page = 0, string where = "") { try { - Relation relation = yield query_service.select (schema, table_name, 500); + + Relation relation = yield query_service.select (schema, table_name, page * query_limit, query_limit, where); // Show error model. debug (relation.to_string ()); @@ -90,27 +130,27 @@ namespace Psequel { continue; } - switch (relation.get_column_type ((int)i)) { - case Type.BOOLEAN, Type.INT64, Type.FLOAT, Type.DOUBLE: - var constexprs = new Gtk.ConstantExpression (Type.INT, i); - 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); + switch (relation.get_column_type ((int) i)) { + case Type.BOOLEAN, Type.INT64, Type.FLOAT, Type.DOUBLE: + var constexprs = new Gtk.ConstantExpression (Type.INT, i); + 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, i); - 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); + default: + var constexprs = new Gtk.ConstantExpression (Type.INT, i); + 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; } - + col.set_title (relation.get_header ((int) i)); col.set_visible (true); } @@ -131,19 +171,21 @@ namespace Psequel { [GtkChild] private unowned Gtk.ColumnView data_view; + + [GtkChild] + private unowned Gtk.Entry filter_entry; + + [GtkChild] + private unowned Gtk.Button filter_btn; } /* */ public string get_col_by_index (Relation.Row row, int index) { - debug ("Access index: %d", index); - return row[index]; } - - public int64 get_col_by_index_int (Relation.Row row, int index) { - debug ("Access index: %d", index); + public int64 get_col_by_index_int (Relation.Row row, int index) { return int64.parse (row[index], 10); } } \ No newline at end of file From e368c1f787d47f3274dd645830217fdb67c76589 Mon Sep 17 00:00:00 2001 From: ppvan Date: Sat, 22 Jul 2023 20:26:21 +0700 Subject: [PATCH 12/12] not allow negative offset --- res/gtk/table-data.blp | 2 +- src/ui/table_data.vala | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/res/gtk/table-data.blp b/res/gtk/table-data.blp index a145d4b..18be54f 100644 --- a/res/gtk/table-data.blp +++ b/res/gtk/table-data.blp @@ -60,7 +60,7 @@ template $PsequelTableData : Gtk.Box { Separator {} - Label { + Label status_label { label: "Rows 1 - 500"; hexpand: true; halign: start; diff --git a/src/ui/table_data.vala b/src/ui/table_data.vala index 5789233..b364be7 100644 --- a/src/ui/table_data.vala +++ b/src/ui/table_data.vala @@ -27,9 +27,24 @@ namespace Psequel { construct { query_service = ResourceManager.instance ().query_service; signals = ResourceManager.instance ().signals; + var setting = ResourceManager.instance ().settings; setting.bind ("query-limit", this, "query-limit", SettingsBindFlags.DEFAULT); + this.bind_property ("current_page", status_label, "label", BindingFlags.SYNC_CREATE, (bindding, from, ref to) => { + int curr_page = from.get_int (); + to.set_string (@"Rows $(curr_page * query_limit + 1) - $(query_limit * (curr_page + 1))"); + + return true; + }); + + this.bind_property ("current_page", left_page, "sensitive", BindingFlags.SYNC_CREATE, (bindding, from, ref to) => { + int curr_page = from.get_int (); + to.set_boolean (curr_page > 0); + + return true; + }); + model = new ObservableArrayList (); backup = new ArrayList (); @@ -96,6 +111,8 @@ namespace Psequel { [GtkCallback] private void load_next_page (Gtk.Button btn) { load_data.begin (schema, tbname, ++current_page); + + } [GtkCallback] @@ -177,6 +194,15 @@ namespace Psequel { [GtkChild] private unowned Gtk.Button filter_btn; + + [GtkChild] + private unowned Gtk.Button left_page; + + [GtkChild] + private unowned Gtk.Button right_page; + + [GtkChild] + private unowned Gtk.Label status_label; } /*