From b1b7e1081fb56eb1712142d89843f9e00f7e7724 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= <gcarrillo@linuxmail.org>
Date: Sat, 8 Jun 2024 19:05:28 -0500
Subject: [PATCH 1/5] [core] Add methods to handle QGIS service connections

---
 pg_service_parser/core/service_connections.py | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 pg_service_parser/core/service_connections.py

diff --git a/pg_service_parser/core/service_connections.py b/pg_service_parser/core/service_connections.py
new file mode 100644
index 0000000..bc0f56d
--- /dev/null
+++ b/pg_service_parser/core/service_connections.py
@@ -0,0 +1,47 @@
+from qgis.core import (
+    QgsAbstractDatabaseProviderConnection,
+    QgsDataSourceUri,
+    QgsProviderRegistry,
+)
+from qgis.gui import QgsGui
+from qgis.PyQt.QtCore import QSettings
+
+
+def get_connections(service: str) -> dict[str, QgsAbstractDatabaseProviderConnection]:
+    res = {}
+    provider = QgsProviderRegistry.instance().providerMetadata("postgres")
+    conns = provider.connections()
+    for key, pg_conn in conns.items():
+        if QgsDataSourceUri(pg_conn.uri()).service() == service:
+            res[key] = pg_conn
+
+    return res
+
+
+def create_connection(service: str, name: str) -> None:
+    config = {}
+    uri = f"service='{service}'"
+    provider = QgsProviderRegistry.instance().providerMetadata("postgres")
+    conn = provider.createConnection(uri, config)
+    provider.saveConnection(conn, name)
+    # conn.store(name)
+
+
+def remove_connection(conn_name: str) -> None:
+    provider = QgsProviderRegistry.instance().providerMetadata("postgres")
+    provider.deleteConnection(conn_name)
+
+
+def edit_connection(conn_name: str) -> None:
+    provider = QgsProviderRegistry.instance().providerMetadata("postgres")
+
+    if conn_name in provider.dbConnections():
+        pg = QgsGui.sourceSelectProviderRegistry().providerByName("postgres")
+        w = pg.createDataSourceWidget()
+
+        settings = QSettings()
+        settings.value("PostgreSQL/connections/selected")
+        settings.setValue("PostgreSQL/connections/selected", conn_name)
+
+        w.refresh()  # To reflect the newly selected connection
+        w.btnEdit_clicked()

From 012a86c3fa81c9bbc3169a88646157af42769038 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= <gcarrillo@linuxmail.org>
Date: Mon, 10 Jun 2024 14:44:58 -0500
Subject: [PATCH 2/5] Add model to handle service connections

---
 pg_service_parser/core/connection_model.py | 64 ++++++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 pg_service_parser/core/connection_model.py

diff --git a/pg_service_parser/core/connection_model.py b/pg_service_parser/core/connection_model.py
new file mode 100644
index 0000000..9dc548a
--- /dev/null
+++ b/pg_service_parser/core/connection_model.py
@@ -0,0 +1,64 @@
+from qgis.core import QgsAbstractDatabaseProviderConnection
+from qgis.PyQt.QtCore import QAbstractTableModel, QModelIndex, Qt
+from qgis.PyQt.QtGui import QFont
+
+
+class ServiceConnectionModel(QAbstractTableModel):
+    KEY_COL = 0
+    VALUE_COL = 1
+
+    def __init__(
+        self, service_name: str, connections: dict[str, QgsAbstractDatabaseProviderConnection]
+    ) -> None:
+        super().__init__()
+        self.__service_name = service_name
+        self.__model_data = connections
+
+    def rowCount(self, parent=QModelIndex()):
+        return len(self.__model_data)
+
+    def columnCount(self, parent=QModelIndex()):
+        return 2
+
+    def index_to_connection_key(self, index):
+        return list(self.__model_data.keys())[index.row()]
+
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+        if not index.isValid():
+            return None
+
+        key = list(self.__model_data.keys())[index.row()]
+        if role == Qt.ItemDataRole.DisplayRole:
+            if index.column() == self.KEY_COL:
+                return key
+            elif index.column() == self.VALUE_COL:
+                return self.__model_data[key].uri()
+        elif role == Qt.ItemDataRole.FontRole:
+            if index.column() == self.KEY_COL:
+                font = QFont()
+                font.setBold(True)
+                return font
+            elif index.column() == self.VALUE_COL:
+                font = QFont()
+                font.setItalic(True)
+                return font
+
+        return None
+
+    def headerData(
+        self, section: int, orientation: Qt.Orientation, role: Qt.DisplayRole = Qt.DisplayRole
+    ):
+        if orientation == Qt.Horizontal:
+            if section == self.KEY_COL:
+                return "Connection name"
+            elif section == self.VALUE_COL:
+                return "Configuration"
+
+    def flags(self, idx):
+        if not idx.isValid():
+            return ~Qt.ItemFlag.ItemIsSelectable & ~Qt.ItemFlag.ItemIsEnabled
+
+        return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled
+
+    def service_name(self):
+        return self.__service_name

