diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..9d926d6
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,17 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(lldb) Launch",
+ "type": "lldb",
+ "request": "launch",
+ "program": "${workspaceFolder}/build/src/psequel",
+ "args": [],
+ "stopOnEntry": false,
+ "cwd": "${workspaceFolder}",
+ "env": {
+
+ },
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 96a330f..ac155ce 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,14 @@
# This is just a file for me to type command faster, not the build file.
-debug:
- cd build; ninja && G_MESSAGES_DEBUG=Psequel ./src/psequel
+debug: clean
+ ninja -C build/ && G_MESSAGES_DEBUG=Psequel ./build/src/psequel
+
+clean:
+ rm -rf build/res
+
+test:
+ ninja -C build/ && ./build/test/psequel-test
flatpak:
flatpak-builder _build/ me.ppvan.psequel-debug.json --force-clean
diff --git a/data/me.ppvan.psequel.desktop.in b/data/me.ppvan.psequel.desktop.in
index 082ffb7..b980bda 100644
--- a/data/me.ppvan.psequel.desktop.in
+++ b/data/me.ppvan.psequel.desktop.in
@@ -4,5 +4,5 @@ Exec=psequel
Icon=me.ppvan.psequel
Terminal=false
Type=Application
-Categories=GTK;
+Categories=Database;Development;GTK;
StartupNotify=true
diff --git a/data/meson.build b/data/meson.build
index 7680fc6..a4ce219 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -12,7 +12,7 @@ if desktop_utils.found()
test('Validate desktop file', desktop_utils, args: [desktop_file])
endif
-appstream_file = i18n.merge_file(
+i18n.merge_file(
input: 'me.ppvan.psequel.appdata.xml.in',
output: 'me.ppvan.psequel.appdata.xml',
po_dir: '../po',
@@ -20,10 +20,11 @@ appstream_file = i18n.merge_file(
install_dir: join_paths(get_option('datadir'), 'appdata')
)
-appstream_util = find_program('appstream-util', required: false)
-if appstream_util.found()
- test('Validate appstream file', appstream_util, args: ['validate', appstream_file])
-endif
+# Disable redudant check.
+# appstream_util = find_program('appstream-util', required: false)
+# if appstream_util.found()
+# test('Validate appstream file', appstream_util, args: ['validate', appstream_file])
+# endif
install_data('me.ppvan.psequel.gschema.xml',
diff --git a/meson.build b/meson.build
index 105f7f1..95f9111 100644
--- a/meson.build
+++ b/meson.build
@@ -1,27 +1,37 @@
-project('psequel', ['c', 'vala'],
- version: '0.1.8',
- meson_version: '>= 0.62.0',
- default_options: [ 'warning_level=2', 'werror=false', ],
+project(
+ 'psequel',
+ ['c', 'vala'],
+ version: '0.1.8',
+ meson_version: '>= 1.3.0',
+ default_options: [
+ 'warning_level=2',
+ 'werror=false',
+ ],
)
app_id = 'me.ppvan.psequel'
i18n = import('i18n')
gnome = import('gnome')
-cc = meson.get_compiler('c')
+# cc = meson.get_compiler('c')
valac = meson.get_compiler('vala')
-vapi_dir = join_paths (meson.project_source_root(), 'src', 'vapi')
+
+# Pass custom vapi to compiler (for libpq)
+vapi_dir = join_paths(meson.project_source_root(), 'src', 'vapi')
add_project_arguments(['--vapidir', vapi_dir], language: 'vala')
subdir('data')
subdir('res')
subdir('src')
+subdir('test')
subdir('po')
+# Using gnome module to do some task after the app is installed, like compile schema (settings data)
+# Update icon cache.
gnome.post_install(
- glib_compile_schemas: true,
+ glib_compile_schemas: true,
gtk_update_icon_cache: true,
- update_desktop_database: true,
+ update_desktop_database: true,
)
diff --git a/meson.options b/meson.options
new file mode 100644
index 0000000..e2a4355
--- /dev/null
+++ b/meson.options
@@ -0,0 +1 @@
+option('with_query', type : 'boolean', value : false, description : 'Run real query test(need postgresql server setup)')
\ No newline at end of file
diff --git a/res/gtk/connection-form.blp b/res/gtk/connection-form.blp
deleted file mode 100644
index 7b13b47..0000000
--- a/res/gtk/connection-form.blp
+++ /dev/null
@@ -1,299 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-
-menu primary_menu {
-
- section {
- item {
- custom: "style-switcher";
- }
- }
-
- section {
- item {
- label: _("_New Window");
- action: "app.new-window";
- }
- }
- section {
- item {
- label: _("_Import");
- action: "win.import";
- }
-
- item {
- label: _("_Export");
- action: "win.export";
- }
- }
-
- section {
- item {
- label: _("_Preferences");
- action: "app.preferences";
- }
-
- item {
- label: _("_Keyboard Shortcuts");
- action: "win.show-help-overlay";
- }
-
- item {
- label: _("_About");
- action: "app.about";
- }
- }
-}
-
-template $PsequelConnectionForm : Adw.Bin {
-
- child: WindowHandle {
- Box {
- orientation: vertical;
- hexpand: true;
- vexpand: true;
- Adw.HeaderBar header {
- styles ["flat"]
-
- [title]
- 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;
- popover: Gtk.PopoverMenu {
- menu-model: primary_menu;
-
- [style-switcher]
- $PsequelStyleSwitcher {
-
- }
- };
- }
- }
-
- Adw.Clamp {
- valign: start;
- hexpand: true;
- vexpand: true;
- maximum-size: 700;
- Box {
- orientation: vertical;
- styles ["connection-form"]
- // vexpand: true;
- spacing: 4;
-
- Label {
- margin-top: 20;
- margin-bottom: 60;
- styles ["title-0"]
-
- label: "Connect with Psequel";
- }
- Grid {
-
- hexpand: true;
- vexpand: true;
- row-homogeneous: true;
- row-spacing: 8;
- column-spacing: 8;
- margin-start: 4;
- margin-end: 4;
-
- Label {
- halign: end;
- label: "Name:";
-
- layout {
- row: 0;
- column: 0;
- column-span: 3;
- }
- }
-
- Label {
- halign: end;
- label: "Host:";
-
- layout {
- row: 1;
- column: 0;
- column-span: 3;
- }
- }
-
- Label {
- halign: end;
- label: "User:";
-
- layout {
- row: 2;
- column: 0;
- column-span: 3;
- }
- }
-
- Label {
- halign: end;
- label: "Password:";
-
- layout {
- row: 3;
- column: 0;
- column-span: 3;
- }
- }
-
- Label {
- halign: end;
- label: "Database:";
-
- layout {
- row: 4;
- column: 0;
- column-span: 3;
- }
- }
-
- Entry name_entry {
- placeholder-text: "Connection name";
- hexpand: true;
- activate => $on_entry_activated();
- changed => $on_text_changed();
- layout {
- row: 0;
- column: 3;
- column-span: 7;
- }
- }
-
- Entry host_entry {
- placeholder-text: "localhost";
- activate => $on_entry_activated();
- changed => $on_text_changed();
- layout {
- row: 1;
- column: 3;
- column-span: 5;
- }
- }
-
- Label {
- label: "Port";
- halign: end;
- layout {
- row: 1;
- column: 8;
- }
- }
-
- Entry port_entry {
- placeholder-text: "5432";
- activate => $on_entry_activated();
- changed => $on_text_changed();
- layout {
- row: 1;
- column: 9;
- column-span: 1;
- }
- }
-
- Entry user_entry {
- placeholder-text: "postgres";
- activate => $on_entry_activated();
- changed => $on_text_changed();
- layout {
- row: 2;
- column: 3;
- column-span: 7;
- }
- }
-
- PasswordEntry password_entry {
- placeholder-text: "";
- show-peek-icon: true;
- activate => $on_entry_activated();
- changed => $on_text_changed();
- layout {
- row: 3;
- column: 3;
- column-span: 7;
- }
- }
-
-
- Entry database_entry {
- placeholder-text: "postgres";
- activate => $on_entry_activated();
- changed => $on_text_changed();
- layout {
- row: 4;
- column: 3;
- column-span: 7;
- }
- }
-
- Label {
- label: "SSL:";
- halign: end;
- layout {
- row: 5;
- column: 0;
- column-span: 3;
- }
- }
-
- Box {
- orientation: horizontal;
- Box {
- valign: center;
- orientation: vertical;
- Switch ssl_switch {
- activate => $on_switch_changed();
- }
- }
- layout {
- row: 5;
- column: 3;
- }
- }
- }
-
- Box {
-
- margin-top: 20;
- margin-bottom: 10;
-
- Label status_label {
- label: "";
- halign: start;
- hexpand: true;
- }
-
- Box {
-
- spacing: 8;
-
- Spinner spinner {
- spinning: bind template.is-connectting;
- }
-
- Button connect_btn {
- styles ["suggested-action"]
- label: "Connect";
- clicked => $on_connect_clicked();
- }
- }
- }
-
- }
- }
- }
- };
-}
\ No newline at end of file
diff --git a/res/gtk/connection-listitem.blp b/res/gtk/connection-listitem.blp
index 79f12f4..3425fd2 100644
--- a/res/gtk/connection-listitem.blp
+++ b/res/gtk/connection-listitem.blp
@@ -1,8 +1,8 @@
using Gtk 4.0;
template ListItem {
- child: $PsequelConnectionRow {
- item: bind template.item;
- pos: bind template.position;
- };
+ child: $PsequelConnectionRow {
+ item: bind template.item;
+ pos: bind template.position;
+ };
}
diff --git a/res/gtk/connection-row.blp b/res/gtk/connection-row.blp
index 3443e5b..7f24247 100644
--- a/res/gtk/connection-row.blp
+++ b/res/gtk/connection-row.blp
@@ -1,28 +1,28 @@
using Gtk 4.0;
-template $PsequelConnectionRow : Gtk.Box {
- orientation: horizontal;
- spacing: 12;
+template $PsequelConnectionRow: Gtk.Box {
+ orientation: horizontal;
+ spacing: 12;
- Image {
- icon-name: "text-sql-symbolic";
- }
+ Image {
+ icon-name: "text-sql-symbolic";
+ }
- Label {
- label: bind template.item as <$PsequelConnection>.name;
- halign: start;
- ellipsize: end;
- single-line-mode: true;
- }
+ Label {
+ label: bind template.item as <$PsequelConnection>.name;
+ halign: start;
+ ellipsize: end;
+ single-line-mode: true;
+ }
- PopoverMenu popover {
- menu-model: menu;
- }
+ PopoverMenu popover {
+ menu-model: menu;
+ }
- GestureClick {
- button: 3; // right clicked
- released => $on_right_clicked();
- }
+ GestureClick {
+ button: 3; // right clicked
+ released => $on_right_clicked();
+ }
}
menu menu {
@@ -44,4 +44,4 @@ menu menu {
action: "conn.delete";
}
}
-}
\ No newline at end of file
+}
diff --git a/res/gtk/connection-sidebar.blp b/res/gtk/connection-sidebar.blp
deleted file mode 100644
index f1484a7..0000000
--- a/res/gtk/connection-sidebar.blp
+++ /dev/null
@@ -1,50 +0,0 @@
-using Gtk 4.0;
-
-template $PsequelConnectionSidebar : Gtk.Box {
- orientation: vertical;
- spacing: 4;
- margin-top: 4;
-
- Box {
- Label {
- styles ["text-bold"]
- margin-top: 6;
- margin-start: 8;
- halign: start;
- margin-bottom: 6;
- use-markup: true;
- label: "Connections";
- }
-
- Box {
- spacing: 4;
- hexpand: true;
- halign: end;
-
-
- Button {
- tooltip-text: "Add new connection";
- styles ["flat"]
- icon-name: "plus-large-symbolic";
- clicked => $on_add_connection();
- }
- }
- }
- ScrolledWindow {
- vexpand: true;
- ListView listview {
- styles ["navigation-sidebar"]
-
- model: SingleSelection selection_model {
- model: bind template.connections;
- autoselect: true;
- };
-
- activate => $on_connection_active();
-
- factory: BuilderListItemFactory {
- resource: "/me/ppvan/psequel/gtk/connection-listitem.ui";
- };
- }
- }
-}
\ No newline at end of file
diff --git a/res/gtk/connection-view.blp b/res/gtk/connection-view.blp
index 9027181..b585328 100644
--- a/res/gtk/connection-view.blp
+++ b/res/gtk/connection-view.blp
@@ -2,45 +2,7 @@ using Gtk 4.0;
using Gio 2.0;
using Adw 1;
-template $PsequelConnectionView : Adw.Bin {
- hexpand: true;
- vexpand: true;
-
- Gtk.Paned paned {
- // can-shrink: false;
- shrink-start-child: false;
- shrink-end-child: false;
-
-
- [start]
- $PsequelConnectionSidebar sidebar {
- width-request: 300;
-
- connections: bind template.viewmodel as <$PsequelConnectionViewModel>.connections;
- selected-connection: bind template.viewmodel as <$PsequelConnectionViewModel>.selected-connection;
-
- request-new-connection => $add_new_connection();
- request_dup_connection => $dup_connection ();
- request_remove_connection => $remove_connection ();
- request_connect_database => $active_connection ();
- }
-
- [end]
- $PsequelConnectionForm form {
- width-request: 800;
-
- selected-connection: bind sidebar.selected-connection;
- is-connectting: bind template.viewmodel as <$PsequelConnectionViewModel>.is-connectting;
- menu-model: primary_menu;
- request-database => $active_connection ();
- connections-changed => $save_connections ();
- }
- }
-}
-
-
menu primary_menu {
-
section {
item {
custom: "style-switcher";
@@ -53,14 +15,15 @@ menu primary_menu {
action: "app.new-window";
}
}
+
section {
item {
- label: _("_Import Connections");
+ label: _("_Import");
action: "win.import";
}
item {
- label: _("_Export Connections");
+ label: _("_Export");
action: "win.export";
}
}
@@ -80,10 +43,374 @@ menu primary_menu {
label: _("_About");
action: "app.about";
}
+ }
+}
- item {
- label: _("_Dark");
- action: "app.dark";
+template $PsequelConnectionView: Adw.Bin {
+ hexpand: true;
+ vexpand: true;
+
+ Gtk.Paned paned {
+ // can-shrink: false;
+ shrink-start-child: false;
+ shrink-end-child: false;
+
+ [start]
+ Box {
+ width-request: 300;
+ orientation: vertical;
+ spacing: 4;
+ margin-top: 4;
+
+ Box {
+ Label {
+ styles [
+ "text-bold"
+ ]
+
+ margin-top: 6;
+ margin-start: 8;
+ halign: start;
+ margin-bottom: 6;
+ use-markup: true;
+ label: "Connections";
+ }
+
+ Box {
+ spacing: 4;
+ hexpand: true;
+ halign: end;
+
+ Button {
+ tooltip-text: "Add new connection";
+
+ styles [
+ "flat"
+ ]
+
+ icon-name: "plus-large-symbolic";
+ clicked => $add_new_connection();
+ }
+ }
+ }
+
+ ScrolledWindow {
+ vexpand: true;
+
+ ListView listview {
+ styles [
+ "navigation-sidebar"
+ ]
+
+ model: SingleSelection selection_model {
+ model: bind template.viewmodel as <$PsequelConnectionViewModel>.connections;
+ autoselect: true;
+ };
+
+ activate => $active_connection();
+
+ factory: BuilderListItemFactory {
+ resource: "/me/ppvan/psequel/gtk/connection-listitem.ui";
+ };
+ }
+ }
}
+
+ // $PsequelConnectionSidebar sidebar {
+ // width-request: 300;
+ // connections: bind template.viewmodel as <$PsequelConnectionViewModel>.connections;
+ // selected-connection: bind template.viewmodel as <$PsequelConnectionViewModel>.selected-connection;
+ // request-new-connection => $add_new_connection();
+ // request_dup_connection => $dup_connection();
+ // request_remove_connection => $remove_connection();
+ // request_connect_database => $active_connection();
+ // }
+
+ [end]
+ Adw.Bin {
+ child: WindowHandle {
+ Box {
+ orientation: vertical;
+ hexpand: true;
+ vexpand: true;
+
+ Adw.HeaderBar header {
+ styles [
+ "flat"
+ ]
+
+ [title]
+ 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;
+
+ popover: Gtk.PopoverMenu {
+ menu-model: primary_menu;
+
+ [style-switcher]
+ $PsequelStyleSwitcher {}
+ };
+ }
+ }
+
+ Adw.Clamp {
+ valign: start;
+ hexpand: true;
+ vexpand: true;
+ maximum-size: 700;
+
+ child: Box {
+ orientation: vertical;
+
+ Box {
+ orientation: vertical;
+
+ styles [
+ "connection-form"
+ ]
+
+ // vexpand: true;
+ spacing: 4;
+
+ Label {
+ margin-top: 20;
+ margin-bottom: 60;
+
+ styles [
+ "title-0"
+ ]
+
+ label: "Connect with Psequel";
+ }
+
+ Grid {
+ hexpand: true;
+ vexpand: true;
+ row-homogeneous: true;
+ row-spacing: 8;
+ column-spacing: 8;
+ margin-start: 4;
+ margin-end: 4;
+
+ Label {
+ halign: end;
+ label: "Name:";
+
+ layout {
+ row: 0;
+ column: 0;
+ column-span: 3;
+ }
+ }
+
+ Label {
+ halign: end;
+ label: "Host:";
+
+ layout {
+ row: 1;
+ column: 0;
+ column-span: 3;
+ }
+ }
+
+ Label {
+ halign: end;
+ label: "User:";
+
+ layout {
+ row: 2;
+ column: 0;
+ column-span: 3;
+ }
+ }
+
+ Label {
+ halign: end;
+ label: "Password:";
+
+ layout {
+ row: 3;
+ column: 0;
+ column-span: 3;
+ }
+ }
+
+ Label {
+ halign: end;
+ label: "Database:";
+
+ layout {
+ row: 4;
+ column: 0;
+ column-span: 3;
+ }
+ }
+
+ Entry name_entry {
+ placeholder-text: "Connection name";
+ hexpand: true;
+ activate => $on_entry_activated();
+ changed => $on_text_changed();
+
+ layout {
+ row: 0;
+ column: 3;
+ column-span: 7;
+ }
+ }
+
+ Entry host_entry {
+ placeholder-text: "localhost";
+ activate => $on_entry_activated();
+ changed => $on_text_changed();
+
+ layout {
+ row: 1;
+ column: 3;
+ column-span: 5;
+ }
+ }
+
+ Label {
+ label: "Port";
+ halign: end;
+
+ layout {
+ row: 1;
+ column: 8;
+ }
+ }
+
+ Entry port_entry {
+ placeholder-text: "5432";
+ activate => $on_entry_activated();
+ changed => $on_text_changed();
+
+ layout {
+ row: 1;
+ column: 9;
+ column-span: 1;
+ }
+ }
+
+ Entry user_entry {
+ placeholder-text: "postgres";
+ activate => $on_entry_activated();
+ changed => $on_text_changed();
+
+ layout {
+ row: 2;
+ column: 3;
+ column-span: 7;
+ }
+ }
+
+ PasswordEntry password_entry {
+ placeholder-text: "";
+ show-peek-icon: true;
+ activate => $on_entry_activated();
+ changed => $on_text_changed();
+
+ layout {
+ row: 3;
+ column: 3;
+ column-span: 7;
+ }
+ }
+
+ Entry database_entry {
+ placeholder-text: "postgres";
+ activate => $on_entry_activated();
+ changed => $on_text_changed();
+
+ layout {
+ row: 4;
+ column: 3;
+ column-span: 7;
+ }
+ }
+
+ Label {
+ label: "SSL:";
+ halign: end;
+
+ layout {
+ row: 5;
+ column: 0;
+ column-span: 3;
+ }
+ }
+
+ Box {
+ orientation: horizontal;
+
+ Box {
+ valign: center;
+ orientation: vertical;
+
+ Switch ssl_switch {
+ activate => $on_switch_changed();
+ }
+ }
+
+ layout {
+ row: 5;
+ column: 3;
+ }
+ }
+ }
+ }
+
+ Box {
+ margin-top: 20;
+ margin-bottom: 10;
+
+ Label status_label {
+ label: "Hello world";
+ halign: start;
+ hexpand: true;
+ }
+
+ Box {
+ spacing: 8;
+
+ Spinner spinner {
+ spinning: bind template.viewmodel as <$PsequelConnectionViewModel>.is-connectting;
+ }
+
+ Button connect_btn {
+ styles [
+ "suggested-action"
+ ]
+
+ label: "Connect";
+ clicked => $on_connect_clicked();
+ }
+ }
+ }
+ };
+ }
+ }
+ };
+ }
+
+ // $PsequelConnectionForm form {
+ // width-request: 800;
+ // selected-connection: bind sidebar.selected-connection;
+ // is-connectting: bind template.viewmodel as <$PsequelConnectionViewModel>.is-connectting;
+ // current-state: bind template.viewmodel as <$PsequelConnectionViewModel>.current-state;
+ // menu-model: primary_menu;
+ // request-database => $active_connection ();
+ // connections-changed => $save_connections ();
+ // }
}
-}
\ No newline at end of file
+}
diff --git a/res/gtk/help-overlay.blp b/res/gtk/help-overlay.blp
index 7ccfa7c..05dcdf2 100644
--- a/res/gtk/help-overlay.blp
+++ b/res/gtk/help-overlay.blp
@@ -26,7 +26,6 @@ ShortcutsWindow help_overlay {
}
}
-
ShortcutsGroup {
title: C_("shortcut window", "Query Editor");
diff --git a/res/gtk/preferences-window.blp b/res/gtk/preferences-window.blp
index 8c852ed..f9ce5ba 100644
--- a/res/gtk/preferences-window.blp
+++ b/res/gtk/preferences-window.blp
@@ -1,92 +1,88 @@
using Gtk 4.0;
using Adw 1;
-template $PsequelPreferencesWindow : Adw.PreferencesWindow {
-
- default-width: 640;
- default-height: 640;
- modal: false;
-
- Adw.PreferencesPage {
- name: "General";
- icon-name: "settings-symbolic";
-
- Adw.PreferencesGroup {
- name: "database";
- title: "Database";
-
- Adw.ActionRow {
- title: "Connection timeout";
- subtitle: "in seconds";
-
- SpinButton conn_timeout {
- valign: center;
- numeric: true;
- value: 5;
- adjustment:
- Adjustment {
- lower: 1;
- upper: 100;
- value: 5;
- step-increment: 1;
- }
- ;
- }
- }
-
- Adw.ActionRow {
- title: "Query timeout";
- subtitle: "in seconds";
-
- SpinButton query_timeout {
- valign: center;
- numeric: true;
- value: 5;
- adjustment:
- Adjustment {
- lower: 1;
- upper: 100;
- step-increment: 1;
- value: 5;
- }
- ;
- }
- }
-
- Adw.ActionRow {
- title: "Query limit";
- subtitle: "Max rows return in SELECT";
-
- SpinButton query_limit {
- valign: center;
- numeric: true;
- value: 200;
- adjustment:
- Adjustment {
- lower: 100;
- upper: 900;
- step-increment: 100;
- value: 200;
- }
- ;
- }
- }
+template $PsequelPreferencesWindow: Adw.PreferencesWindow {
+ default-width: 640;
+ default-height: 640;
+ modal: false;
+
+ Adw.PreferencesPage {
+ name: "General";
+ icon-name: "settings-symbolic";
+
+ Adw.PreferencesGroup {
+ name: "database";
+ title: "Database";
+
+ Adw.ActionRow {
+ title: "Connection timeout";
+ subtitle: "in seconds";
+
+ SpinButton conn_timeout {
+ valign: center;
+ numeric: true;
+ value: 5;
+
+ adjustment: Adjustment {
+ lower: 1;
+ upper: 100;
+ value: 5;
+ step-increment: 1;
+ };
}
+ }
- Adw.PreferencesGroup {
- name: "editor";
- title: "Editor";
- Adw.ActionRow {
- title: "Editor Font";
- activatable: true;
+ Adw.ActionRow {
+ title: "Query timeout";
+ subtitle: "in seconds";
- Label font_label {
- label: "Source Code Pro 12";
- }
+ SpinButton query_timeout {
+ valign: center;
+ numeric: true;
+ value: 5;
- activated => $on_font_chooser();
- }
+ adjustment: Adjustment {
+ lower: 1;
+ upper: 100;
+ step-increment: 1;
+ value: 5;
+ };
}
+ }
+
+ Adw.ActionRow {
+ title: "Query limit";
+ subtitle: "Max rows return in SELECT";
+
+ SpinButton query_limit {
+ valign: center;
+ numeric: true;
+ value: 200;
+
+ adjustment: Adjustment {
+ lower: 100;
+ upper: 900;
+ step-increment: 100;
+ value: 200;
+ };
+ }
+ }
+ }
+
+ Adw.PreferencesGroup {
+ name: "editor";
+ title: "Editor";
+
+ Adw.ActionRow {
+ title: "Editor Font";
+ activatable: true;
+
+ Label font_label {
+ label: "Source Code Pro 12";
+ }
+
+ activated => $on_font_chooser();
+ }
}
-
+ }
}
diff --git a/res/gtk/query-editor.blp b/res/gtk/query-editor.blp
index 69976f2..25b3908 100644
--- a/res/gtk/query-editor.blp
+++ b/res/gtk/query-editor.blp
@@ -3,171 +3,186 @@ using Adw 1;
using Gdk 4.0;
using GtkSource 5;
-template $PsequelQueryEditor : Adw.Bin {
-
- selected-query: bind template.query-viewmodel as <$PsequelQueryViewModel>.selected-query;
-
- Paned paned {
- orientation: vertical;
- shrink-start-child: false;
- resize-start-child: true;
-
- shrink-end-child: false;
- resize-end-child: false;
-
- Adw.Bin {
- height-request: 250;
- ScrolledWindow {
- hscrollbar-policy: never;
- GtkSource.View editor {
- buffer: GtkSource.Buffer buffer {
-
- };
-
- wrap-mode: word;
- monospace: true;
- auto-indent: true;
- show-line-numbers: true;
- smart-backspace: true;
- smart-home-end: before;
- top-margin: 6;
-
- highlight-current-line: true;
- insert-spaces-instead-of-tabs: true;
- }
- }
+template $PsequelQueryEditor: Adw.Bin {
+ selected-query: bind template.query-viewmodel as <$PsequelQueryViewModel>.selected-query;
+
+ Paned paned {
+ orientation: vertical;
+ shrink-start-child: false;
+ resize-start-child: true;
+ shrink-end-child: false;
+ resize-end-child: false;
+
+ Adw.Bin {
+ height-request: 250;
+
+ ScrolledWindow {
+ hscrollbar-policy: never;
+
+ GtkSource.View editor {
+ buffer: GtkSource.Buffer buffer {};
+
+ wrap-mode: word;
+ monospace: true;
+ auto-indent: true;
+ show-line-numbers: true;
+ smart-backspace: true;
+ smart-home-end: before;
+ top-margin: 6;
+ highlight-current-line: true;
+ insert-spaces-instead-of-tabs: true;
}
+ }
+ }
- Box {
- orientation: vertical;
- height-request: 400;
- Box {
- margin-top: 8;
- margin-bottom: 8;
- margin-start: 8;
- margin-end: 8;
- spacing: 8;
-
- MenuButton {
- styles ["big-icon"]
- tooltip-text: "editor setting";
- icon-name: "settings-symbolic";
- halign: start;
- valign: start;
- direction: down;
- menu-model: menu;
- }
+ Box {
+ orientation: vertical;
+ height-request: 400;
+
+ Box {
+ margin-top: 8;
+ margin-bottom: 8;
+ margin-start: 8;
+ margin-end: 8;
+ spacing: 8;
+
+ MenuButton {
+ styles [
+ "big-icon"
+ ]
+
+ tooltip-text: "editor setting";
+ icon-name: "settings-symbolic";
+ halign: start;
+ valign: start;
+ direction: down;
+ menu-model: menu;
+ }
- MenuButton {
- styles ["big-icon"]
- icon-name: "history-undo-symbolic";
- tooltip-text: "Query History";
- popover: Popover popover {
- height-request: 300;
- width-request: 600;
- Box {
- spacing: 4;
- orientation: vertical;
- // SearchEntry {
- // placeholder-text: "Find Query";
- // }
-
- ScrolledWindow {
- vexpand: true;
- min-content-width: 300;
- max-content-width: 600;
-
- ListView {
- styles ["navigation-sidebar"]
- model: SingleSelection selection_model {
- model: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.query-history;
- };
-
- factory: BuilderListItemFactory {
- resource: "/me/ppvan/psequel/gtk/query-listitem.ui";
- };
-
- activate => $on_query_history_exec();
- }
- }
-
- Separator {}
-
- Button {
- styles ["destructive-action"]
- label: "Clear History";
- clicked => $on_clear_history();
- }
-
- }
- };
- }
+ MenuButton {
+ styles [
+ "big-icon"
+ ]
+ icon-name: "history-undo-symbolic";
+ tooltip-text: "Query History";
- Label {
- hexpand: true;
- }
+ popover: Popover popover {
+ height-request: 300;
+ width-request: 600;
- Spinner spinner {
- spinning: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.is-loading;
+ Box {
+ spacing: 4;
+ orientation: vertical;
+ // SearchEntry {
+ // placeholder-text: "Find Query";
+ // }
+ ScrolledWindow {
+ vexpand: true;
+ min-content-width: 300;
+ max-content-width: 600;
+
+ ListView {
+ styles [
+ "navigation-sidebar"
+ ]
+
+ model: SingleSelection selection_model {
+ model: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.query-history;
+ };
+
+ factory: BuilderListItemFactory {
+ resource: "/me/ppvan/psequel/gtk/query-listitem.ui";
+ };
+
+ activate => $on_query_history_exec();
}
+ }
- // Label query_time {
- // label: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.query-time;
- // }
+ Separator {}
- Button run_query_btn {
- styles ["suggested-action"]
- label: "Run Query";
- tooltip-text: "Ctrl+Enter";
- clicked => $run_query_cb();
- }
+ Button {
+ styles [
+ "destructive-action"
+ ]
+
+ label: "Clear History";
+ clicked => $on_clear_history();
+ }
}
+ };
+ }
- Separator {}
+ Label {
+ hexpand: true;
+ }
- $PsequelQueryResults query_results {
- wellcome_message: "Run a query";
- current-relation: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.current-relation;
- is-loading: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.is-loading;
- err-msg: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.err-msg;
- }
+ Spinner spinner {
+ spinning: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.is-loading;
+ }
- Box {
- spacing: 4;
- margin-start: 12;
- margin-top: 8;
- margin-bottom: 8;
- margin-end: 12;
- Image {
- icon-size: normal;
- margin-end: 6;
- styles ["success"]
- icon-name: "test-pass-symbolic";
- pixel-size: 20;
- visible: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.success;
- }
+ // Label query_time {
+ // label: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.query-time;
+ // }
+ Button run_query_btn {
+ styles [
+ "suggested-action"
+ ]
+
+ label: "Run Query";
+ tooltip-text: "Ctrl+Enter";
+ clicked => $run_query_cb();
+ }
+ }
+
+ Separator {}
+
+ $PsequelQueryResults query_results {
+ wellcome_message: "Run a query";
+ current-relation: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.current-relation;
+ is-loading: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.is-loading;
+ err-msg: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.err-msg;
+ }
+
+ Box {
+ spacing: 4;
+ margin-start: 12;
+ margin-top: 8;
+ margin-bottom: 8;
+ margin-end: 12;
+
+ Image {
+ icon-size: normal;
+ margin-end: 6;
+
+ styles [
+ "success"
+ ]
+
+ icon-name: "test-pass-symbolic";
+ pixel-size: 20;
+ visible: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.success;
+ }
- Label row_affect {
- label: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.row-affected;
- }
+ Label row_affect {
+ label: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.row-affected;
+ }
- Label {
- hexpand: true;
- }
+ Label {
+ hexpand: true;
+ }
- Button export {
- label: "Export";
- clicked => $on_export_csv();
- }
- }
+ Button export {
+ label: "Export";
+ clicked => $on_export_csv();
}
+ }
}
+ }
}
menu menu {
section {
-
item {
label: _("_Editor Font");
action: "app.preferences";
@@ -188,4 +203,4 @@ menu menu {
// action: "editor.auto-completion";
// }
}
-}
\ No newline at end of file
+}
diff --git a/res/gtk/query-listitem.blp b/res/gtk/query-listitem.blp
index c97ac1d..c1494f7 100644
--- a/res/gtk/query-listitem.blp
+++ b/res/gtk/query-listitem.blp
@@ -1,10 +1,10 @@
using Gtk 4.0;
template ListItem {
- child: Label {
- label: bind template.item as <$PsequelQuery>.sql;
- halign: start;
- ellipsize: end;
- single-line-mode: true;
- };
-}
\ No newline at end of file
+ child: Label {
+ label: bind template.item as <$PsequelQuery>.sql;
+ halign: start;
+ ellipsize: end;
+ single-line-mode: true;
+ };
+}
diff --git a/res/gtk/query-results.blp b/res/gtk/query-results.blp
index 547949e..b18e564 100644
--- a/res/gtk/query-results.blp
+++ b/res/gtk/query-results.blp
@@ -1,79 +1,93 @@
using Gtk 4.0;
using Adw 1;
-template $PsequelQueryResults : Adw.Bin {
-
- wellcome_message: "Select a table";
- Stack stack {
- StackPage {
- name: "empty";
-
- child: Adw.Clamp {
- maximum-size: 32;
-
- Box {
- styles ["icon-xl"]
- orientation: vertical;
- valign: center;
- Image {
- icon-name: "webview-filler-symbolic";
- }
-
- Label label {
- styles ["title-2"]
- halign: center;
- valign: center;
- margin-start: 24;
- margin-top: 24;
- margin-bottom: 24;
- margin-end: 24;
-
- label: bind template.wellcome_message as ;
- }
- }
- };
+template $PsequelQueryResults: Adw.Bin {
+ wellcome_message: "Select a table";
+
+ Stack stack {
+ StackPage {
+ name: "empty";
+
+ child: Adw.Clamp {
+ maximum-size: 32;
+
+ Box {
+ styles [
+ "icon-xl"
+ ]
+
+ orientation: vertical;
+ valign: center;
+
+ Image {
+ icon-name: "webview-filler-symbolic";
+ }
+
+ Label label {
+ styles [
+ "title-2"
+ ]
+
+ halign: center;
+ valign: center;
+ margin-start: 24;
+ margin-top: 24;
+ margin-bottom: 24;
+ margin-end: 24;
+ label: bind template.wellcome_message as ;
+ }
}
+ };
+ }
+ StackPage {
+ name: "data";
- StackPage {
- name: "data";
- child: ScrolledWindow {
- ColumnView data_view {
- show-row-separators: true;
- show-column-separators: true;
- styles ["data-table"]
- vexpand: true;
- }
- };
+ child: ScrolledWindow {
+ ColumnView data_view {
+ show-row-separators: true;
+ show-column-separators: true;
+
+ styles [
+ "data-table"
+ ]
+
+ vexpand: true;
}
+ };
+ }
+
+ StackPage {
+ name: "loading";
- StackPage {
- name: "loading";
- child: Adw.Clamp {
- maximum-size: 32;
+ child: Adw.Clamp {
+ maximum-size: 32;
- Spinner spinner {
- spinning: bind template.is-loading;
- }
- };
+ Spinner spinner {
+ spinning: bind template.is-loading;
}
+ };
+ }
+
+ StackPage {
+ name: "error";
+
+ child: ScrolledWindow {
+ Label status_label {
+ halign: start;
+ valign: start;
+ margin-start: 24;
+ margin-top: 24;
+ margin-bottom: 24;
+ margin-end: 24;
+
+ styles [
+ "error"
+ ]
- StackPage {
- name: "error";
-
- child: ScrolledWindow {
- Label status_label {
- halign: start;
- valign: start;
- margin-start: 24;
- margin-top: 24;
- margin-bottom: 24;
- margin-end: 24;
-
- styles ["error"]
- label: bind template.err_msg;
- }
- };
+ label: bind template.err_msg;
}
+ };
}
-}
\ No newline at end of file
+ }
+}
diff --git a/res/gtk/schema-main.blp b/res/gtk/schema-main.blp
deleted file mode 100644
index 66b945b..0000000
--- a/res/gtk/schema-main.blp
+++ /dev/null
@@ -1,127 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-
-menu primary_menu {
-
- section {
- item {
- custom: "style-switcher";
- }
- }
-
- section {
- item {
- label: _("_New Window");
- action: "app.new-window";
- }
- }
-
- section {
- item {
- label: _("_Preferences");
- action: "app.preferences";
- }
-
- item {
- label: _("_Keyboard Shortcuts");
- action: "win.show-help-overlay";
- }
-
- item {
- label: _("_About");
- action: "app.about";
- }
- }
-}
-
-
-template $PsequelSchemaMain : Gtk.Box {
- orientation: vertical;
- width-request: 920;
- Adw.HeaderBar {
- styles ["flat"]
-
- [title]
- Adw.ViewSwitcher {
- policy: wide;
- stack: stack;
- }
-
- [end]
- MenuButton {
- icon-name: "open-menu-symbolic";
- menu-model: primary_menu;
- popover: Gtk.PopoverMenu {
- menu-model: primary_menu;
-
- [style-switcher]
- $PsequelStyleSwitcher {
-
- }
- };
-
- }
- }
-
- Adw.ViewStack stack {
- hexpand: true;
- vexpand: true;
- Adw.ViewStackPage {
- name: "structure-view";
- icon-name: "library-symbolic";
- title: "Structure";
- child: Stack {
-
- visible-child-name: bind template.view-mode;
- transition-type: slide_left_right;
-
-
- StackPage {
- name: "table";
- child: $PsequelTableStructureView {
- };
- }
-
- StackPage {
- name: "view";
- child: $PsequelViewStructureView {
- };
- }
- };
- }
-
- Adw.ViewStackPage {
- name: "data-view";
- title: "Data";
- icon-name: "object-rows-symbolic";
-
- child: Stack {
-
- visible-child-name: bind template.view-mode;
- transition-type: slide_left_right;
-
-
- StackPage {
- name: "table";
- child: $PsequelTableDataView {
- };
- }
-
- StackPage {
- name: "view";
- child: $PsequelViewDataView {
- };
- }
- };
- }
-
- Adw.ViewStackPage {
- name: "query-editor";
- icon-name: "terminal-symbolic";
- title: "Query";
- child: $PsequelQueryEditor {
- };
- }
- }
-}
diff --git a/res/gtk/schema-sidebar.blp b/res/gtk/schema-sidebar.blp
deleted file mode 100644
index 75f798a..0000000
--- a/res/gtk/schema-sidebar.blp
+++ /dev/null
@@ -1,225 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-template $PsequelSchemaSidebar : Gtk.Box {
- orientation: vertical;
- width-request: 300;
-
- Box {
- margin-start: 8;
- margin-end: 8;
- margin-top: 8;
- margin-bottom: 8;
- hexpand: true;
- spacing: 12;
- Label {
- styles ["title-3"]
- halign: start;
- margin-bottom: 0;
- label: "Schema";
- }
- DropDown dropdown {
- hexpand: true;
- model: bind template.schema-viewmodel as <$PsequelSchemaViewModel>.schemas;
- }
- }
-
-
- Adw.ViewSwitcher tables_views_switcher {
-
- styles ["big-icon"]
- height-request: 40;
- margin-start: 8;
- margin-end: 8;
- margin-top: 4;
- margin-bottom: 8;
- policy: wide;
- stack: sql_views;
- }
-
- Separator {}
-
- Adw.ViewStack sql_views {
- hexpand: true;
- vexpand: true;
- Adw.ViewStackPage {
- icon-name: "table-symbolic";
- name: "table";
-
- child: Box {
- orientation: vertical;
- Box {
- margin-start: 8;
- margin-end: 8;
- margin-top: 4;
- margin-bottom: 8;
-
- Label {
- styles ["title-3"]
- margin-top: 8;
- margin-start: 4;
- halign: start;
- margin-bottom: 0;
- label: "Tables";
- }
-
- Separator {
- hexpand: true;
- styles ["spacer"]
- }
-
- ToggleButton search_btn {
- styles ["flat"]
- tooltip-text: "Search tables";
- icon-name: "loupe-large-symbolic";
-
- toggled => $table_search_reveal();
- }
- }
-
- Revealer {
- transition-duration: 300;
- reveal-child: bind search_btn.active;
- styles ["background"]
- SearchEntry search_table_entry {
- placeholder-text: "Search Tables";
- margin-start: 4;
- margin-bottom: 4;
-
- search-changed => $on_table_search();
- }
-
- }
-
- ScrolledWindow {
- vexpand: true;
- ListView table_list {
- styles ["navigation-sidebar"]
- model: SingleSelection table_selection {
- model: FilterListModel table_model {
- incremental: true;
- model: bind template.table-viewmodel as <$PsequelTableViewModel>.tables;
- filter: StringFilter table_filter {
-
- };
- };
- autoselect: true;
- };
-
- factory: BuilderListItemFactory {
- resource: "/me/ppvan/psequel/gtk/table-listitem.ui";
- };
-
- }
- }
- };
- }
-
- Adw.ViewStackPage {
- icon-name: "category-search-symbolic";
- name: "view";
-
- child: Box {
- orientation: vertical;
- Box {
- margin-start: 8;
- margin-end: 8;
- margin-top: 4;
- margin-bottom: 8;
-
- Label {
- styles ["title-3"]
- margin-top: 8;
- margin-start: 4;
- halign: start;
- margin-bottom: 0;
- label: "Views";
- }
-
- Separator {
- hexpand: true;
- styles ["spacer"]
- }
-
- ToggleButton search_views_btn {
- styles ["flat"]
- tooltip-text: "Search views";
- icon-name: "loupe-large-symbolic";
-
- toggled => $view_search_reveal();
- }
- }
-
- Revealer {
- transition-duration: 300;
- reveal-child: bind search_views_btn.active;
- styles ["background"]
- SearchEntry search_views_entry {
- placeholder-text: "Search Views";
- margin-start: 4;
- margin-bottom: 4;
-
- search-changed => $on_view_search();
- }
-
- }
-
- ScrolledWindow {
- vexpand: true;
- ListView views_list {
- styles ["navigation-sidebar"]
- model: SingleSelection view_selection {
- model: FilterListModel view_model {
- incremental: true;
- model: bind template.view-viewmodel as <$PsequelViewViewModel>.views;
- filter: StringFilter view_filter {
-
- };
- };
- autoselect: true;
- };
-
- factory: BuilderListItemFactory {
- resource: "/me/ppvan/psequel/gtk/view-listitem.ui";
- };
-
- }
- }
- };
- }
- }
-
- Box {
- spacing: 4;
- margin-bottom: 2;
- margin-end: 2;
- margin-start: 4;
-
- Button {
- visible: false;
- styles ["flat"]
- icon-name: "plus-large-symbolic";
- tooltip-text: "Create new table";
- }
-
- Button reload {
- styles ["flat"]
- icon-name: "refresh-large-symbolic";
- tooltip-text: "Reload tables";
-
- clicked => $reload_btn_clicked();
- }
-
- Label {
- hexpand: true;
- }
-
- Button logout {
- styles ["flat"]
- icon-name: "step-out-symbolic";
- tooltip-text: "Logout";
- clicked => $logout_btn_clicked();
- }
-
- }
-}
diff --git a/res/gtk/schema-view.blp b/res/gtk/schema-view.blp
index e41073b..caeb35a 100644
--- a/res/gtk/schema-view.blp
+++ b/res/gtk/schema-view.blp
@@ -1,41 +1,21 @@
using Gtk 4.0;
using Adw 1;
-template $PsequelSchemaView : Adw.Bin {
- hexpand: true;
- vexpand: true;
-
- Box {
- orientation: vertical;
-
- Gtk.Paned paned {
- shrink-end-child: false;
- shrink-start-child: false;
-
- $PsequelSchemaSidebar sidebar {
- width-request: 320;
- }
-
- $PsequelSchemaMain {
-
- width-request: 300;
-
- menu: primary_menu;
- view-mode: bind sidebar.view-mode;
- }
- }
- }
-}
-
-
menu primary_menu {
section {
+ item {
+ custom: "style-switcher";
+ }
+ }
+ section {
item {
label: _("_New Window");
action: "app.new-window";
}
+ }
+ section {
item {
label: _("_Preferences");
action: "app.preferences";
@@ -51,4 +31,401 @@ menu primary_menu {
action: "app.about";
}
}
-}
\ No newline at end of file
+}
+
+template $PsequelSchemaView: Adw.Bin {
+ hexpand: true;
+ vexpand: true;
+
+ Box {
+ orientation: vertical;
+
+ Gtk.Paned paned {
+ shrink-end-child: false;
+ shrink-start-child: false;
+
+ [start]
+ Box sidebar {
+ orientation: vertical;
+ width-request: 300;
+
+ Box {
+ margin-start: 8;
+ margin-end: 8;
+ margin-top: 8;
+ margin-bottom: 8;
+ hexpand: true;
+ spacing: 12;
+
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ halign: start;
+ margin-bottom: 0;
+ label: "Schema";
+ }
+
+ DropDown dropdown {
+ hexpand: true;
+ model: bind template.schema-viewmodel as <$PsequelSchemaViewModel>.schemas;
+ }
+ }
+
+ Adw.ViewSwitcher tables_views_switcher {
+ styles [
+ "big-icon"
+ ]
+
+ height-request: 40;
+ margin-start: 8;
+ margin-end: 8;
+ margin-top: 4;
+ margin-bottom: 8;
+ policy: wide;
+ stack: sql_views;
+ }
+
+ Separator {}
+
+ Adw.ViewStack sql_views {
+ hexpand: true;
+ vexpand: true;
+
+ Adw.ViewStackPage {
+ icon-name: "table-symbolic";
+ name: "table";
+
+ child: Box {
+ orientation: vertical;
+
+ Box {
+ margin-start: 8;
+ margin-end: 8;
+ margin-top: 4;
+ margin-bottom: 8;
+
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ margin-top: 8;
+ margin-start: 4;
+ halign: start;
+ margin-bottom: 0;
+ label: "Tables";
+ }
+
+ Label {
+ styles [
+ "badges"
+ ]
+
+ margin-top: 8;
+ margin-start: 4;
+ halign: center;
+ valign: center;
+ margin-bottom: 0;
+ label: bind table_model.n-items;
+ }
+
+ Separator {
+ hexpand: true;
+
+ styles [
+ "spacer"
+ ]
+ }
+
+ ToggleButton search_btn {
+ styles [
+ "flat"
+ ]
+
+ tooltip-text: "Search tables";
+ icon-name: "loupe-large-symbolic";
+ toggled => $table_search_reveal();
+ }
+ }
+
+ Revealer {
+ transition-duration: 300;
+ reveal-child: bind search_btn.active;
+
+ styles [
+ "background"
+ ]
+
+ SearchEntry search_table_entry {
+ placeholder-text: "Search Tables";
+ margin-start: 4;
+ margin-bottom: 4;
+ search-changed => $on_table_search();
+ }
+ }
+
+ ScrolledWindow {
+ vexpand: true;
+
+ ListView table_list {
+ styles [
+ "navigation-sidebar"
+ ]
+
+ model: SingleSelection table_selection {
+ model: FilterListModel table_model {
+ incremental: true;
+ model: bind template.table-viewmodel as <$PsequelTableViewModel>.tables;
+
+ filter: StringFilter table_filter {};
+ };
+
+ autoselect: true;
+ };
+
+ factory: BuilderListItemFactory {
+ resource: "/me/ppvan/psequel/gtk/table-listitem.ui";
+ };
+ }
+ }
+ };
+ }
+
+ Adw.ViewStackPage {
+ icon-name: "category-search-symbolic";
+ name: "view";
+
+ child: Box {
+ orientation: vertical;
+
+ Box {
+ margin-start: 8;
+ margin-end: 8;
+ margin-top: 4;
+ margin-bottom: 8;
+
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ margin-top: 8;
+ margin-start: 4;
+ halign: start;
+ margin-bottom: 0;
+ label: "Views";
+ }
+
+ Label {
+ styles [
+ "badges"
+ ]
+
+ margin-top: 8;
+ margin-start: 4;
+ halign: center;
+ valign: center;
+ margin-bottom: 0;
+ label: bind view_model.n-items;
+ }
+
+ Separator {
+ hexpand: true;
+
+ styles [
+ "spacer"
+ ]
+ }
+
+ ToggleButton search_views_btn {
+ styles [
+ "flat"
+ ]
+
+ tooltip-text: "Search views";
+ icon-name: "loupe-large-symbolic";
+ toggled => $view_search_reveal();
+ }
+ }
+
+ Revealer {
+ transition-duration: 300;
+ reveal-child: bind search_views_btn.active;
+
+ styles [
+ "background"
+ ]
+
+ SearchEntry search_views_entry {
+ placeholder-text: "Search Views";
+ margin-start: 4;
+ margin-bottom: 4;
+ search-changed => $on_view_search();
+ }
+ }
+
+ ScrolledWindow {
+ vexpand: true;
+
+ ListView views_list {
+ styles [
+ "navigation-sidebar"
+ ]
+
+ model: SingleSelection view_selection {
+ model: FilterListModel view_model {
+ incremental: true;
+ model: bind template.view-viewmodel as <$PsequelViewViewModel>.views;
+
+ filter: StringFilter view_filter {};
+ };
+
+ autoselect: true;
+ };
+
+ factory: BuilderListItemFactory {
+ resource: "/me/ppvan/psequel/gtk/view-listitem.ui";
+ };
+ }
+ }
+ };
+ }
+ }
+
+ Box {
+ spacing: 4;
+ margin-bottom: 2;
+ margin-end: 2;
+ margin-start: 4;
+
+ Button {
+ visible: false;
+
+ styles [
+ "flat"
+ ]
+
+ icon-name: "plus-large-symbolic";
+ tooltip-text: "Create new table";
+ }
+
+ Button reload {
+ styles [
+ "flat"
+ ]
+
+ icon-name: "refresh-large-symbolic";
+ tooltip-text: "Reload tables";
+ clicked => $reload_btn_clicked();
+ }
+
+ Label {
+ hexpand: true;
+ }
+
+ Button logout {
+ styles [
+ "flat"
+ ]
+
+ icon-name: "step-out-symbolic";
+ tooltip-text: "Logout";
+ clicked => $logout_btn_clicked();
+ }
+ }
+ }
+
+ [end]
+ Box main-view {
+ orientation: vertical;
+ width-request: 920;
+
+ Adw.HeaderBar {
+ styles [
+ "flat"
+ ]
+
+ [title]
+ Adw.ViewSwitcher {
+ policy: wide;
+ stack: stack;
+ }
+
+ [end]
+ MenuButton {
+ icon-name: "open-menu-symbolic";
+ menu-model: primary_menu;
+
+ popover: Gtk.PopoverMenu {
+ menu-model: primary_menu;
+
+ [style-switcher]
+ $PsequelStyleSwitcher {}
+ };
+ }
+ }
+
+ Adw.ViewStack stack {
+ hexpand: true;
+ vexpand: true;
+
+ Adw.ViewStackPage {
+ name: "structure-view";
+ icon-name: "library-symbolic";
+ title: "Structure";
+
+ child: Stack {
+ visible-child-name: bind template.view-mode;
+ transition-type: slide_left_right;
+
+ StackPage {
+ name: "table";
+
+ child: $PsequelTableStructureView {};
+ }
+
+ StackPage {
+ name: "view";
+
+ child: $PsequelViewStructureView {};
+ }
+ };
+ }
+
+ Adw.ViewStackPage {
+ name: "data-view";
+ title: "Data";
+ icon-name: "object-rows-symbolic";
+
+ child: Stack {
+ visible-child-name: bind template.view-mode;
+ transition-type: slide_left_right;
+
+ StackPage {
+ name: "table";
+
+ child: $PsequelTableDataView {};
+ }
+
+ StackPage {
+ name: "view";
+
+ child: $PsequelViewDataView {};
+ }
+ };
+ }
+
+ Adw.ViewStackPage {
+ name: "query-editor";
+ icon-name: "terminal-symbolic";
+ title: "Query";
+
+ child: $PsequelQueryEditor {};
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/res/gtk/style-switcher.blp b/res/gtk/style-switcher.blp
index 38d4096..fb922e9 100644
--- a/res/gtk/style-switcher.blp
+++ b/res/gtk/style-switcher.blp
@@ -1,8 +1,6 @@
using Gtk 4.0;
-
// Adapt from https://gitlab.gnome.org/raggesilver/blackbox/-/blob/dc3417f2185e539adcaf6e0775ba83bb238b1d14/src/gtk/style-switcher.ui
-
-template $PsequelStyleSwitcher : Widget {
+template $PsequelStyleSwitcher: Widget {
Box box {
orientation: horizontal;
homogeneous: true;
@@ -91,4 +89,4 @@ template $PsequelStyleSwitcher : Widget {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/res/gtk/style.css b/res/gtk/style.css
index 855e3ee..39d17f8 100644
--- a/res/gtk/style.css
+++ b/res/gtk/style.css
@@ -8,6 +8,14 @@
margin-bottom: 32px;
}
+.badges {
+ font-size: 14px;
+ padding: 4px 6px;
+ font-weight: bold;
+ background-color: @sidebar_bg_color;
+ border-radius: 8px;
+}
+
.big-icon image {
-gtk-icon-size: 20px;
diff --git a/res/gtk/table-cols.blp b/res/gtk/table-cols.blp
index 134bd0f..14e5cfc 100644
--- a/res/gtk/table-cols.blp
+++ b/res/gtk/table-cols.blp
@@ -1,18 +1,20 @@
using Gtk 4.0;
using Adw 1;
-template $PsequelTableColInfo : Adw.Bin {
- ScrolledWindow {
- ColumnView view {
+template $PsequelTableColInfo: Adw.Bin {
+ ScrolledWindow {
+ ColumnView view {
+ model: NoSelection {
+ model: bind template.columns;
+ };
- model: NoSelection {
- model: bind template.columns;
- };
+ styles [
+ "data-table"
+ ]
- styles ["data-table"]
- show-column-separators: true;
- show-row-separators: true;
- vexpand: true;
- }
+ show-column-separators: true;
+ show-row-separators: true;
+ vexpand: true;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/res/gtk/table-data-view.blp b/res/gtk/table-data-view.blp
index 597a36c..eef15dd 100644
--- a/res/gtk/table-data-view.blp
+++ b/res/gtk/table-data-view.blp
@@ -1,89 +1,99 @@
using Gtk 4.0;
-
using Adw 1;
-template $PsequelTableDataView : Gtk.Box {
-
- width-request: 900;
- height-request: 600;
+template $PsequelTableDataView: Gtk.Box {
+ width-request: 900;
+ height-request: 600;
+ orientation: vertical;
+ spacing: 4;
+ margin-start: 8;
+ margin-top: 8;
+ margin-end: 8;
+ margin-bottom: 8;
- orientation: vertical;
- spacing: 4;
- margin-start: 8;
+ Box {
+ visible: false;
+ spacing: 12;
margin-top: 8;
- margin-end: 8;
margin-bottom: 8;
-
- Box {
- visible: false;
- spacing: 12;
- margin-top: 8;
- margin-bottom: 8;
- Entry filter_entry {
- hexpand: true;
- placeholder-text: "WHERE clause";
- // activate => $on_entry_activated();
- }
-
- Button filter_btn {
- styles ["suggested-action"]
- label: "Filter";
-
- // clicked => $filter_query();
- }
+
+ Entry filter_entry {
+ hexpand: true;
+ placeholder-text: "WHERE clause";
+ // activate => $on_entry_activated();
+ }
+
+ Button filter_btn {
+ styles [
+ "suggested-action"
+ ]
+
+ label: "Filter";
+ // clicked => $filter_query();
}
+ }
+
+ $PsequelQueryResults query_results {
+ current-relation: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.current-relation;
+ is-loading: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.is-loading;
+ err-msg: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.err-msg;
+ }
+
+ Box {
+ spacing: 8;
+ // height-request: 40;
+ Button {
+ visible: false;
+
+ styles [
+ "flat"
+ ]
+
+ icon-name: "plus-large-symbolic";
+ tooltip-text: "Insert Row";
+ }
+
+ Button reload {
+ styles [
+ "flat"
+ ]
+
+ icon-name: "refresh-large-symbolic";
+ tooltip-text: "Reload Data";
+ clicked => $reload_data();
+ }
+
+ // Separator {}
+ Label status_label {
+ label: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.row-ranges;
+ halign: start;
+ }
+
+ // Separator {}
+ Label {
+ hexpand: true;
+ }
+
+ Button left_page {
+ styles [
+ "flat"
+ ]
- $PsequelQueryResults query_results {
- current-relation: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.current-relation;
- is-loading: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.is-loading;
- err-msg: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.err-msg;
+ sensitive: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.has-pre-page;
+ icon-name: "left-large-symbolic";
+ tooltip-text: "Last Page";
+ clicked => $pre_page();
}
+ Button right_page {
+ styles [
+ "flat"
+ ]
- Box {
- spacing: 8;
- // height-request: 40;
- Button {
- visible: false;
- styles ["flat"]
- icon-name: "plus-large-symbolic";
- tooltip-text: "Insert Row";
- }
-
- Button reload {
- styles ["flat"]
- icon-name: "refresh-large-symbolic";
- tooltip-text: "Reload Data";
- clicked => $reload_data();
- }
-
- // Separator {}
-
- Label status_label {
- label: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.row-ranges;
- halign: start;
- }
-
- // Separator {}
-
- Label {
- hexpand: true;
- }
-
- Button left_page {
- styles ["flat"]
- sensitive: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.has-pre-page;
- icon-name: "left-large-symbolic";
- tooltip-text: "Last Page";
- clicked => $pre_page();
- }
-
- Button right_page {
- styles ["flat"]
- sensitive: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.has-next-page;
- icon-name: "right-large-symbolic";
- tooltip-text: "Next Page";
- clicked => $next_page();
- }
+ sensitive: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.has-next-page;
+ icon-name: "right-large-symbolic";
+ tooltip-text: "Next Page";
+ clicked => $next_page();
}
-}
\ No newline at end of file
+ }
+}
diff --git a/res/gtk/table-fk.blp b/res/gtk/table-fk.blp
index a299e5c..87497ba 100644
--- a/res/gtk/table-fk.blp
+++ b/res/gtk/table-fk.blp
@@ -1,18 +1,20 @@
using Gtk 4.0;
using Adw 1;
-template $PsequelTableFKInfo : Adw.Bin {
- ScrolledWindow {
- ColumnView view {
+template $PsequelTableFKInfo: Adw.Bin {
+ ScrolledWindow {
+ ColumnView view {
+ model: NoSelection {
+ model: bind template.fks;
+ };
- model: NoSelection {
- model: bind template.fks;
- };
+ styles [
+ "data-table"
+ ]
- styles ["data-table"]
- show-column-separators: true;
- show-row-separators: true;
- vexpand: true;
- }
+ show-column-separators: true;
+ show-row-separators: true;
+ vexpand: true;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/res/gtk/table-index.blp b/res/gtk/table-index.blp
index 8c98dec..b1a5d98 100644
--- a/res/gtk/table-index.blp
+++ b/res/gtk/table-index.blp
@@ -1,18 +1,20 @@
using Gtk 4.0;
using Adw 1;
-template $PsequelTableIndexInfo : Adw.Bin {
- ScrolledWindow {
- ColumnView view {
+template $PsequelTableIndexInfo: Adw.Bin {
+ ScrolledWindow {
+ ColumnView view {
+ model: NoSelection {
+ model: bind template.indexes;
+ };
- model: NoSelection {
- model: bind template.indexes;
- };
+ styles [
+ "data-table"
+ ]
- styles ["data-table"]
- show-column-separators: true;
- show-row-separators: true;
- vexpand: true;
- }
+ show-column-separators: true;
+ show-row-separators: true;
+ vexpand: true;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/res/gtk/table-listitem.blp b/res/gtk/table-listitem.blp
index 1c5f136..1e03294 100644
--- a/res/gtk/table-listitem.blp
+++ b/res/gtk/table-listitem.blp
@@ -1,19 +1,19 @@
using Gtk 4.0;
template ListItem {
- child: Box {
- orientation: horizontal;
- spacing: 12;
+ child: Box {
+ orientation: horizontal;
+ spacing: 12;
- Image {
- icon-name: "table-symbolic";
- }
+ Image {
+ icon-name: "table-symbolic";
+ }
- Label {
- label: bind template.item as <$PsequelTable>.name;
- halign: start;
- ellipsize: end;
- single-line-mode: true;
- }
- };
-}
\ No newline at end of file
+ Label {
+ label: bind template.item as <$PsequelTable>.name;
+ halign: start;
+ ellipsize: end;
+ single-line-mode: true;
+ }
+ };
+}
diff --git a/res/gtk/table-row.blp b/res/gtk/table-row.blp
index 055003c..0f2034b 100644
--- a/res/gtk/table-row.blp
+++ b/res/gtk/table-row.blp
@@ -1,2 +1 @@
using Gtk 4.0;
-
diff --git a/res/gtk/table-structure-view.blp b/res/gtk/table-structure-view.blp
index 1d4411e..a2d02b2 100644
--- a/res/gtk/table-structure-view.blp
+++ b/res/gtk/table-structure-view.blp
@@ -2,44 +2,50 @@ using Gtk 4.0;
using Gio 2.0;
using Adw 1;
-template $PsequelTableStructureView : Gtk.Box {
- orientation: vertical;
- spacing: 4;
- margin-start: 8;
- margin-top: 12;
- margin-bottom: 12;
-
- // width-request: 960;
- // height-request: 800;
-
- Label {
- styles ["title-3"]
- label: "Column Info";
- halign: start;
- }
-
- $PsequelTableColInfo columns {
- columns: bind template.columns;
- }
-
-
- Label {
- styles ["title-3"]
- label: "Indexes";
- halign: start;
- }
-
- $PsequelTableIndexInfo indexes {
- indexes: bind template.indexes;
- }
-
- Label {
- styles ["title-3"]
- label: "Foreign Keys";
- halign: start;
- }
-
- $PsequelTableFKInfo foreign_keys {
- fks: bind template.fks;
- }
+template $PsequelTableStructureView: Gtk.Box {
+ orientation: vertical;
+ spacing: 4;
+ margin-start: 8;
+ margin-top: 12;
+ margin-bottom: 12;
+ // width-request: 960;
+ // height-request: 800;
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ label: "Column Info";
+ halign: start;
+ }
+
+ $PsequelTableColInfo columns {
+ columns: bind template.columns;
+ }
+
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ label: "Indexes";
+ halign: start;
+ }
+
+ $PsequelTableIndexInfo indexes {
+ indexes: bind template.indexes;
+ }
+
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ label: "Foreign Keys";
+ halign: start;
+ }
+
+ $PsequelTableFKInfo foreign_keys {
+ fks: bind template.fks;
+ }
}
diff --git a/res/gtk/view-data-view.blp b/res/gtk/view-data-view.blp
index 2b9fe28..a5a6df0 100644
--- a/res/gtk/view-data-view.blp
+++ b/res/gtk/view-data-view.blp
@@ -1,86 +1,98 @@
using Gtk 4.0;
-
using Adw 1;
-template $PsequelViewDataView : Gtk.Box {
-
- width-request: 900;
- height-request: 600;
+template $PsequelViewDataView: Gtk.Box {
+ width-request: 900;
+ height-request: 600;
+ orientation: vertical;
+ spacing: 4;
+ margin-start: 8;
+ margin-top: 8;
+ margin-end: 8;
+ margin-bottom: 8;
- orientation: vertical;
- spacing: 4;
- margin-start: 8;
+ Box {
+ visible: false;
+ spacing: 12;
margin-top: 8;
- margin-end: 8;
margin-bottom: 8;
-
- Box {
- visible: false;
- spacing: 12;
- margin-top: 8;
- margin-bottom: 8;
- Entry filter_entry {
- hexpand: true;
- placeholder-text: "WHERE clause";
- // activate => $on_entry_activated();
- }
-
- Button filter_btn {
- styles ["suggested-action"]
- label: "Filter";
-
- // clicked => $filter_query();
- }
+
+ Entry filter_entry {
+ hexpand: true;
+ placeholder-text: "WHERE clause";
+ // activate => $on_entry_activated();
+ }
+
+ Button filter_btn {
+ styles [
+ "suggested-action"
+ ]
+
+ label: "Filter";
+ // clicked => $filter_query();
}
+ }
+
+ $PsequelQueryResults query_results {
+ current-relation: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.current-relation;
+ is-loading: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.is-loading;
+ err-msg: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.err-msg;
+ }
+
+ Box {
+ spacing: 8;
+ // height-request: 40;
+ Button {
+ visible: false;
+
+ styles [
+ "flat"
+ ]
+
+ icon-name: "plus-large-symbolic";
+ tooltip-text: "Insert Row";
+ }
+
+ Button reload {
+ styles [
+ "flat"
+ ]
+
+ icon-name: "refresh-large-symbolic";
+ tooltip-text: "Reload Data";
+ clicked => $reload_data();
+ }
+
+ Separator {}
+
+ Label status_label {
+ label: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.row-ranges;
+ hexpand: true;
+ halign: start;
+ }
+
+ Separator {}
+
+ Button left_page {
+ styles [
+ "flat"
+ ]
- $PsequelQueryResults query_results {
- current-relation: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.current-relation;
- is-loading: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.is-loading;
- err-msg: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.err-msg;
+ icon-name: "left-large-symbolic";
+ tooltip-text: "Last Page";
+ sensitive: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.has-pre-page;
+ clicked => $pre_page();
}
+ Button right_page {
+ styles [
+ "flat"
+ ]
- Box {
- spacing: 8;
- // height-request: 40;
- Button {
- visible: false;
- styles ["flat"]
- icon-name: "plus-large-symbolic";
- tooltip-text: "Insert Row";
- }
-
- Button reload {
- styles ["flat"]
- icon-name: "refresh-large-symbolic";
- tooltip-text: "Reload Data";
- clicked => $reload_data();
- }
-
- Separator {}
-
- Label status_label {
- label: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.row-ranges;
- hexpand: true;
- halign: start;
- }
-
- Separator {}
-
- Button left_page {
- styles ["flat"]
- icon-name: "left-large-symbolic";
- tooltip-text: "Last Page";
- sensitive: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.has-pre-page;
- clicked => $pre_page();
- }
-
- Button right_page {
- styles ["flat"]
- icon-name: "right-large-symbolic";
- tooltip-text: "Next Page";
- sensitive: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.has-next-page;
- clicked => $next_page();
- }
+ icon-name: "right-large-symbolic";
+ tooltip-text: "Next Page";
+ sensitive: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.has-next-page;
+ clicked => $next_page();
}
-}
\ No newline at end of file
+ }
+}
diff --git a/res/gtk/view-listitem.blp b/res/gtk/view-listitem.blp
index be15143..91364ea 100644
--- a/res/gtk/view-listitem.blp
+++ b/res/gtk/view-listitem.blp
@@ -1,19 +1,19 @@
using Gtk 4.0;
template ListItem {
- child: Box {
- orientation: horizontal;
- spacing: 12;
+ child: Box {
+ orientation: horizontal;
+ spacing: 12;
- Image {
- icon-name: "category-search-symbolic";
- }
+ Image {
+ icon-name: "category-search-symbolic";
+ }
- Label {
- label: bind template.item as <$PsequelView>.name;
- halign: start;
- ellipsize: end;
- single-line-mode: true;
- }
- };
-}
\ No newline at end of file
+ Label {
+ label: bind template.item as <$PsequelView>.name;
+ halign: start;
+ ellipsize: end;
+ single-line-mode: true;
+ }
+ };
+}
diff --git a/res/gtk/view-structure-view.blp b/res/gtk/view-structure-view.blp
index ae1dd18..3a54af0 100644
--- a/res/gtk/view-structure-view.blp
+++ b/res/gtk/view-structure-view.blp
@@ -1,34 +1,36 @@
using Gtk 4.0;
-
-template $PsequelViewStructureView : Box {
- orientation: vertical;
- spacing: 4;
- margin-start: 8;
- margin-top: 12;
- margin-bottom: 12;
-
- // width-request: 960;
- // height-request: 800;
-
- Label {
- styles ["title-3"]
- label: "Column Info";
- halign: start;
- }
-
- $PsequelTableColInfo columns {
- columns: bind template.columns;
- }
-
-
- Label {
- styles ["title-3"]
- label: "Indexes";
- halign: start;
- }
-
- $PsequelTableIndexInfo indexes {
- indexes: bind template.indexes;
- }
-}
\ No newline at end of file
+template $PsequelViewStructureView: Box {
+ orientation: vertical;
+ spacing: 4;
+ margin-start: 8;
+ margin-top: 12;
+ margin-bottom: 12;
+ // width-request: 960;
+ // height-request: 800;
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ label: "Column Info";
+ halign: start;
+ }
+
+ $PsequelTableColInfo columns {
+ columns: bind template.columns;
+ }
+
+ Label {
+ styles [
+ "title-3"
+ ]
+
+ label: "Indexes";
+ halign: start;
+ }
+
+ $PsequelTableIndexInfo indexes {
+ indexes: bind template.indexes;
+ }
+}
diff --git a/res/gtk/window.blp b/res/gtk/window.blp
index 6cfca66..a29db79 100644
--- a/res/gtk/window.blp
+++ b/res/gtk/window.blp
@@ -1,33 +1,29 @@
using Gtk 4.0;
using Adw 1;
-template $PsequelWindow : Adw.ApplicationWindow {
- default-width: 1280;
- default-height: 800;
-
- width-request: 1280;
- height-request: 800;
- title: "Psequel";
+template $PsequelWindow: Adw.ApplicationWindow {
+ default-width: 1280;
+ default-height: 800;
+ width-request: 1280;
+ height-request: 800;
+ title: "Psequel";
+ Adw.ToastOverlay overlay {
+ Stack stack {
+ transition-type: slide_up_down;
+ visible-child-name: bind template.navigation as <$PsequelNavigationService>.current-view;
- Adw.ToastOverlay overlay {
- Stack stack {
- transition-type: slide_up_down;
- visible-child-name: bind template.navigation as <$PsequelNavigationService>.current-view;
+ StackPage {
+ name: "connection-view";
- StackPage {
- name: "connection-view";
- child: $PsequelConnectionView {
- };
- }
+ child: $PsequelConnectionView {};
+ }
- StackPage query-view {
- name: "query-view";
- child: $PsequelSchemaView {
- // schema-viewmodel: bind template.schema-viewmodel;
- // request_logout => $on_request_logout ();
- };
- }
- }
+ StackPage query-view {
+ name: "query-view";
+
+ child: $PsequelSchemaView {};
+ }
}
+ }
}
diff --git a/res/meson.build b/res/meson.build
index 44b8620..499de23 100644
--- a/res/meson.build
+++ b/res/meson.build
@@ -10,9 +10,6 @@ blueprints = custom_target('blueprints',
input: files(
'gtk/style-switcher.blp',
-
- 'gtk/connection-form.blp',
- 'gtk/connection-sidebar.blp',
'gtk/connection-view.blp',
'gtk/connection-row.blp',
'gtk/connection-listitem.blp',
@@ -21,8 +18,6 @@ blueprints = custom_target('blueprints',
'gtk/query-results.blp',
'gtk/schema-view.blp',
- 'gtk/schema-sidebar.blp',
- 'gtk/schema-main.blp',
'gtk/table-data-view.blp',
'gtk/view-data-view.blp',
diff --git a/res/psequel.gresource.xml b/res/psequel.gresource.xml
index cb9359a..9bb6095 100644
--- a/res/psequel.gresource.xml
+++ b/res/psequel.gresource.xml
@@ -3,15 +3,10 @@
gtk/style.css
gtk/style-switcher.ui
- gtk/connection-form.ui
- gtk/connection-sidebar.ui
gtk/connection-view.ui
gtk/connection-row.ui
gtk/connection-listitem.ui
-
- gtk/schema-main.ui
gtk/schema-view.ui
- gtk/schema-sidebar.ui
gtk/query-editor.ui
gtk/query-results.ui
diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png
index 1a8bd5b..56b5baf 100644
Binary files a/screenshots/screenshot.png and b/screenshots/screenshot.png differ
diff --git a/screenshots/screenshot1.png b/screenshots/screenshot1.png
index 4c2c8ab..380e488 100644
Binary files a/screenshots/screenshot1.png and b/screenshots/screenshot1.png differ
diff --git a/screenshots/screenshot2.png b/screenshots/screenshot2.png
index 106eb44..f633033 100644
Binary files a/screenshots/screenshot2.png and b/screenshots/screenshot2.png differ
diff --git a/screenshots/screenshot3.png b/screenshots/screenshot3.png
index f9e62c8..440ca8d 100644
Binary files a/screenshots/screenshot3.png and b/screenshots/screenshot3.png differ
diff --git a/screenshots/screenshot4.png b/screenshots/screenshot4.png
index 1a8bd5b..9196496 100644
Binary files a/screenshots/screenshot4.png and b/screenshots/screenshot4.png differ
diff --git a/src/application.vala b/src/application.vala
index 6f7be87..1f9c3c2 100644
--- a/src/application.vala
+++ b/src/application.vala
@@ -34,8 +34,6 @@ namespace Psequel {
* Static field for easy access in other places.
* If need to create many application instance (rarely happens) reconsider this approach.
*/
- public static Application app;
- public static Settings settings;
public static ThreadPool background;
public int color_scheme { get; set; }
@@ -74,22 +72,24 @@ namespace Psequel {
base.startup ();
GtkSource.init ();
set_up_logging ();
- debug ("Begin to load resources");
- Application.app = this;
- Application.settings = new Settings (this.application_id);
+ var settings = new Settings (this.application_id);
settings.bind ("color-scheme", this, "color_scheme", SettingsBindFlags.GET);
this.notify["color-scheme"].connect (update_color_scheme);
- try {
+ var container = Container.instance ();
+ container.register (settings);
+ container.register (this);
+ debug ("Begin to load resources");
+ try {
// Don't change the max_thread because libpq did not support many query with 1 connection.
background = new ThreadPool.with_owned_data ((worker) => {
worker.run ();
}, 1, false);
} catch (ThreadError err) {
debug (err.message);
- return_if_reached ();
+ assert_not_reached ();
}
debug ("Resources loaded");
}
@@ -141,13 +141,9 @@ namespace Psequel {
typeof (Psequel.StyleSwitcher).ensure ();
typeof (Psequel.ConnectionViewModel).ensure ();
typeof (Psequel.SchemaView).ensure ();
- typeof (Psequel.SchemaSidebar).ensure ();
- typeof (Psequel.SchemaMain).ensure ();
typeof (Psequel.ConnectionRow).ensure ();
typeof (Psequel.ConnectionView).ensure ();
- typeof (Psequel.ConnectionSidebar).ensure ();
- typeof (Psequel.ConnectionForm).ensure ();
typeof (Psequel.QueryResults).ensure ();
typeof (Psequel.QueryEditor).ensure ();
typeof (Psequel.TableStructureView).ensure ();
@@ -206,26 +202,28 @@ namespace Psequel {
private Window new_window () {
// give temp access because window is not created yet
- Window.temp = create_viewmodels ();
- var window = new Window (this, Window.temp);
- // Window.temp = null;
+ create_viewmodels ();
+ var window = new Window (this);
return window;
}
private Container create_viewmodels () {
- var container = new Container ();
+ var container = Container.instance ();
+
+ var settings = autowire ();
// services
var sql_service = new SQLService (Application.background);
var schema_service = new SchemaService (sql_service);
- var repository = new ConnectionRepository (Application.settings);
+ var connection_repo = new ConnectionRepository (settings);
+ var query_repo = new QueryRepository (settings);
var navigation = new NavigationService ();
var export = new ExportService ();
var completer = new CompleterService (sql_service);
// viewmodels
- var conn_vm = new ConnectionViewModel (repository, sql_service, navigation);
+ var conn_vm = new ConnectionViewModel (connection_repo, sql_service, navigation);
var sche_vm = new SchemaViewModel (schema_service);
var table_vm = new TableViewModel (sql_service);
var view_vm = new ViewViewModel (sql_service);
@@ -233,14 +231,14 @@ namespace Psequel {
var view_structure_vm = new ViewStructureViewModel (sql_service);
var table_data_vm = new TableDataViewModel (sql_service);
var view_data_vm = new ViewDataViewModel (sql_service);
- var query_history_vm = new QueryHistoryViewModel (sql_service);
+ var query_history_vm = new QueryHistoryViewModel (sql_service, query_repo);
var query_vm = new QueryViewModel (query_history_vm);
container.register (sql_service);
container.register (completer);
container.register (schema_service);
container.register (export);
- container.register (repository);
+ container.register (connection_repo);
container.register (navigation);
container.register (conn_vm);
container.register (sche_vm);
diff --git a/src/meson.build b/src/meson.build
index 4c4da12..9c13ad9 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,30 +1,5 @@
# Source code
-psequel_sources = [
- 'application.vala',
- 'ui/connection/ConnectionForm.vala',
- 'ui/connection/ConnectionSidebar.vala',
- 'ui/connection/ConnectionView.vala',
-
-
- 'ui/schema/SchemaView.vala',
- 'ui/schema/SchemaSidebar.vala',
- 'ui/schema/SchemaMain.vala',
- 'ui/schema/QueryResult.vala',
- 'ui/schema/TableStructureView.vala',
- 'ui/schema/ViewStructureView.vala',
- 'ui/schema/TableColumnInfo.vala',
- 'ui/schema/TableForeignKeyInfo.vala',
- 'ui/schema/TableIndexInfo.vala',
- 'ui/schema/TableDataView.vala',
- 'ui/schema/ViewDataView.vala',
-
- 'ui/editor/QueryEditor.vala',
- 'ui/editor/SQLCompletionProvider.vala',
-
- 'ui/widgets/StyleSwitcher.vala',
- 'ui/Window.vala',
- 'ui/PreferencesWindow.vala',
-
+psequel_sources = files([
'models/Connection.vala',
'models/Schema.vala',
'models/Relation.vala',
@@ -33,7 +8,7 @@ psequel_sources = [
# 'models/utils.vala',
'services/SQLService.vala',
- 'services/CompleterService.vala',
+ 'services/SQLCompletionService.vala',
'services/ExportService.vala',
'services/NavigationService.vala',
'services/ConnectionService.vala',
@@ -60,15 +35,38 @@ psequel_sources = [
'utils/ObservableList.vala',
'utils/ValueConverter.vala',
'utils/Event.vala',
- 'utils/helpers.vala',
'utils/logging.vala',
'utils/errors.vala',
-]
+ 'utils/types.vala',
+])
+ui_sources = files([
+ 'ui/views/ConnectionView.vala',
+ 'ui/views/SchemaView.vala',
+ 'ui/schema/QueryResult.vala',
+ 'ui/schema/TableStructureView.vala',
+ 'ui/schema/ViewStructureView.vala',
+ 'ui/schema/TableColumnInfo.vala',
+ 'ui/schema/TableForeignKeyInfo.vala',
+ 'ui/schema/TableIndexInfo.vala',
+ 'ui/schema/TableDataView.vala',
+ 'ui/schema/ViewDataView.vala',
+
+ 'ui/editor/QueryEditor.vala',
+
+ 'ui/widgets/StyleSwitcher.vala',
+ 'ui/Window.vala',
+ 'ui/PreferencesWindow.vala',
+
+ 'utils/helpers.vala',
+
+])
# add_project_arguments(['--gresourcesdir', 'res/' ], language: 'vala')
# add_project_arguments(['--enable-experimental' ], language: 'vala')
+# Create config data, this will create a config.h file, then be bind to Config class.
+
conf = configuration_data()
conf.set_quoted('APP_ID', app_id)
conf.set_quoted('APP_NAME', 'Psequel')
@@ -92,17 +90,19 @@ psequel_deps = [
]
+
add_project_arguments(
'-include', 'config.h',
'-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()),
language: 'c'
)
+psequel_sources_main = psequel_sources + ui_sources + files(['application.vala'])
-executable('psequel', [blueprint_hack, psequel_resources, psequel_sources],
+executable('psequel', [blueprint_hack, psequel_resources, psequel_sources_main],
dependencies: psequel_deps,
vala_args: [
'--gresourcesdir=' + blueprints_output_dir,
],
install: true,
-)
+)
\ No newline at end of file
diff --git a/src/models/Connection.vala b/src/models/Connection.vala
index 0cdc9c3..21df27b 100644
--- a/src/models/Connection.vala
+++ b/src/models/Connection.vala
@@ -26,6 +26,7 @@ namespace Psequel {
public class Connection : Object, Json.Serializable {
public const string DEFAULT = "";
+ public const string SCHEME = "postgresql";
public string name { get; set; default = DEFAULT; }
public string host { get; set; default = DEFAULT; }
@@ -35,6 +36,8 @@ namespace Psequel {
public string database { get; set; default = DEFAULT; }
public bool use_ssl { get; set; default = false; }
+ public string options { get; set; default = DEFAULT; }
+
public Connection (string name = "New Connection") {
this._name = name;
@@ -47,23 +50,28 @@ namespace Psequel {
*/
public string url_form () {
// postgresql://[user[:password]@][host][:port][/dbname][?param1=value1&...]
- var builder = new StringBuilder ("postgres://");
- if (user != DEFAULT) {
- builder.append (user);
- if (password != DEFAULT) {
- builder.append (@":$password@");
- } else {
- builder.append ("@");
- }
+
+ var parsed_port = 5432;
+ if (!int.try_parse (port, out parsed_port, null, 10)) {
+ debug ("Parse port error: defautl to 5432");
+ }
+
+
+ var safe_user = user != DEFAULT ? user : "postgres";
+ var safe_password = password != DEFAULT ? password : "postgres";
+ var safe_host = host != DEFAULT ? host : "localhost"; // TODO IPv6 check
+ var safe_port = port != DEFAULT ? parsed_port : 5432;
+ var safe_db = database != DEFAULT ? @"/$database" : "/postgres";
+
+ var safe_options = use_ssl ? "sslmode=required" : "sslmode=disable";
+ if (options != DEFAULT) {
+ safe_options = @"$safe_options&$options";
}
- builder.append (host != DEFAULT ? host : "127.0.0.1");
- builder.append (port != DEFAULT ? @":$port" : ":5432");
- builder.append (database != DEFAULT ? @"/$database" : "/postgres");
- builder.append (use_ssl ? "?sslmode=require" : "?sslmode=disable");
+ var url = Uri.join_with_user (UriFlags.NONE, SCHEME, safe_user, safe_password, null, safe_host, safe_port, safe_db, safe_options, null);
- return builder.free_and_steal ();
+ return url.to_string ();
}
/**
diff --git a/src/models/Query.vala b/src/models/Query.vala
index c4dace8..3b0254d 100644
--- a/src/models/Query.vala
+++ b/src/models/Query.vala
@@ -4,6 +4,9 @@ namespace Psequel {
// Properties must be public, get, set inorder to Json.Serializable works
public string sql { get; set; }
public string[] params {get; set;}
+
+ public static Regex DDL_REG = /^(CREATE|DROP|RENAME|ALTER|INSERT|UPDATE|DELETE).*$/i;
+
public Query (string sql) {
base();
this.sql = sql;
@@ -26,9 +29,7 @@ namespace Psequel {
}
public bool is_ddl () {
- var regex = /CREATE|DROP|RENAME|ALTER|INSERT|UPDATE|DELETE/;
- var query = sql.up (6);
- return regex.match (query, 0, null);
+ return DDL_REG.match (sql, 0, null);
}
public Query clone () {
diff --git a/src/models/Schema.vala b/src/models/Schema.vala
index 253dd09..0daa201 100644
--- a/src/models/Schema.vala
+++ b/src/models/Schema.vala
@@ -145,9 +145,8 @@ namespace Psequel {
}
private void extract_info () {
-
// Match the index type and column from fk_def
- var regex = /FOREIGN KEY \(([$a-zA-Z_, ]+)\) REFERENCES ([a-zA-Z_, ]+)\(([a-zA-Z_, ]+)\)( ON UPDATE (CASCADE))?( ON DELETE (RESTRICT))?/;
+ var regex = /FOREIGN KEY \(([$a-zA-Z_, ]+)\) REFERENCES ([a-zA-Z_,"]+)\(([a-zA-Z_, ]+)\)( ON UPDATE (CASCADE))?( ON DELETE (RESTRICT))?/;
MatchInfo match_info;
if (regex.match (fk_def, 0, out match_info)) {
diff --git a/src/services/Container.vala b/src/services/Container.vala
index 14dac8d..e3e3a4e 100644
--- a/src/services/Container.vala
+++ b/src/services/Container.vala
@@ -2,6 +2,15 @@ namespace Psequel {
public class Container : Object {
private HashTable dependencies;
+ private static Container? _instance;
+
+ public static Container instance() {
+ if (_instance == null) {
+ _instance = new Container();
+ }
+
+ return _instance;
+ }
/** Manual dependency injection map */
diff --git a/src/services/ExportService.vala b/src/services/ExportService.vala
index 0ab2c46..3072e75 100644
--- a/src/services/ExportService.vala
+++ b/src/services/ExportService.vala
@@ -1,4 +1,4 @@
-namespace Psequel{
+namespace Psequel {
public class ExportService : Object {
public const string DELIMETER = ",";
@@ -28,13 +28,13 @@ namespace Psequel{
rows[i + 1] = string.joinv (DELIMETER, cols);
}
-
+
var bytes = new Bytes.take (string.joinv (NEWLINE, rows).data);
try {
yield dest.replace_contents_bytes_async (bytes, null, false, FileCreateFlags.PRIVATE, null, null);
} catch (GLib.Error err) {
- throw new PsequelError.EXPORT_ERROR(err.message);
+ throw new PsequelError.EXPORT_ERROR (err.message);
}
}
@@ -44,6 +44,6 @@ namespace Psequel{
}
return str;
- }
+ }
}
}
\ No newline at end of file
diff --git a/src/services/NavigationService.vala b/src/services/NavigationService.vala
index ee7e899..6af1a5d 100644
--- a/src/services/NavigationService.vala
+++ b/src/services/NavigationService.vala
@@ -3,18 +3,18 @@ namespace Psequel {
public const string CONNECTION_VIEW = "connection-view";
public const string QUERY_VIEW = "query-view";
- public const string[] VIEW_NAMES = {CONNECTION_VIEW, QUERY_VIEW};
+ public const string[] VIEW_NAMES = { CONNECTION_VIEW, QUERY_VIEW };
- public string current_view { get; set; default = CONNECTION_VIEW;}
+ public string current_view { get; set; default = CONNECTION_VIEW; }
- public NavigationService() {
+ public NavigationService () {
}
public void navigate (string view_name) {
if (view_name == current_view) {
return;
}
-
+
for (int i = 0; i < VIEW_NAMES.length; i++) {
if (VIEW_NAMES[i] == view_name) {
debug ("Navigating to " + view_name);
diff --git a/src/services/CompleterService.vala b/src/services/SQLCompletionService.vala
similarity index 69%
rename from src/services/CompleterService.vala
rename to src/services/SQLCompletionService.vala
index d7bcb7a..509af02 100644
--- a/src/services/CompleterService.vala
+++ b/src/services/SQLCompletionService.vala
@@ -1,4 +1,127 @@
namespace Psequel {
+ public class SQLCompletionService : Object, GtkSource.CompletionProvider {
+ private CompleterService completer;
+ private Gtk.FilterListModel model;
+ private Gtk.StringFilter filter;
+
+ private SchemaContext ctx;
+
+
+ private SchemaViewModel schema_viewmodel;
+ public SQLCompletionService () {
+ base ();
+ this.schema_viewmodel = autowire ();
+ this.completer = autowire ();
+ this.ctx = new SchemaContext ();
+
+ // static_candidates = new List ();
+ // for (int i = 0; i < PGListerals.KEYWORDS.length; i++) {
+ // static_candidates.append (new Candidate (PGListerals.KEYWORDS[i], "KEYWORD"));
+ // }
+
+ // for (int i = 0; i < PGListerals.FUNCTIONS.length; i++) {
+ // static_candidates.append (new Candidate (PGListerals.FUNCTIONS[i], "FUNCTION"));
+ // }
+
+ // for (int i = 0; i < PGListerals.DATATYPES.length; i++) {
+ // static_candidates.append (new Candidate (PGListerals.DATATYPES[i], "DATATYPE"));
+ // }
+
+ // for (int i = 0; i < PGListerals.RESERVED.length; i++) {
+ // static_candidates.append (new Candidate (PGListerals.RESERVED[i], "RESERVED"));
+ // }
+
+ // this.notify["query-viewmodel"].connect (() => {
+
+ // dynamic_candidates = new List ();
+ // query_viewmodel.current_schema.tables.foreach ((table) => {
+ // dynamic_candidates.append (new Model (table.name, "TABLE"));
+ // });
+ // query_viewmodel.current_schema.views.foreach ((view) => {
+ // dynamic_candidates.append (new Model (view.name, "VIEW"));
+ // });
+ // });
+
+ var expression = new Gtk.PropertyExpression (typeof (Candidate), null, "value");
+ filter = new Gtk.StringFilter (expression);
+ filter.match_mode = Gtk.StringFilterMatchMode.PREFIX;
+
+ model = new Gtk.FilterListModel (null, filter);
+ }
+
+ public void activate (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal) {
+ var model = (Candidate) proposal;
+ var buf = context.get_buffer ();
+ Gtk.TextIter start, end;
+
+ last_word (buf, out start, out end);
+
+ buf.delete_range (start, end);
+ buf.insert_at_cursor (model.value, model.value.length);
+ // buf.insert (ref iter, model.value, model.value.length);
+ }
+
+ public void display (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal, GtkSource.CompletionCell cell) {
+
+ var candidate = (Candidate) proposal;
+ switch (cell.column) {
+ // name
+ case GtkSource.CompletionColumn.TYPED_TEXT:
+ cell.text = candidate.value;
+ break;
+
+ // group
+ case GtkSource.CompletionColumn.AFTER:
+ cell.text = candidate.group;
+ break;
+ // case GtkSource.CompletionColumn.DETAILS:
+ // cell.text = "DETAILS";
+ // break;
+ // case GtkSource.CompletionColumn.COMMENT:
+ // cell.text = "COMMENT";
+ // break;
+ // case GtkSource.CompletionColumn.ICON:
+ // cell.text = "ICON";
+ // break;
+ // case GtkSource.CompletionColumn.BEFORE:
+ // cell.text = candidate.value;
+ // break;
+ default: break;
+ }
+ }
+
+ public async GLib.ListModel populate_async (GtkSource.CompletionContext context, GLib.Cancellable? cancellable) {
+
+ var word = last_word (context.get_buffer ());
+ var candidates = new ObservableList ();
+
+ candidates.append_all (completer.get_suggestions (this.ctx, word));
+ model.model = candidates;
+
+ return model;
+ }
+
+ public void refilter (GtkSource.CompletionContext context, GLib.ListModel _model) {
+ var word = last_word (context.get_buffer ());
+ var candidates = new ObservableList ();
+ candidates.append_all (completer.get_suggestions (this.ctx, word));
+ model.model = candidates;
+ }
+
+ private string last_word (GtkSource.Buffer buf, out Gtk.TextIter start = null, out Gtk.TextIter end = null) {
+ Gtk.TextIter iter;
+ buf.get_iter_at_offset (out iter, buf.cursor_position);
+
+ start = iter;
+ end = iter;
+
+ start.backward_word_start ();
+ end.forward_word_end ();
+
+ return buf.get_text (start, end, false);
+ }
+ }
+
public class CompleterService : Object, Observer {
public SQLService sql_service { get; construct; }
diff --git a/src/services/SQLService.vala b/src/services/SQLService.vala
index 58a3735..4b17c40 100644
--- a/src/services/SQLService.vala
+++ b/src/services/SQLService.vala
@@ -7,13 +7,15 @@ namespace Psequel {
*/
public class SQLService : Object {
- public int query_limit { get; set; }
+ public int query_limit { get; set; default = 100; }
+ public int query_timeout { get; set; }
public SQLService (ThreadPool background) {
Object ();
this.background = background;
+ var settings = autowire ();
- Application.settings.bind ("query-limit", this, "query-limit", SettingsBindFlags.GET);
+ settings.bind ("query-limit", this, "query-limit", SettingsBindFlags.GET);
}
/** Select info from a table. */
diff --git a/src/ui/PreferencesWindow.vala b/src/ui/PreferencesWindow.vala
index 1d7e904..dbdfd0c 100644
--- a/src/ui/PreferencesWindow.vala
+++ b/src/ui/PreferencesWindow.vala
@@ -7,18 +7,21 @@ namespace Psequel {
// { "editor-font", old_choser },
// };
+ private Settings? settings;
+
public PreferencesWindow () {
Object ();
}
construct {
+ this.settings = autowire ();
setup_binding ();
defaults ();
}
private void defaults () {
- var desc = Pango.FontDescription.from_string (Application.settings.get_string ("editor-font"));
+ var desc = Pango.FontDescription.from_string (settings.get_string ("editor-font"));
font_label.get_pango_context ().set_font_description (desc);
font_label.label = desc.to_string ();
@@ -27,12 +30,12 @@ namespace Psequel {
}
private void setup_binding () {
- // Application.settings.bind_with_mapping (string key, GLib.Object object, string property, GLib.SettingsBindFlags flags, GLib.SettingsBindGetMappingShared get_mapping, GLib.SettingsBindSetMappingShared set_mapping, void* user_data, GLib.DestroyNotify? notify)
- Application.settings.bind ("query-limit", query_limit, "value", SettingsBindFlags.DEFAULT);
- Application.settings.bind ("query-timeout", query_timeout, "value", SettingsBindFlags.DEFAULT);
- Application.settings.bind ("connection-timeout", conn_timeout, "value", SettingsBindFlags.DEFAULT);
+ // settings.bind_with_mapping (string key, GLib.Object object, string property, GLib.SettingsBindFlags flags, GLib.SettingsBindGetMappingShared get_mapping, GLib.SettingsBindSetMappingShared set_mapping, void* user_data, GLib.DestroyNotify? notify)
+ settings.bind ("query-limit", query_limit, "value", SettingsBindFlags.DEFAULT);
+ settings.bind ("query-timeout", query_timeout, "value", SettingsBindFlags.DEFAULT);
+ settings.bind ("connection-timeout", conn_timeout, "value", SettingsBindFlags.DEFAULT);
- Application.settings.changed["editor-font"].connect ((_setting, key) => {
+ settings.changed["editor-font"].connect ((_setting, key) => {
var desc = Pango.FontDescription.from_string (_setting.get_string (key));
@@ -46,7 +49,7 @@ namespace Psequel {
modal = true,
transient_for = this,
level = Gtk.FontChooserLevel.FAMILY | Gtk.FontChooserLevel.SIZE | Gtk.FontChooserLevel.STYLE,
- font = Application.settings.get_string ("editor-font"),
+ font = settings.get_string ("editor-font"),
};
dialog.set_filter_func ((desc) => {
@@ -60,7 +63,7 @@ namespace Psequel {
font_label.get_pango_context ().set_font_description (dialog.font_desc);
font_label.label = dialog.font_desc.to_string ();
- Application.settings.set_string ("editor-font", dialog.font_desc.to_string ());
+ settings.set_string ("editor-font", dialog.font_desc.to_string ());
}
dialog.destroy ();
diff --git a/src/ui/Window.vala b/src/ui/Window.vala
index 68d2311..dc162f4 100644
--- a/src/ui/Window.vala
+++ b/src/ui/Window.vala
@@ -25,9 +25,6 @@ namespace Psequel {
[GtkTemplate (ui = "/me/ppvan/psequel/gtk/window.ui")]
public class Window : Adw.ApplicationWindow {
- public static Container? temp;
- public Container containter { get; construct; }
-
const ActionEntry[] ACTIONS = {
{ "import", import_connection },
{ "export", export_connection },
@@ -39,11 +36,12 @@ namespace Psequel {
public ConnectionViewModel connection_viewmodel { get; construct; }
public QueryViewModel query_viewmodel { get; private set; }
+ private Settings? settings;
+
- public Window (Application app, Container container) {
+ public Window (Application app) {
Object (
- application: app,
- containter: container
+ application: app
);
}
@@ -51,10 +49,11 @@ namespace Psequel {
this.navigation = autowire ();
this.connection_viewmodel = autowire ();
this.query_viewmodel = autowire ();
+ this.settings = autowire ();
debug ("[CONTRUCT] %s", this.name);
- Application.settings.bind ("window-width", this, "default-width", SettingsBindFlags.DEFAULT);
- Application.settings.bind ("window-height", this, "default-height", SettingsBindFlags.DEFAULT);
+ settings.bind ("window-width", this, "default-width", SettingsBindFlags.DEFAULT);
+ settings.bind ("window-height", this, "default-height", SettingsBindFlags.DEFAULT);
this.add_action_entries (ACTIONS, this);
}
diff --git a/src/ui/connection/ConnectionForm.vala b/src/ui/connection/ConnectionForm.vala
deleted file mode 100644
index 8cafab0..0000000
--- a/src/ui/connection/ConnectionForm.vala
+++ /dev/null
@@ -1,107 +0,0 @@
-namespace Psequel {
-
- [GtkTemplate (ui = "/me/ppvan/psequel/gtk/connection-form.ui")]
- public class ConnectionForm : Adw.Bin {
-
-
- public MenuModel menu_model { get; set; }
- BindingGroup binddings;
-
-
- public Connection? selected_connection { get; set; }
- public bool is_connectting { get; set; }
-
- public signal void request_database (Connection conn);
- public signal void connections_changed ();
-
- public ConnectionForm () {
- Object ();
- }
-
- construct {
- debug ("[CONTRUCT] %s", this.name);
- // Create group to maped the entry widget to connection data.
- this.binddings = create_form_bind_group ();
- set_up_bindings ();
-
- connect_btn.grab_focus ();
- }
-
- private BindingGroup create_form_bind_group () {
-
- var binddings = new BindingGroup ();
- debug ("set_up connection form bindings group");
- binddings.bind ("name", name_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
- binddings.bind ("host", host_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
- binddings.bind ("port", port_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
- binddings.bind ("user", user_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
- binddings.bind ("password", password_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
- binddings.bind ("database", database_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
- binddings.bind ("use_ssl", ssl_switch, "active", SYNC_CREATE | BIDIRECTIONAL);
- debug ("set_up binddings done");
-
-
- return binddings;
- }
-
- private void set_up_bindings () {
-
- this.bind_property ("selected-connection", binddings, "source", BindingFlags.SYNC_CREATE);
-
- this.bind_property ("is-connectting", connect_btn, "sensitive", INVERT_BOOLEAN | SYNC_CREATE);
-
- password_entry.bind_property ("text",
- password_entry,
- "show-peek-icon",
- BindingFlags.SYNC_CREATE,
- (binding, from_value, ref to_value) => {
-
- string text = from_value.get_string ();
- to_value.set_boolean (text.length > 0);
-
- return true;
- });
- }
-
- [GtkCallback]
- private void on_connect_clicked (Gtk.Button btn) {
- request_database (selected_connection);
- }
-
- [GtkCallback]
- private void on_entry_activated (Gtk.Entry entry) {
- connect_btn.clicked ();
- }
-
- [GtkCallback]
- private void on_text_changed (Gtk.Editable editable) {
- connections_changed ();
- }
-
- [GtkCallback]
- private void on_switch_changed () {
- connections_changed ();
- }
-
- [GtkChild]
- private unowned Gtk.Button connect_btn;
-
- [GtkChild]
- private unowned Gtk.Entry name_entry;
-
- [GtkChild]
- private unowned Gtk.Entry host_entry;
- [GtkChild]
- private unowned Gtk.Entry port_entry;
- [GtkChild]
- private unowned Gtk.Entry user_entry;
- [GtkChild]
- private unowned Gtk.PasswordEntry password_entry;
-
- [GtkChild]
- private unowned Gtk.Entry database_entry;
-
- [GtkChild]
- private unowned Gtk.Switch ssl_switch;
- }
-}
\ No newline at end of file
diff --git a/src/ui/connection/ConnectionSidebar.vala b/src/ui/connection/ConnectionSidebar.vala
deleted file mode 100644
index 6a2c0ae..0000000
--- a/src/ui/connection/ConnectionSidebar.vala
+++ /dev/null
@@ -1,113 +0,0 @@
-
-namespace Psequel {
-
-
- [GtkTemplate (ui = "/me/ppvan/psequel/gtk/connection-sidebar.ui")]
- public class ConnectionSidebar : Gtk.Box {
-
-
- const ActionEntry[] ACTION_ENTRIES = {
- { "connect", on_connect_connection },
- { "dupplicate", on_dupplicate_connection },
- { "delete", on_remove_connection },
- };
-
- public ObservableList connections { get; set; }
- public Connection? selected_connection { get; set; }
-
- public signal void request_new_connection ();
- public signal void request_dup_connection (Connection conn);
- public signal void request_remove_connection (Connection conn);
- public signal void request_connect_database (Connection conn);
-
- public ConnectionSidebar () {
- Object ();
- }
-
- construct {
- debug ("[CONTRUCT] %s", this.name);
-
- var action_group = new SimpleActionGroup ();
- action_group.add_action_entries (ACTION_ENTRIES, this);
- this.insert_action_group ("conn", action_group);
-
- selection_model.bind_property ("selected", this, "selected-connection",
- DEFAULT | BIDIRECTIONAL, from_selected, to_selected);
- }
-
- // On add, create new connection and select it.
- [GtkCallback]
- public void on_add_connection (Gtk.Button btn) {
- request_new_connection ();
- }
-
- [GtkCallback]
- public void on_connection_active (Gtk.ListView view, uint pos) {
- request_connect_database (selected_connection);
- }
-
- // [GtkAction]
- private void on_dupplicate_connection () {
- request_dup_connection (selected_connection);
- }
-
- // [GtkAction]
- private void on_connect_connection () {
- request_connect_database (selected_connection);
- }
-
- // [GtkAction]
- private void on_remove_connection () {
- request_remove_connection (selected_connection);
- }
-
- private bool from_selected (Binding binding, Value from, ref Value to) {
- uint pos = from.get_uint ();
-
- if (pos != Gtk.INVALID_LIST_POSITION) {
- to.set_object (selection_model.get_item (pos));
- }
-
- return true;
- }
-
- private bool to_selected (Binding binding, Value from, ref Value to) {
-
- Connection conn = (Connection) from.get_object ();
- for (uint i = 0; i < selection_model.get_n_items (); i++) {
- if (selection_model.get_item (i) == conn) {
- to.set_uint (i);
- return true;
- }
- }
-
- to.set_uint (Gtk.INVALID_LIST_POSITION);
-
- return true;
- }
-
- [GtkChild]
- private unowned Gtk.SingleSelection selection_model;
-
- // [GtkChild]
- // private unowned Gtk.ListView listview;
- }
-
- [GtkTemplate (ui = "/me/ppvan/psequel/gtk/connection-row.ui")]
- public class ConnectionRow : Gtk.Box {
- public Connection item { get; set; }
- public uint pos { get; set; }
-
-
- [GtkCallback]
- public void on_right_clicked () {
- var list_view = this.parent.parent as Gtk.ListView;
- list_view.model.select_item (pos, true);
-
- popover.popup ();
- }
-
- [GtkChild]
- private unowned Gtk.PopoverMenu popover;
- }
-}
\ No newline at end of file
diff --git a/src/ui/connection/ConnectionView.vala b/src/ui/connection/ConnectionView.vala
deleted file mode 100644
index f490c13..0000000
--- a/src/ui/connection/ConnectionView.vala
+++ /dev/null
@@ -1,48 +0,0 @@
-
-namespace Psequel {
-
- [GtkTemplate (ui = "/me/ppvan/psequel/gtk/connection-view.ui")]
- public class ConnectionView : Adw.Bin {
-
- public ConnectionViewModel viewmodel { get; private set; }
-
- public ConnectionView (Application app) {
- Object ();
- }
-
- // Connect event.
- construct {
- debug ("[CONTRUCT] %s", this.name);
- setup_paned (paned);
- viewmodel = autowire ();
- }
-
- [GtkCallback]
- public void save_connections () {
- viewmodel.save_connections ();
- }
-
- [GtkCallback]
- public void add_new_connection () {
- viewmodel.new_connection ();
- }
-
- [GtkCallback]
- public void active_connection (Connection conn) {
- viewmodel.active_connection.begin (conn);
- }
-
- [GtkCallback]
- public void dup_connection (Connection conn) {
- viewmodel.dupplicate_connection (conn);
- }
-
- [GtkCallback]
- public void remove_connection (Connection conn) {
- viewmodel.remove_connection (conn);
- }
-
- [GtkChild]
- private unowned Gtk.Paned paned;
- }
-}
\ No newline at end of file
diff --git a/src/ui/editor/QueryEditor.vala b/src/ui/editor/QueryEditor.vala
index fd15572..0ac016a 100644
--- a/src/ui/editor/QueryEditor.vala
+++ b/src/ui/editor/QueryEditor.vala
@@ -21,12 +21,15 @@ namespace Psequel {
private GtkSource.Completion completion;
- private SQLCompletionProvider provider;
+ private SQLCompletionService provider;
private LanguageManager lang_manager;
private StyleSchemeManager style_manager;
+ private Settings? settings;
+ private Application? app;
+
public class QueryEditor () {
Object ();
}
@@ -36,6 +39,8 @@ namespace Psequel {
this.export_service = autowire ();
this.query_viewmodel = autowire ();
this.query_history_viewmodel = autowire ();
+ this.settings = autowire ();
+ this.app = autowire ();
default_setttings ();
selection_model.bind_property ("selected", this, "selected-query", BindingFlags.BIDIRECTIONAL, from_selected, to_selected);
@@ -95,7 +100,7 @@ namespace Psequel {
completion = editor.get_completion ();
completion.select_on_show = true;
completion.page_size = 8;
- provider = new SQLCompletionProvider ();
+ provider = new SQLCompletionService ();
completion.add_provider (provider);
var lang = lang_manager.get_language ("sql");
@@ -114,7 +119,8 @@ namespace Psequel {
buffer.tag_table.add (light_tag);
buffer.tag_table.add (dark_tag);
- Application.app.style_manager.bind_property ("dark", buffer, "style_scheme", BindingFlags.SYNC_CREATE, (binding, from, ref to) => {
+
+ app.style_manager.bind_property ("dark", buffer, "style_scheme", BindingFlags.SYNC_CREATE, (binding, from, ref to) => {
var is_dark = from.get_boolean ();
if (is_dark) {
var scheme = style_manager.get_scheme ("Adwaita-dark");
@@ -158,7 +164,7 @@ namespace Psequel {
}
- if (Application.app.style_manager.dark) {
+ if (app.style_manager.dark) {
buffer.apply_tag_by_name (DARK_TAG, iter1, iter2);
} else {
buffer.apply_tag_by_name (LIGHT_TAG, iter1, iter2);
@@ -199,7 +205,7 @@ namespace Psequel {
}
private SimpleAction boolean_state_factory (string name, owned ChangeStateFunc func) {
- bool init = Application.settings.get_boolean (name);
+ bool init = settings.get_boolean (name);
var action = new SimpleAction.stateful (name, null, new Variant.boolean (init));
action.activate.connect (toggle_boolean);
@@ -221,7 +227,7 @@ namespace Psequel {
debug ("Change auto uppercase");
action.set_state (new_state);
- Application.settings.set_boolean ("auto-uppercase", autouppercase);
+ settings.set_boolean ("auto-uppercase", autouppercase);
}
private void change_auto_exec_history (SimpleAction action, Variant? new_state) {
@@ -229,7 +235,7 @@ namespace Psequel {
bool auto_exec = new_state.get_boolean ();
action.set_state (new_state);
- Application.settings.set_boolean ("auto-exec-history", auto_exec);
+ settings.set_boolean ("auto-exec-history", auto_exec);
}
private bool from_selected (Binding binding, Value from, ref Value to) {
diff --git a/src/ui/editor/SQLCompletionProvider.vala b/src/ui/editor/SQLCompletionProvider.vala
deleted file mode 100644
index 1cad55c..0000000
--- a/src/ui/editor/SQLCompletionProvider.vala
+++ /dev/null
@@ -1,124 +0,0 @@
-namespace Psequel {
- public class SQLCompletionProvider : Object, GtkSource.CompletionProvider {
- private CompleterService completer;
- private Gtk.FilterListModel model;
- private Gtk.StringFilter filter;
-
- private SchemaContext ctx;
-
-
- private SchemaViewModel schema_viewmodel;
- public SQLCompletionProvider () {
- base ();
- this.schema_viewmodel = autowire ();
- this.completer = autowire ();
- this.ctx = new SchemaContext ();
-
- // static_candidates = new List ();
- // for (int i = 0; i < PGListerals.KEYWORDS.length; i++) {
- // static_candidates.append (new Candidate (PGListerals.KEYWORDS[i], "KEYWORD"));
- // }
-
- // for (int i = 0; i < PGListerals.FUNCTIONS.length; i++) {
- // static_candidates.append (new Candidate (PGListerals.FUNCTIONS[i], "FUNCTION"));
- // }
-
- // for (int i = 0; i < PGListerals.DATATYPES.length; i++) {
- // static_candidates.append (new Candidate (PGListerals.DATATYPES[i], "DATATYPE"));
- // }
-
- // for (int i = 0; i < PGListerals.RESERVED.length; i++) {
- // static_candidates.append (new Candidate (PGListerals.RESERVED[i], "RESERVED"));
- // }
-
- // this.notify["query-viewmodel"].connect (() => {
-
- // dynamic_candidates = new List ();
- // query_viewmodel.current_schema.tables.foreach ((table) => {
- // dynamic_candidates.append (new Model (table.name, "TABLE"));
- // });
- // query_viewmodel.current_schema.views.foreach ((view) => {
- // dynamic_candidates.append (new Model (view.name, "VIEW"));
- // });
- // });
-
- var expression = new Gtk.PropertyExpression (typeof (Candidate), null, "value");
- filter = new Gtk.StringFilter (expression);
- filter.match_mode = Gtk.StringFilterMatchMode.PREFIX;
-
- model = new Gtk.FilterListModel (null, filter);
- }
-
- public void activate (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal) {
- var model = (Candidate) proposal;
- var buf = context.get_buffer ();
- Gtk.TextIter start, end;
-
- last_word (buf, out start, out end);
-
- buf.delete_range (start, end);
- buf.insert_at_cursor (model.value, model.value.length);
- // buf.insert (ref iter, model.value, model.value.length);
- }
-
- public void display (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal, GtkSource.CompletionCell cell) {
-
- var candidate = (Candidate) proposal;
- switch (cell.column) {
- // name
- case GtkSource.CompletionColumn.TYPED_TEXT:
- cell.text = candidate.value;
- break;
-
- // group
- case GtkSource.CompletionColumn.AFTER:
- cell.text = candidate.group;
- break;
- // case GtkSource.CompletionColumn.DETAILS:
- // cell.text = "DETAILS";
- // break;
- // case GtkSource.CompletionColumn.COMMENT:
- // cell.text = "COMMENT";
- // break;
- // case GtkSource.CompletionColumn.ICON:
- // cell.text = "ICON";
- // break;
- // case GtkSource.CompletionColumn.BEFORE:
- // cell.text = candidate.value;
- // break;
- default: break;
- }
- }
-
- public async GLib.ListModel populate_async (GtkSource.CompletionContext context, GLib.Cancellable? cancellable) {
-
- var word = last_word (context.get_buffer ());
- var candidates = new ObservableList ();
-
- candidates.append_all (completer.get_suggestions (this.ctx, word));
- model.model = candidates;
-
- return model;
- }
-
- public void refilter (GtkSource.CompletionContext context, GLib.ListModel _model) {
- var word = last_word (context.get_buffer ());
- var candidates = new ObservableList ();
- candidates.append_all (completer.get_suggestions (this.ctx, word));
- model.model = candidates;
- }
-
- private string last_word (GtkSource.Buffer buf, out Gtk.TextIter start = null, out Gtk.TextIter end = null) {
- Gtk.TextIter iter;
- buf.get_iter_at_offset (out iter, buf.cursor_position);
-
- start = iter;
- end = iter;
-
- start.backward_word_start ();
- end.forward_word_end ();
-
- return buf.get_text (start, end, false);
- }
- }
-}
\ No newline at end of file
diff --git a/src/ui/schema/SchemaMain.vala b/src/ui/schema/SchemaMain.vala
deleted file mode 100644
index 877adc9..0000000
--- a/src/ui/schema/SchemaMain.vala
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-namespace Psequel {
-
- [GtkTemplate (ui = "/me/ppvan/psequel/gtk/schema-main.ui")]
- public class SchemaMain : Gtk.Box {
- public MenuModel menu {get; set;}
- // public Table selected_table {get; set;}
- public View selected_view {get; set;}
-
- public TableViewModel table_viewmodel {get; set;}
- public ViewViewModel view_viewmodel {get; set;}
- public QueryViewModel query_viewmodel { get; set; }
-
-
- /** Whether should show view or table info */
- public string view_mode {get; set;}
-
- }
-}
\ No newline at end of file
diff --git a/src/ui/schema/SchemaView.vala b/src/ui/schema/SchemaView.vala
deleted file mode 100644
index 75600b5..0000000
--- a/src/ui/schema/SchemaView.vala
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Psequel {
-
- [GtkTemplate (ui = "/me/ppvan/psequel/gtk/schema-view.ui")]
- public class SchemaView : Adw.Bin {
- public SchemaViewModel schema_viewmodel { get; set; }
-
-
- public signal void request_logout ();
-
- public SchemaView () {
- Object ();
- }
-
- construct {
- setup_paned (paned);
- schema_viewmodel = autowire ();
- }
-
-
- [GtkChild]
- private unowned Gtk.Paned paned;
- }
-}
\ No newline at end of file
diff --git a/src/ui/views/ConnectionView.vala b/src/ui/views/ConnectionView.vala
new file mode 100644
index 0000000..dfbfeca
--- /dev/null
+++ b/src/ui/views/ConnectionView.vala
@@ -0,0 +1,199 @@
+
+namespace Psequel {
+
+ [GtkTemplate (ui = "/me/ppvan/psequel/gtk/connection-view.ui")]
+ public class ConnectionView : Adw.Bin {
+
+ public ConnectionViewModel viewmodel { get; private set; }
+ public ObservableList connections { get; set; }
+ public Connection? selected_connection { get; set; }
+
+ BindingGroup bindings;
+
+ const ActionEntry[] ACTION_ENTRIES = {
+ { "connect", on_connect_connection },
+ { "dupplicate", on_dupplicate_connection },
+ { "delete", on_remove_connection },
+ };
+
+ public ConnectionView (Application app) {
+ Object ();
+ }
+
+ // Connect event.
+ construct {
+ debug ("[CONTRUCT] %s", this.name);
+ setup_paned (paned);
+ viewmodel = autowire ();
+
+ var action_group = new SimpleActionGroup ();
+ action_group.add_action_entries (ACTION_ENTRIES, this);
+ this.insert_action_group ("conn", action_group);
+
+ set_up_bindings ();
+ }
+
+ // [GtkCallback]
+ // public void save_connections () {
+ // viewmodel.save_connections ();
+ // }
+
+ [GtkCallback]
+ public void add_new_connection () {
+ viewmodel.new_connection ();
+ }
+
+ [GtkCallback]
+ public void active_connection (Gtk.ListView view, uint pos) {
+ viewmodel.active_connection.begin (viewmodel.selected_connection);
+ }
+
+ [GtkCallback]
+ public void on_connect_clicked (Gtk.Button btn) {
+ viewmodel.active_connection.begin (viewmodel.selected_connection);
+ }
+
+ [GtkCallback]
+ private void on_entry_activated (Gtk.Entry entry) {
+ on_connect_connection();
+ }
+
+ [GtkCallback]
+ private void on_text_changed (Gtk.Editable editable) {
+ viewmodel.save_connections ();
+ }
+
+ [GtkCallback]
+ private void on_switch_changed () {
+ viewmodel.save_connections ();
+ }
+
+ // [GtkAction]
+ private void on_dupplicate_connection () {
+ viewmodel.dupplicate_connection (viewmodel.selected_connection);
+ }
+
+ // [GtkAction]
+ private void on_connect_connection () {
+ viewmodel.active_connection.begin (viewmodel.selected_connection);
+ }
+
+ // [GtkAction]
+ private void on_remove_connection () {
+ viewmodel.remove_connection (viewmodel.selected_connection);
+ }
+
+ private bool from_selected (Binding binding, Value from, ref Value to) {
+ uint pos = from.get_uint ();
+
+ if (pos != Gtk.INVALID_LIST_POSITION) {
+ to.set_object (selection_model.get_item (pos));
+ }
+
+ return true;
+ }
+
+ private bool to_selected (Binding binding, Value from, ref Value to) {
+
+ Connection conn = (Connection) from.get_object ();
+ for (uint i = 0; i < selection_model.get_n_items (); i++) {
+ if (selection_model.get_item (i) == conn) {
+ to.set_uint (i);
+ return true;
+ }
+ }
+
+ to.set_uint (Gtk.INVALID_LIST_POSITION);
+
+ return true;
+ }
+
+ private void set_up_bindings () {
+
+ // Save ref so it does not be cleaned
+ this.bindings = create_form_bind_group ();
+
+ viewmodel.bind_property ("selected-connection", this.bindings, "source", BindingFlags.SYNC_CREATE);
+ viewmodel.bind_property ("is-connectting", connect_btn, "sensitive", INVERT_BOOLEAN | SYNC_CREATE);
+ selection_model.bind_property ("selected", viewmodel, "selected-connection",
+ DEFAULT | BIDIRECTIONAL, from_selected, to_selected);
+ password_entry.bind_property ("text",
+ password_entry,
+ "show-peek-icon",
+ BindingFlags.SYNC_CREATE,
+ (binding, from_value, ref to_value) => {
+
+ string text = from_value.get_string ();
+ to_value.set_boolean (text.length > 0);
+
+ return true;
+ });
+
+
+ }
+
+ private BindingGroup create_form_bind_group () {
+
+ var binddings = new BindingGroup ();
+ debug ("set_up connection form bindings group");
+ binddings.bind ("name", name_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
+ binddings.bind ("host", host_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
+ binddings.bind ("port", port_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
+ binddings.bind ("user", user_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
+ binddings.bind ("password", password_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
+ binddings.bind ("database", database_entry, "text", SYNC_CREATE | BIDIRECTIONAL);
+ binddings.bind ("use_ssl", ssl_switch, "active", SYNC_CREATE | BIDIRECTIONAL);
+ debug ("set_up binddings done");
+
+
+ return binddings;
+ }
+
+ [GtkChild]
+ private unowned Gtk.SingleSelection selection_model;
+ [GtkChild]
+ private unowned Gtk.Paned paned;
+
+
+ [GtkChild]
+ private unowned Gtk.Button connect_btn;
+
+ [GtkChild]
+ private unowned Gtk.Entry name_entry;
+
+ [GtkChild]
+ private unowned Gtk.Entry host_entry;
+ [GtkChild]
+ private unowned Gtk.Entry port_entry;
+ [GtkChild]
+ private unowned Gtk.Entry user_entry;
+ [GtkChild]
+ private unowned Gtk.PasswordEntry password_entry;
+
+ [GtkChild]
+ private unowned Gtk.Entry database_entry;
+
+ [GtkChild]
+ private unowned Gtk.Switch ssl_switch;
+ }
+
+
+
+ [GtkTemplate (ui = "/me/ppvan/psequel/gtk/connection-row.ui")]
+ public class ConnectionRow : Gtk.Box {
+ public Connection item { get; set; }
+ public uint pos { get; set; }
+
+
+ [GtkCallback]
+ public void on_right_clicked () {
+ var list_view = this.parent.parent as Gtk.ListView;
+ list_view.model.select_item (pos, true);
+
+ popover.popup ();
+ }
+
+ [GtkChild]
+ private unowned Gtk.PopoverMenu popover;
+ }
+}
\ No newline at end of file
diff --git a/src/ui/schema/SchemaSidebar.vala b/src/ui/views/SchemaView.vala
similarity index 89%
rename from src/ui/schema/SchemaSidebar.vala
rename to src/ui/views/SchemaView.vala
index 22cb743..2e2be9e 100644
--- a/src/ui/schema/SchemaSidebar.vala
+++ b/src/ui/views/SchemaView.vala
@@ -1,29 +1,35 @@
namespace Psequel {
- [GtkTemplate (ui = "/me/ppvan/psequel/gtk/schema-sidebar.ui")]
- public class SchemaSidebar : Gtk.Box {
-
-
- public NavigationService navigation_service { get; set; }
+ [GtkTemplate (ui = "/me/ppvan/psequel/gtk/schema-view.ui")]
+ public class SchemaView : Adw.Bin {
public SchemaViewModel schema_viewmodel { get; set; }
public TableViewModel table_viewmodel {get; set;}
public ViewViewModel view_viewmodel {get; set;}
+ public NavigationService navigation_service { get; set; }
public string view_mode {get; set;}
- public SchemaSidebar () {
+ public signal void request_logout ();
+
+ public SchemaView () {
Object ();
}
construct {
+ setup_paned (paned);
+ this.schema_viewmodel = autowire ();
this.table_viewmodel = autowire ();
this.view_viewmodel = autowire ();
- this.schema_viewmodel = autowire ();
this.navigation_service = autowire ();
sql_views.bind_property ("visible-child-name", this, "view-mode", DEFAULT);
dropdown.notify["selected"].connect (() => {
+
+ if (dropdown.selected == Gtk.INVALID_LIST_POSITION) {
+ return;
+ }
+
schema_viewmodel.select_index ((int)dropdown.selected);
});
table_selection.notify["selected"].connect (() => {
@@ -40,6 +46,10 @@ namespace Psequel {
view_filter.expression = new Gtk.PropertyExpression (typeof (View), null, "name");
}
+
+ [GtkChild]
+ private unowned Gtk.Paned paned;
+
[GtkCallback]
private void on_table_search (Gtk.SearchEntry entry) {
table_filter.search = entry.text;
diff --git a/src/ui/widgets/StyleSwitcher.vala b/src/ui/widgets/StyleSwitcher.vala
index 4a75930..3576b00 100644
--- a/src/ui/widgets/StyleSwitcher.vala
+++ b/src/ui/widgets/StyleSwitcher.vala
@@ -18,7 +18,7 @@ namespace Psequel {
construct {
this.notify["style"].connect (this.on_style_changed);
- var s = Application.settings;
+ var s = autowire ();
s.bind ("color-scheme", this, "style", GLib.SettingsBindFlags.DEFAULT);
}
diff --git a/src/utils/ObservableList.vala b/src/utils/ObservableList.vala
index 5f17ea5..6dca652 100644
--- a/src/utils/ObservableList.vala
+++ b/src/utils/ObservableList.vala
@@ -67,13 +67,13 @@ namespace Psequel {
_data.remove (position);
}
- public void insert (uint pos, Connection conn) {
- _data.insert (pos, conn);
+ public void insert (uint pos, T conn) {
+ _data.insert (pos, (Object)conn);
}
- public uint indexof (Connection conn) {
+ public uint indexof (T conn) {
uint pos;
- _data.find (conn, out pos);
+ _data.find ((Object)conn, out pos);
return pos;
}
diff --git a/src/utils/helpers.vala b/src/utils/helpers.vala
index 6232350..c431b10 100644
--- a/src/utils/helpers.vala
+++ b/src/utils/helpers.vala
@@ -40,13 +40,10 @@ namespace Psequel {
});
}
- public T autowire () {
- var container = Window.temp;
- return (T)container.find_type (typeof (T));
- }
public Adw.MessageDialog create_dialog (string heading, string body) {
- var window = Application.app.active_window;
+ var app = autowire ();
+ var window = app.active_window;
var dialog = new Adw.MessageDialog (window, heading, body);
dialog.close_response = "okay";
@@ -54,47 +51,4 @@ namespace Psequel {
return dialog;
}
-
- /**
- * Util class to mesure execution time than log it using debug()
- */
- public class TimePerf {
- private static int64 _start;
- private static int64 _end;
-
- public static void begin () {
- _start = GLib.get_real_time ();
- }
-
- public static void end () {
- _end = GLib.get_real_time ();
-
- debug (@"Elapsed: %$(int64.FORMAT) ms", (_end - _start) / 1000);
- }
-
- public static int64 uend () {
- _end = GLib.get_real_time ();
-
- debug (@"Elapsed: %$(int64.FORMAT) μs", (_end - _start));
-
- return _end - _start;
- }
- }
-
- public delegate void Job ();
- public class Worker {
- public string thread_name { private set; get; }
- public Job task;
-
- public Worker (string name, owned Job task) {
- this.thread_name = name;
- this.task = (owned) task;
- }
-
- public void run () {
-
- // Thread.usleep ((ulong)1e6);
- this.task ();
- }
- }
}
\ No newline at end of file
diff --git a/src/utils/types.vala b/src/utils/types.vala
new file mode 100644
index 0000000..39a572e
--- /dev/null
+++ b/src/utils/types.vala
@@ -0,0 +1,49 @@
+namespace Psequel {
+ /**
+ * Util class to mesure execution time than log it using debug()
+ */
+ public class TimePerf {
+ private static int64 _start;
+ private static int64 _end;
+
+ public static void begin () {
+ _start = GLib.get_real_time ();
+ }
+
+ public static void end () {
+ _end = GLib.get_real_time ();
+
+ debug (@"Elapsed: %$(int64.FORMAT) ms", (_end - _start) / 1000);
+ }
+
+ public static int64 uend () {
+ _end = GLib.get_real_time ();
+
+ debug (@"Elapsed: %$(int64.FORMAT) μs", (_end - _start));
+
+ return _end - _start;
+ }
+ }
+
+ public delegate void Job ();
+ public class Worker {
+ public string thread_name { private set; get; }
+ public Job task;
+
+ public Worker (string name, owned Job task) {
+ this.thread_name = name;
+ this.task = (owned) task;
+ }
+
+ public void run () {
+
+ // Thread.usleep ((ulong)1e6);
+ this.task ();
+ }
+ }
+
+ public T autowire () {
+ var container = Container.instance ();
+ return (T)container.find_type (typeof (T));
+ }
+}
\ No newline at end of file
diff --git a/src/viewmodels/ConnectionViewModel.vala b/src/viewmodels/ConnectionViewModel.vala
index 76ccefe..465c755 100644
--- a/src/viewmodels/ConnectionViewModel.vala
+++ b/src/viewmodels/ConnectionViewModel.vala
@@ -1,10 +1,23 @@
namespace Psequel {
public class ConnectionViewModel : BaseViewModel {
+
+ public enum State {
+ IDLE,
+ CONNECTING,
+ ERROR
+ }
+
+
uint timeout_id = 0;
public ConnectionRepository repository { get; private set; }
public SQLService sql_service { get; private set; }
public NavigationService navigation_service { get; private set; }
+
+
+ // States
+
+ public State current_state {get; private set; default = State.IDLE;}
public ObservableList connections { get; private set; default = new ObservableList (); }
public Connection? selected_connection { get; set; }
@@ -65,6 +78,7 @@ namespace Psequel {
public async void active_connection (Connection connection) {
this.is_connectting = true;
+ this.current_state = State.CONNECTING;
try {
yield sql_service.connect_db (connection);
this.navigation_service.navigate (NavigationService.QUERY_VIEW);
@@ -72,8 +86,10 @@ namespace Psequel {
} catch (PsequelError err) {
debug ("Error: %s", err.message);
- create_dialog ("Connection Error", err.message.dup ()).present ();
+ this.current_state = State.ERROR;
+ return;
}
+ this.current_state = State.IDLE;
this.is_connectting = false;
}
diff --git a/src/viewmodels/QueryHistoryViewModel.vala b/src/viewmodels/QueryHistoryViewModel.vala
index 553199d..3a2329a 100644
--- a/src/viewmodels/QueryHistoryViewModel.vala
+++ b/src/viewmodels/QueryHistoryViewModel.vala
@@ -2,7 +2,7 @@ namespace Psequel {
public class QueryHistoryViewModel : BaseViewModel {
const string AUTO_EXEC_HISTORY = "auto-exec-history";
- public QueryRepository query_repository {get; private set;}
+
public ObservableList query_history { get; set; default = new ObservableList (); }
public Query? selected_query { get; set; }
@@ -19,13 +19,13 @@ namespace Psequel {
public string query_time { get; private set; }
public SQLService sql_service { get; construct; }
+ public QueryRepository query_repository {get; construct;}
- public QueryHistoryViewModel (SQLService sql_service) {
- Object (sql_service: sql_service);
- query_repository = new QueryRepository (Application.settings);
- query_history.append_all (query_repository.get_queries ());
+ public QueryHistoryViewModel (SQLService sql_service, QueryRepository query_repository) {
+ Object (sql_service: sql_service, query_repository: query_repository);
+ this.query_history.append_all (query_repository.get_queries ());
this.notify["current-relation"].connect (() => {
success = true;
row_affected = @"$(current_relation.row_affected) row affected.";
@@ -51,9 +51,11 @@ namespace Psequel {
}
public async void exec_history (Query query) {
- if (Application.settings.get_boolean (AUTO_EXEC_HISTORY)) {
- yield run_query_internal (query);
- }
+ // if (Application.settings.get_boolean (AUTO_EXEC_HISTORY)) {
+
+ // }
+
+ yield run_query_internal (query);
query_history.remove (query);
query_history.prepend (query);
diff --git a/src/viewmodels/SchemaViewModel.vala b/src/viewmodels/SchemaViewModel.vala
index ef1fa93..6d88f91 100644
--- a/src/viewmodels/SchemaViewModel.vala
+++ b/src/viewmodels/SchemaViewModel.vala
@@ -6,6 +6,9 @@ namespace Psequel {
public ObservableList schemas { get; set; default = new ObservableList (); }
public Schema? current_schema { get; set; }
+ // Needed to interact with gtk.dropdown properly.
+ public int current_index { get; set;}
+
public SchemaRepository repository;
// Services
@@ -22,9 +25,11 @@ namespace Psequel {
}
public void select_index (int index) {
+
if (index < 0 || index >= schemas.size) {
return;
}
+ debug ("Select index %d, %s\n", index, schemas[index].name);
select_schema.begin (schemas[index]);
}
@@ -55,15 +60,23 @@ namespace Psequel {
private async void select_schema (Schema schema) throws PsequelError {
debug ("Select schema: %s", schema.name);
current_schema = schema;
- // force reload
- this.notify_property ("current-schema");
}
/** List schema from database. */
private async void list_schemas () throws PsequelError {
var unload_schemas = yield schema_service.schema_list ();
- schemas.append_all (unload_schemas);
+ var public_first_schemas = new List ();
+
+ foreach(var s in unload_schemas) {
+ if (s.name == DEFAULT) {
+ public_first_schemas.insert (s, 0);
+ } else {
+ public_first_schemas.append (s);
+ }
+ }
+
+ schemas.append_all (public_first_schemas);
}
}
}
\ No newline at end of file
diff --git a/src/viewmodels/TableViewModel.vala b/src/viewmodels/TableViewModel.vala
index a0c8871..3f3b566 100644
--- a/src/viewmodels/TableViewModel.vala
+++ b/src/viewmodels/TableViewModel.vala
@@ -8,6 +8,8 @@ namespace Psequel {
public SQLService sql_service {get; private set;}
+ // public signal void table_changed (Table table);
+
public TableViewModel (SQLService sql_service) {
base ();
this.sql_service = sql_service;
diff --git a/test/connection_test.vala b/test/connection_test.vala
new file mode 100644
index 0000000..0ce1aa8
--- /dev/null
+++ b/test/connection_test.vala
@@ -0,0 +1,48 @@
+using Psequel;
+using GLib;
+
+
+public int main(string[] args) {
+ Test.init(ref args);
+
+ Test.add_func("/connection/default", () => {
+ var conn = new Connection("test-conn") {
+
+ };
+ var expect_url = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
+
+ assert_cmpstr(conn.url_form(), CompareOperator.EQ, expect_url);
+ });
+
+ Test.add_func("/connection/user", () => {
+ var conn = new Connection("test-conn") {
+ user = "psequel",
+ password = "psequel-pass"
+ };
+ var expect_url = "postgresql://psequel:psequel-pass@localhost:5432/postgres?sslmode=disable";
+
+ assert_cmpstr(conn.url_form(), CompareOperator.EQ, expect_url);
+ });
+
+ Test.add_func("/connection/database", () => {
+ var conn = new Connection("test-conn") {
+ database = "psequel-test"
+ };
+ var expect_url = "postgresql://postgres:postgres@localhost:5432/psequel-test?sslmode=disable";
+
+ assert_cmpstr(conn.url_form(), CompareOperator.EQ, expect_url);
+ });
+
+ Test.add_func("/connection/percent-encode", () => {
+ var conn = new Connection("test-conn") {
+ user = "psequel_test",
+ password = "===",
+ options = "options=-c synchronous_commit=off"
+ };
+ var expect_url = "postgresql://psequel_test:===@localhost:5432/postgres?sslmode=disable&options=-c%20synchronous_commit=off";
+
+ assert_cmpstr(conn.url_form(), CompareOperator.EQ, expect_url);
+ });
+
+ return Test.run();
+}
\ No newline at end of file
diff --git a/test/meson.build b/test/meson.build
new file mode 100644
index 0000000..49525ed
--- /dev/null
+++ b/test/meson.build
@@ -0,0 +1,36 @@
+
+
+# VSGI tests
+psequel_tests = [
+ 'connection',
+]
+
+
+
+
+configure_file(output: 'config.h', configuration: conf)
+# env = environment()
+# env.set('G_MESSAGES_DEBUG', 'Psequel')
+
+foreach name : psequel_tests
+ test(name,
+ executable(name, name + '_test.vala', [psequel_sources], dependencies: psequel_deps),
+ env: [
+ 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
+ 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
+ ],
+ protocol: 'tap',
+ )
+endforeach
+
+
+# if get_option('with_query')
+# test('query',
+# executable('query', 'query_test.vala', [psequel_sources], dependencies: psequel_deps),
+# env: [
+# 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
+# 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
+# ],
+# protocol: 'tap',
+# )
+# endif