286 lines
11 KiB
Python
286 lines
11 KiB
Python
# environments_dialog.py
|
|
#
|
|
# Copyright 2025 Pavel Baksy
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from gi.repository import Adw, Gtk, GObject
|
|
from .widgets.variable_row import VariableRow
|
|
from .widgets.environment_row import EnvironmentRow
|
|
from .widgets.environment_header_row import EnvironmentHeaderRow
|
|
from .widgets.variable_data_row import VariableDataRow
|
|
|
|
|
|
@Gtk.Template(resource_path='/cz/bugsy/roster/environments-dialog.ui')
|
|
class EnvironmentsDialog(Adw.Dialog):
|
|
"""Dialog for managing project environments and variables."""
|
|
|
|
__gtype_name__ = 'EnvironmentsDialog'
|
|
|
|
table_container = Gtk.Template.Child()
|
|
add_environment_button = Gtk.Template.Child()
|
|
|
|
__gsignals__ = {
|
|
'environments-updated': (GObject.SIGNAL_RUN_FIRST, None, ()),
|
|
}
|
|
|
|
def __init__(self, project, project_manager, recently_updated_variables=None):
|
|
super().__init__()
|
|
self.project = project
|
|
self.project_manager = project_manager
|
|
self.recently_updated_variables = recently_updated_variables or {}
|
|
self.size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
|
|
self.header_row = None
|
|
self.data_rows = []
|
|
self._populate_table()
|
|
|
|
def _populate_table(self):
|
|
"""Populate the transposed environment-variable table."""
|
|
# Clear existing
|
|
while child := self.table_container.get_first_child():
|
|
self.table_container.remove(child)
|
|
|
|
self.data_rows = []
|
|
|
|
# Create header row
|
|
self.header_row = EnvironmentHeaderRow(
|
|
self.project.environments,
|
|
self.size_group
|
|
)
|
|
self.header_row.connect('environment-edit-requested',
|
|
self._on_environment_edit)
|
|
self.header_row.connect('environment-delete-requested',
|
|
self._on_environment_delete)
|
|
self.table_container.append(self.header_row)
|
|
|
|
# Add separator
|
|
separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
|
|
self.table_container.append(separator)
|
|
|
|
# Create data rows for each variable
|
|
for var_name in self.project.variable_names:
|
|
# Check if variable was recently updated
|
|
update_timestamp = self.recently_updated_variables.get(var_name)
|
|
|
|
row = VariableDataRow(
|
|
var_name,
|
|
self.project.environments,
|
|
self.size_group,
|
|
update_timestamp=update_timestamp
|
|
)
|
|
row.connect('variable-changed',
|
|
self._on_variable_changed, var_name, row)
|
|
row.connect('variable-delete-requested',
|
|
self._on_variable_remove, var_name)
|
|
row.connect('value-changed',
|
|
self._on_value_changed, var_name)
|
|
|
|
self.table_container.append(row)
|
|
self.data_rows.append(row)
|
|
|
|
# Add "Add Variable" button row at the bottom
|
|
add_var_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
|
add_var_row.set_margin_start(12)
|
|
add_var_row.set_margin_end(12)
|
|
add_var_row.set_margin_top(6)
|
|
add_var_row.set_margin_bottom(6)
|
|
|
|
# Variable cell with add button
|
|
var_cell = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
|
var_cell.set_size_request(150, -1)
|
|
if self.size_group:
|
|
self.size_group.add_widget(var_cell)
|
|
|
|
add_variable_button = Gtk.Button()
|
|
add_variable_button.set_icon_name("list-add-symbolic")
|
|
add_variable_button.set_tooltip_text("Add variable")
|
|
add_variable_button.connect('clicked', self.on_add_variable_clicked)
|
|
add_variable_button.add_css_class("flat")
|
|
add_variable_button.add_css_class("circular")
|
|
|
|
var_cell.append(add_variable_button)
|
|
add_var_row.append(var_cell)
|
|
|
|
# Empty space for value columns
|
|
empty_space = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
|
empty_space.set_hexpand(True)
|
|
add_var_row.append(empty_space)
|
|
|
|
self.table_container.append(add_var_row)
|
|
|
|
def on_add_variable_clicked(self, button):
|
|
"""Add new variable."""
|
|
dialog = Adw.AlertDialog()
|
|
dialog.set_heading("New Variable")
|
|
dialog.set_body("Enter variable name:")
|
|
|
|
entry = Gtk.Entry()
|
|
entry.set_placeholder_text("Variable name")
|
|
entry.set_activates_default(True)
|
|
dialog.set_extra_child(entry)
|
|
|
|
dialog.add_response("cancel", "Cancel")
|
|
dialog.add_response("add", "Add")
|
|
dialog.set_response_appearance("add", Adw.ResponseAppearance.SUGGESTED)
|
|
dialog.set_default_response("add")
|
|
dialog.set_close_response("cancel")
|
|
|
|
def on_response(dlg, response):
|
|
if response == "add":
|
|
name = entry.get_text().strip()
|
|
if name and name not in self.project.variable_names:
|
|
self.project_manager.add_variable(self.project.id, name)
|
|
# Reload project data
|
|
self._reload_project()
|
|
self._populate_table()
|
|
self.emit('environments-updated')
|
|
|
|
dialog.connect("response", on_response)
|
|
dialog.present(self)
|
|
|
|
def _on_variable_remove(self, widget, var_name):
|
|
"""Remove variable with confirmation."""
|
|
dialog = Adw.AlertDialog()
|
|
dialog.set_heading("Delete Variable?")
|
|
dialog.set_body(f"Delete variable '{var_name}' from all environments? This cannot be undone.")
|
|
|
|
dialog.add_response("cancel", "Cancel")
|
|
dialog.add_response("delete", "Delete")
|
|
dialog.set_response_appearance("delete", Adw.ResponseAppearance.DESTRUCTIVE)
|
|
dialog.set_close_response("cancel")
|
|
|
|
def on_response(dlg, response):
|
|
if response == "delete":
|
|
self.project_manager.delete_variable(self.project.id, var_name)
|
|
self._reload_project()
|
|
self._populate_table()
|
|
self.emit('environments-updated')
|
|
|
|
dialog.connect("response", on_response)
|
|
dialog.present(self)
|
|
|
|
def _on_variable_changed(self, widget, old_name, row):
|
|
"""Handle variable name change."""
|
|
new_name = row.get_variable_name()
|
|
if new_name and new_name != old_name and new_name not in self.project.variable_names:
|
|
self.project_manager.rename_variable(self.project.id, old_name, new_name)
|
|
self._reload_project()
|
|
self._populate_table()
|
|
self.emit('environments-updated')
|
|
|
|
@Gtk.Template.Callback()
|
|
def on_add_environment_clicked(self, button):
|
|
"""Add new environment."""
|
|
dialog = Adw.AlertDialog()
|
|
dialog.set_heading("New Environment")
|
|
dialog.set_body("Enter environment name:")
|
|
|
|
entry = Gtk.Entry()
|
|
entry.set_placeholder_text("Environment name (e.g., Production)")
|
|
entry.set_activates_default(True)
|
|
dialog.set_extra_child(entry)
|
|
|
|
dialog.add_response("cancel", "Cancel")
|
|
dialog.add_response("add", "Add")
|
|
dialog.set_response_appearance("add", Adw.ResponseAppearance.SUGGESTED)
|
|
dialog.set_default_response("add")
|
|
dialog.set_close_response("cancel")
|
|
|
|
def on_response(dlg, response):
|
|
if response == "add":
|
|
name = entry.get_text().strip()
|
|
if name:
|
|
self.project_manager.add_environment(self.project.id, name)
|
|
self._reload_project()
|
|
self._populate_table()
|
|
self.emit('environments-updated')
|
|
|
|
dialog.connect("response", on_response)
|
|
dialog.present(self)
|
|
|
|
def _on_environment_edit(self, widget, environment):
|
|
"""Edit environment name."""
|
|
dialog = Adw.AlertDialog()
|
|
dialog.set_heading("Edit Environment")
|
|
dialog.set_body("Enter new name:")
|
|
|
|
entry = Gtk.Entry()
|
|
entry.set_text(environment.name)
|
|
entry.set_activates_default(True)
|
|
dialog.set_extra_child(entry)
|
|
|
|
dialog.add_response("cancel", "Cancel")
|
|
dialog.add_response("save", "Save")
|
|
dialog.set_response_appearance("save", Adw.ResponseAppearance.SUGGESTED)
|
|
dialog.set_default_response("save")
|
|
dialog.set_close_response("cancel")
|
|
|
|
def on_response(dlg, response):
|
|
if response == "save":
|
|
new_name = entry.get_text().strip()
|
|
if new_name:
|
|
self.project_manager.update_environment(self.project.id, environment.id, name=new_name)
|
|
self._reload_project()
|
|
self._populate_table()
|
|
self.emit('environments-updated')
|
|
|
|
dialog.connect("response", on_response)
|
|
dialog.present(self)
|
|
|
|
def _on_environment_delete(self, widget, environment):
|
|
"""Delete environment with confirmation."""
|
|
# Prevent deletion of last environment
|
|
if len(self.project.environments) <= 1:
|
|
dialog = Adw.AlertDialog()
|
|
dialog.set_heading("Cannot Delete")
|
|
dialog.set_body("Each project must have at least one environment.")
|
|
dialog.add_response("ok", "OK")
|
|
dialog.present(self)
|
|
return
|
|
|
|
dialog = Adw.AlertDialog()
|
|
dialog.set_heading("Delete Environment?")
|
|
dialog.set_body(f"Delete environment '{environment.name}'? This cannot be undone.")
|
|
|
|
dialog.add_response("cancel", "Cancel")
|
|
dialog.add_response("delete", "Delete")
|
|
dialog.set_response_appearance("delete", Adw.ResponseAppearance.DESTRUCTIVE)
|
|
dialog.set_close_response("cancel")
|
|
|
|
def on_response(dlg, response):
|
|
if response == "delete":
|
|
self.project_manager.delete_environment(self.project.id, environment.id)
|
|
self._reload_project()
|
|
self._populate_table()
|
|
self.emit('environments-updated')
|
|
|
|
dialog.connect("response", on_response)
|
|
dialog.present(self)
|
|
|
|
def _on_value_changed(self, widget, env_id, value, var_name):
|
|
"""Handle value change in table cell."""
|
|
self.project_manager.update_environment_variable(self.project.id, env_id, var_name, value)
|
|
self.emit('environments-updated')
|
|
|
|
def _reload_project(self):
|
|
"""Reload project data from manager."""
|
|
projects = self.project_manager.load_projects()
|
|
for p in projects:
|
|
if p.id == self.project.id:
|
|
self.project = p
|
|
break
|