From 47cf2a305b01fbe43aaf854935f03251e4d5e4f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= <gcarrillo@linuxmail.org>
Date: Mon, 10 Jun 2024 18:38:22 -0500
Subject: [PATCH 3/5] [gui] Add tab for listing and managing (add/edit/remove)
 connections to a particular service; refactor ServiceNameDialog to also
 handle connection names

---
 pg_service_parser/core/connection_model.py    |  13 +-
 pg_service_parser/core/service_connections.py |  14 +-
 .../core/{item_models.py => setting_model.py} |   0
 pg_service_parser/gui/dlg_new_name.py         |  42 ++++++
 pg_service_parser/gui/dlg_pg_service.py       | 111 +++++++++++++-
 pg_service_parser/gui/dlg_service_name.py     |  21 ---
 ...vice_name_dialog.ui => new_name_dialog.ui} |  16 +-
 pg_service_parser/ui/pg_service_dialog.ui     | 139 +++++++++++++++++-
 8 files changed, 308 insertions(+), 48 deletions(-)
 rename pg_service_parser/core/{item_models.py => setting_model.py} (100%)
 create mode 100644 pg_service_parser/gui/dlg_new_name.py
 delete mode 100644 pg_service_parser/gui/dlg_service_name.py
 rename pg_service_parser/ui/{service_name_dialog.ui => new_name_dialog.ui} (86%)

diff --git a/pg_service_parser/core/connection_model.py b/pg_service_parser/core/connection_model.py
index 9dc548a..6c4a89d 100644
--- a/pg_service_parser/core/connection_model.py
+++ b/pg_service_parser/core/connection_model.py
@@ -42,17 +42,20 @@ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
                 font = QFont()
                 font.setItalic(True)
                 return font
+        elif role == Qt.ItemDataRole.ToolTipRole:
+            if index.column() == self.VALUE_COL:
+                return self.__model_data[key].uri()
 
         return None
 
-    def headerData(
-        self, section: int, orientation: Qt.Orientation, role: Qt.DisplayRole = Qt.DisplayRole
-    ):
-        if orientation == Qt.Horizontal:
+    def headerData(self, section, orientation, role):
+        if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
             if section == self.KEY_COL:
                 return "Connection name"
             elif section == self.VALUE_COL:
-                return "Configuration"
+                return "URI"
+
+        return QAbstractTableModel.headerData(self, section, orientation, role)
 
     def flags(self, idx):
         if not idx.isValid():
diff --git a/pg_service_parser/core/service_connections.py b/pg_service_parser/core/service_connections.py
index bc0f56d..03069f7 100644
--- a/pg_service_parser/core/service_connections.py
+++ b/pg_service_parser/core/service_connections.py
@@ -18,30 +18,30 @@ def get_connections(service: str) -> dict[str, QgsAbstractDatabaseProviderConnec
     return res
 
 
-def create_connection(service: str, name: str) -> None:
+def create_connection(service: str, connection_name: str) -> None:
     config = {}
     uri = f"service='{service}'"
     provider = QgsProviderRegistry.instance().providerMetadata("postgres")
     conn = provider.createConnection(uri, config)
-    provider.saveConnection(conn, name)
+    provider.saveConnection(conn, connection_name)
     # conn.store(name)
 
 
-def remove_connection(conn_name: str) -> None:
+def remove_connection(connection_name: str) -> None:
     provider = QgsProviderRegistry.instance().providerMetadata("postgres")
-    provider.deleteConnection(conn_name)
+    provider.deleteConnection(connection_name)
 
 
-def edit_connection(conn_name: str) -> None:
+def edit_connection(connection_name: str) -> None:
     provider = QgsProviderRegistry.instance().providerMetadata("postgres")
 
-    if conn_name in provider.dbConnections():
+    if connection_name in provider.dbConnections():
         pg = QgsGui.sourceSelectProviderRegistry().providerByName("postgres")
         w = pg.createDataSourceWidget()
 
         settings = QSettings()
         settings.value("PostgreSQL/connections/selected")
-        settings.setValue("PostgreSQL/connections/selected", conn_name)
+        settings.setValue("PostgreSQL/connections/selected", connection_name)
 
         w.refresh()  # To reflect the newly selected connection
         w.btnEdit_clicked()
diff --git a/pg_service_parser/core/item_models.py b/pg_service_parser/core/setting_model.py
similarity index 100%
rename from pg_service_parser/core/item_models.py
rename to pg_service_parser/core/setting_model.py
diff --git a/pg_service_parser/gui/dlg_new_name.py b/pg_service_parser/gui/dlg_new_name.py
new file mode 100644
index 0000000..031d8dc
--- /dev/null
+++ b/pg_service_parser/gui/dlg_new_name.py
@@ -0,0 +1,42 @@
+from enum import Enum
+
+from qgis.PyQt.QtCore import pyqtSlot
+from qgis.PyQt.QtWidgets import QDialog, QWidget
+
+from pg_service_parser.utils import get_ui_class
+
+DIALOG_UI = get_ui_class("new_name_dialog.ui")
+
+
+class EnumNewName(Enum):
+    SERVICE = 0
+    CONNECTION = 1
+
+
+class NewNameDialog(QDialog, DIALOG_UI):
+
+    def __init__(self, mode: EnumNewName, parent: QWidget, data: str = "") -> None:
+        QDialog.__init__(self, parent)
+        self.setupUi(self)
+        self.__mode = mode
+
+        self.buttonBox.accepted.connect(self.__accepted)
+
+        if self.__mode == EnumNewName.SERVICE:
+            self.setWindowTitle("Service name")
+            self.label.setText("Enter a service name")
+            self.txtNewName.setPlaceholderText("e.g., my-service")
+            self.new_name = "my-service"
+        elif self.__mode == EnumNewName.CONNECTION:
+            self.setWindowTitle("Connection name")
+            self.label.setText("Enter a connection name")
+            self.txtNewName.setPlaceholderText("e.g., My Service Connection")
+            self.new_name = f"{data} connection"
+
+    @pyqtSlot()
+    def __accepted(self):
+        if self.txtNewName.text().strip():
+            if self.__mode == EnumNewName.SERVICE:
+                self.new_name = self.txtNewName.text().strip().replace(" ", "-")
+            elif self.__mode == EnumNewName.CONNECTION:
+                self.new_name = self.txtNewName.text().strip()
diff --git a/pg_service_parser/gui/dlg_pg_service.py b/pg_service_parser/gui/dlg_pg_service.py
index bf9c27e..97b4a5b 100644
--- a/pg_service_parser/gui/dlg_pg_service.py
+++ b/pg_service_parser/gui/dlg_pg_service.py
@@ -2,11 +2,11 @@
 
 from qgis.core import QgsApplication
 from qgis.gui import QgsMessageBar
-from qgis.PyQt.QtCore import QItemSelection, Qt, pyqtSlot
-from qgis.PyQt.QtWidgets import QDialog, QMessageBox, QSizePolicy
+from qgis.PyQt.QtCore import QItemSelection, QModelIndex, Qt, pyqtSlot
+from qgis.PyQt.QtWidgets import QDialog, QHeaderView, QMessageBox, QSizePolicy
 
 from pg_service_parser.conf.service_settings import SERVICE_SETTINGS, SETTINGS_TEMPLATE
-from pg_service_parser.core.item_models import ServiceConfigModel
+from pg_service_parser.core.connection_model import ServiceConnectionModel
 from pg_service_parser.core.pg_service_parser_wrapper import (
     add_new_service,
     conf_path,
@@ -15,7 +15,14 @@
     service_names,
     write_service,
 )
-from pg_service_parser.gui.dlg_service_name import ServiceNameDialog
+from pg_service_parser.core.service_connections import (
+    create_connection,
+    edit_connection,
+    get_connections,
+    remove_connection,
+)
+from pg_service_parser.core.setting_model import ServiceConfigModel
+from pg_service_parser.gui.dlg_new_name import EnumNewName, NewNameDialog
 from pg_service_parser.gui.dlg_service_settings import ServiceSettingsDialog
 from pg_service_parser.gui.item_delegates import ServiceConfigDelegate
 from pg_service_parser.utils import get_ui_class
@@ -23,6 +30,7 @@
 DIALOG_UI = get_ui_class("pg_service_dialog.ui")
 EDIT_TAB_INDEX = 0
 COPY_TAB_INDEX = 1
+CONNECTION_TAB_INDEX = 2
 
 
 class PgServiceDialog(QDialog, DIALOG_UI):
@@ -53,9 +61,13 @@ def __initialize_dialog(self):
             return
 
         self.__edit_model = None
+        self.__connection_model = None
 
         self.btnAddSettings.setIcon(QgsApplication.getThemeIcon("/symbologyAdd.svg"))
         self.btnRemoveSetting.setIcon(QgsApplication.getThemeIcon("/symbologyRemove.svg"))
+        self.btnAddConnection.setIcon(QgsApplication.getThemeIcon("/symbologyAdd.svg"))
+        self.btnEditConnection.setIcon(QgsApplication.getThemeIcon("/symbologyEdit.svg"))
+        self.btnRemoveConnection.setIcon(QgsApplication.getThemeIcon("/symbologyRemove.svg"))
         self.txtConfFile.setText(str(self.__conf_file_path))
         self.lblWarning.setVisible(False)
         self.lblConfFile.setText("Config file path found at ")
@@ -63,6 +75,7 @@ def __initialize_dialog(self):
         self.txtConfFile.setVisible(True)
         self.tabWidget.setEnabled(True)
         self.btnCreateServiceFile.setVisible(False)
+        self.tblServiceConnections.horizontalHeader().setVisible(True)
         self.btnRemoveSetting.setEnabled(False)
 
         self.radOverwrite.toggled.connect(self.__update_target_controls)
@@ -73,9 +86,15 @@ def __initialize_dialog(self):
         self.btnAddSettings.clicked.connect(self.__add_settings_clicked)
         self.btnRemoveSetting.clicked.connect(self.__remove_setting_clicked)
         self.btnUpdateService.clicked.connect(self.__update_service_clicked)
+        self.cboConnectionService.currentIndexChanged.connect(self.__connection_service_changed)
+        self.btnAddConnection.clicked.connect(self.__add_connection_clicked)
+        self.btnEditConnection.clicked.connect(self.__edit_connection_clicked)
+        self.btnRemoveConnection.clicked.connect(self.__remove_connection_clicked)
+        self.tblServiceConnections.doubleClicked.connect(self.__edit_double_clicked_connection)
 
         self.__initialize_edit_services()
         self.__initialize_copy_services()
+        self.__initialize_connection_services()
         self.__update_target_controls(True)
         self.__update_add_settings_button()
 
@@ -85,11 +104,11 @@ def __initialize_dialog(self):
 
     @pyqtSlot()
     def __create_file_clicked(self):
-        dlg = ServiceNameDialog(self)
+        dlg = NewNameDialog(EnumNewName.SERVICE, self)
         dlg.exec()
         if dlg.result() == QDialog.DialogCode.Accepted:
             Path.touch(self.__conf_file_path)
-            add_new_service(dlg.service_name)
+            add_new_service(dlg.new_name)
 
             # Set flag to get a template after some initialization
             self.__new_empty_file = True
@@ -138,6 +157,15 @@ def __initialize_edit_services(self):
         self.cboEditService.addItems(service_names(self.__conf_file_path))
         self.cboEditService.setCurrentText(current_text)
 
+    def __initialize_connection_services(self):
+        self.__connection_model = None
+        current_text = self.cboConnectionService.currentText()  # Remember latest currentText
+        self.cboConnectionService.blockSignals(True)  # Avoid triggering custom slot while clearing
+        self.cboConnectionService.clear()
+        self.cboConnectionService.blockSignals(False)
+        self.cboConnectionService.addItems(service_names(self.__conf_file_path))
+        self.cboConnectionService.setCurrentText(current_text)
+
     @pyqtSlot()
     def __copy_service(self):
         # Validations
@@ -178,6 +206,8 @@ def __current_tab_changed(self, index):
             pass  # For now, services to be copied won't be altered in other tabs
         elif index == EDIT_TAB_INDEX:
             self.__initialize_edit_services()
+        elif index == CONNECTION_TAB_INDEX:
+            self.__initialize_connection_services()
 
     @pyqtSlot(int)
     def __edit_service_changed(self, index):
@@ -275,3 +305,72 @@ def __update_service_clicked(self):
             self.__edit_model.set_not_dirty()
         else:
             self.bar.pushInfo("PG service", "Edit the service configuration and try again.")
+
+    @pyqtSlot(int)
+    def __connection_service_changed(self, index):
+        self.__initialize_service_connections()
+
+    def __initialize_service_connections(self, selected_index=QModelIndex()):
+        service = self.cboConnectionService.currentText()
+        self.__connection_model = ServiceConnectionModel(service, get_connections(service))
+        self.__update_connection_controls(False)
+        self.tblServiceConnections.setModel(self.__connection_model)
+        self.tblServiceConnections.horizontalHeader().setSectionResizeMode(
+            0, QHeaderView.ResizeToContents
+        )
+
+        self.tblServiceConnections.selectionModel().selectionChanged.connect(
+            self.__conn_table_selection_changed
+        )
+        self.tblServiceConnections.selectRow(selected_index.row())  # Remember selection
+
+    @pyqtSlot()
+    def __add_connection_clicked(self):
+        service = self.cboConnectionService.currentText()
+        dlg = NewNameDialog(EnumNewName.CONNECTION, self, service)
+        dlg.exec()
+        if dlg.result() == QDialog.DialogCode.Accepted:
+            create_connection(service, dlg.new_name)
+            self.__initialize_service_connections()
+
+    @pyqtSlot()
+    def __edit_connection_clicked(self):
+        selected_indexes = self.tblServiceConnections.selectedIndexes()
+        if selected_indexes:
+            self.__edit_connection(selected_indexes[0])
+
+    @pyqtSlot(QModelIndex)
+    def __edit_double_clicked_connection(self, index):
+        self.__edit_connection(index)
+
+    def __edit_connection(self, index):
+        connection_name = self.__connection_model.index_to_connection_key(index)
+        edit_connection(connection_name)
+        self.__initialize_service_connections(index)
+
+    @pyqtSlot()
+    def __remove_connection_clicked(self):
+        selected_indexes = self.tblServiceConnections.selectedIndexes()
+        if selected_indexes:
+            connection_name = self.__connection_model.index_to_connection_key(selected_indexes[0])
+            if (
+                QMessageBox.question(
+                    self,
+                    "Remove service connection",
+                    f"Are you sure you want to remove the connection to '{connection_name}'?",
+                    QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+                    QMessageBox.StandardButton.No,
+                )
+                == QMessageBox.StandardButton.Yes
+            ):
+                remove_connection(connection_name)
+                self.__initialize_service_connections()
+
+    @pyqtSlot(QItemSelection, QItemSelection)
+    def __conn_table_selection_changed(self, selected, deselected):
+        selected_indexes = bool(self.tblServiceConnections.selectedIndexes())
+        self.__update_connection_controls(selected_indexes)
+
+    def __update_connection_controls(self, enable):
+        self.btnEditConnection.setEnabled(enable)
+        self.btnRemoveConnection.setEnabled(enable)
diff --git a/pg_service_parser/gui/dlg_service_name.py b/pg_service_parser/gui/dlg_service_name.py
deleted file mode 100644
index edb7798..0000000
--- a/pg_service_parser/gui/dlg_service_name.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from qgis.PyQt.QtCore import pyqtSlot
-from qgis.PyQt.QtWidgets import QDialog
-
-from pg_service_parser.utils import get_ui_class
-
-DIALOG_UI = get_ui_class("service_name_dialog.ui")
-
-
-class ServiceNameDialog(QDialog, DIALOG_UI):
-
-    def __init__(self, parent):
-        QDialog.__init__(self, parent)
-        self.setupUi(self)
-
-        self.buttonBox.accepted.connect(self.__accepted)
-        self.service_name = "my-service"
-
-    @pyqtSlot()
-    def __accepted(self):
-        if self.txtServiceName.text().strip():
-            self.service_name = self.txtServiceName.text().replace(" ", "-")
diff --git a/pg_service_parser/ui/service_name_dialog.ui b/pg_service_parser/ui/new_name_dialog.ui
similarity index 86%
rename from pg_service_parser/ui/service_name_dialog.ui
rename to pg_service_parser/ui/new_name_dialog.ui
index 69a0c96..bf54cf3 100644
--- a/pg_service_parser/ui/service_name_dialog.ui
+++ b/pg_service_parser/ui/new_name_dialog.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
- <class>dlgServiceName</class>
- <widget class="QDialog" name="dlgServiceName">
+ <class>dlgNewName</class>
+ <widget class="QDialog" name="dlgNewName">
   <property name="geometry">
    <rect>
     <x>0</x>
@@ -11,23 +11,23 @@
    </rect>
   </property>
   <property name="windowTitle">
-   <string>Service name</string>
+   <string>New name</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QLabel" name="label">
      <property name="text">
-      <string>Enter a service name</string>
+      <string>Enter a new name</string>
      </property>
     </widget>
    </item>
    <item row="1" column="0">
-    <widget class="QLineEdit" name="txtServiceName">
+    <widget class="QLineEdit" name="txtNewName">
      <property name="alignment">
       <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
      </property>
      <property name="placeholderText">
-      <string>e.g., my-service</string>
+      <string>e.g., new name</string>
      </property>
      <property name="clearButtonEnabled">
       <bool>true</bool>
@@ -66,7 +66,7 @@
   <connection>
    <sender>buttonBox</sender>
    <signal>accepted()</signal>
-   <receiver>dlgServiceName</receiver>
+   <receiver>dlgNewName</receiver>
    <slot>accept()</slot>
    <hints>
     <hint type="sourcelabel">
@@ -82,7 +82,7 @@
   <connection>
    <sender>buttonBox</sender>
    <signal>rejected()</signal>
-   <receiver>dlgServiceName</receiver>
+   <receiver>dlgNewName</receiver>
    <slot>reject()</slot>
    <hints>
     <hint type="sourcelabel">
diff --git a/pg_service_parser/ui/pg_service_dialog.ui b/pg_service_parser/ui/pg_service_dialog.ui
index c43e301..902a0e2 100644
--- a/pg_service_parser/ui/pg_service_dialog.ui
+++ b/pg_service_parser/ui/pg_service_dialog.ui
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>497</width>
+    <width>499</width>
     <height>316</height>
    </rect>
   </property>
@@ -326,6 +326,143 @@
        </item>
       </layout>
      </widget>
+     <widget class="QWidget" name="tabConnections">
+      <attribute name="title">
+       <string>Connections</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_4">
+       <item row="0" column="0" colspan="2">
+        <layout class="QHBoxLayout" name="horizontalLayout_5">
+         <item>
+          <widget class="QLabel" name="lblServiceConnections">
+           <property name="text">
+            <string>Service</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QComboBox" name="cboConnectionService">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_4">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="0">
+        <widget class="QTableView" name="tblServiceConnections">
+         <property name="editTriggers">
+          <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
+         </property>
+         <property name="alternatingRowColors">
+          <bool>true</bool>
+         </property>
+         <property name="selectionMode">
+          <enum>QAbstractItemView::SingleSelection</enum>
+         </property>
+         <property name="selectionBehavior">
+          <enum>QAbstractItemView::SelectRows</enum>
+         </property>
+         <property name="gridStyle">
+          <enum>Qt::DotLine</enum>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+         <attribute name="horizontalHeaderVisible">
+          <bool>false</bool>
+         </attribute>
+         <attribute name="horizontalHeaderStretchLastSection">
+          <bool>true</bool>
+         </attribute>
+         <attribute name="verticalHeaderVisible">
+          <bool>false</bool>
+         </attribute>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <layout class="QVBoxLayout" name="verticalLayout_3">
+         <item>
+          <widget class="QPushButton" name="btnAddConnection">
+           <property name="maximumSize">
+            <size>
+             <width>25</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>Add connection to current service</string>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="btnEditConnection">
+           <property name="maximumSize">
+            <size>
+             <width>25</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>Edit connection</string>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="btnRemoveConnection">
+           <property name="maximumSize">
+            <size>
+             <width>25</width>
+             <height>25</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>Remove connection to current service</string>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer name="verticalSpacer_3">
+           <property name="orientation">
+            <enum>Qt::Vertical</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>40</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
     </widget>
    </item>
    <item>

From a0468a33be83f9f5c3f5053eb10e4ea9b32bc6cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= <gcarrillo@linuxmail.org>
Date: Fri, 4 Oct 2024 17:25:32 -0500
Subject: [PATCH 4/5] Address review: rename tabs

---
 pg_service_parser/ui/pg_service_dialog.ui | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pg_service_parser/ui/pg_service_dialog.ui b/pg_service_parser/ui/pg_service_dialog.ui
index 902a0e2..c50ebb1 100644
--- a/pg_service_parser/ui/pg_service_dialog.ui
+++ b/pg_service_parser/ui/pg_service_dialog.ui
@@ -86,7 +86,7 @@
      </property>
      <widget class="QWidget" name="tabEdit">
       <attribute name="title">
-       <string>Edit</string>
+       <string>Edit Service</string>
       </attribute>
       <layout class="QGridLayout" name="gridLayout_3">
        <item row="1" column="0">
@@ -228,7 +228,7 @@
      </widget>
      <widget class="QWidget" name="tabCopy">
       <attribute name="title">
-       <string>Copy</string>
+       <string>Copy Service</string>
       </attribute>
       <layout class="QGridLayout" name="gridLayout_2">
        <item row="2" column="1">
@@ -328,7 +328,7 @@
      </widget>
      <widget class="QWidget" name="tabConnections">
       <attribute name="title">
-       <string>Connections</string>
+       <string>QGIS Connections</string>
       </attribute>
       <layout class="QGridLayout" name="gridLayout_4">
        <item row="0" column="0" colspan="2">

From 1c713662e43887ed6ff319005eb22ab2371b886f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Germ=C3=A1n=20Carrillo?= <gcarrillo@linuxmail.org>
Date: Fri, 4 Oct 2024 17:42:20 -0500
Subject: [PATCH 5/5] [connections] Code cleanup, pass parent and widget mode
 to edit connection dialog

---
 pg_service_parser/core/service_connections.py | 13 +++++++------
 pg_service_parser/gui/dlg_pg_service.py       |  2 +-
 pg_service_parser/gui/item_delegates.py       |  2 +-
 3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/pg_service_parser/core/service_connections.py b/pg_service_parser/core/service_connections.py
index 03069f7..41d798f 100644
--- a/pg_service_parser/core/service_connections.py
+++ b/pg_service_parser/core/service_connections.py
@@ -5,6 +5,7 @@
 )
 from qgis.gui import QgsGui
 from qgis.PyQt.QtCore import QSettings
+from qgis.PyQt.QtWidgets import QWidget
 
 
 def get_connections(service: str) -> dict[str, QgsAbstractDatabaseProviderConnection]:
@@ -24,7 +25,6 @@ def create_connection(service: str, connection_name: str) -> None:
     provider = QgsProviderRegistry.instance().providerMetadata("postgres")
     conn = provider.createConnection(uri, config)
     provider.saveConnection(conn, connection_name)
-    # conn.store(name)
 
 
 def remove_connection(connection_name: str) -> None:
@@ -32,16 +32,17 @@ def remove_connection(connection_name: str) -> None:
     provider.deleteConnection(connection_name)
 
 
-def edit_connection(connection_name: str) -> None:
+def edit_connection(connection_name: str, parent: QWidget) -> None:
     provider = QgsProviderRegistry.instance().providerMetadata("postgres")
 
     if connection_name in provider.dbConnections():
         pg = QgsGui.sourceSelectProviderRegistry().providerByName("postgres")
-        w = pg.createDataSourceWidget()
+        widget = pg.createDataSourceWidget(
+            parent, widgetMode=QgsProviderRegistry.WidgetMode.Standalone
+        )
 
         settings = QSettings()
-        settings.value("PostgreSQL/connections/selected")
         settings.setValue("PostgreSQL/connections/selected", connection_name)
 
-        w.refresh()  # To reflect the newly selected connection
-        w.btnEdit_clicked()
+        widget.refresh()  # To reflect the newly selected connection
+        widget.btnEdit_clicked()
diff --git a/pg_service_parser/gui/dlg_pg_service.py b/pg_service_parser/gui/dlg_pg_service.py
index 97b4a5b..b7b9e1a 100644
--- a/pg_service_parser/gui/dlg_pg_service.py
+++ b/pg_service_parser/gui/dlg_pg_service.py
@@ -345,7 +345,7 @@ def __edit_double_clicked_connection(self, index):
 
     def __edit_connection(self, index):
         connection_name = self.__connection_model.index_to_connection_key(index)
-        edit_connection(connection_name)
+        edit_connection(connection_name, self)
         self.__initialize_service_connections(index)
 
     @pyqtSlot()
diff --git a/pg_service_parser/gui/item_delegates.py b/pg_service_parser/gui/item_delegates.py
index 04767fd..66f1758 100644
--- a/pg_service_parser/gui/item_delegates.py
+++ b/pg_service_parser/gui/item_delegates.py
@@ -4,7 +4,7 @@
 from qgis.PyQt.QtCore import Qt
 from qgis.PyQt.QtWidgets import QComboBox, QStyledItemDelegate
 
-from pg_service_parser.core.item_models import ServiceConfigModel
+from pg_service_parser.core.setting_model import ServiceConfigModel
 
 
 class ServiceConfigDelegate(QStyledItemDelegate):