first attempt to get gaphas working
git-svn-id: file:///Users/arjan/backup/gaphor/gaphor/trunk@930 a8418922-720d-0410-834f-a69b97ada669
This commit is contained in:
parent
baf8304adc
commit
1a64b2aa8d
@ -1,3 +1,8 @@
|
||||
2006-06-07 arjan <arjan at yirdis dot nl>
|
||||
|
||||
* Changed Canvas and View to Gaphas. Items should still be converted.
|
||||
In other words, the application does nothing from this point.
|
||||
|
||||
2006-06-06 arjan <arjan at yirdis dot nl>
|
||||
|
||||
* Merged Stereotypes branch.
|
||||
|
@ -8,63 +8,40 @@ __version__ = '$revision$'
|
||||
__date__ = '$date$'
|
||||
|
||||
import gobject
|
||||
import diacanvas
|
||||
import gaphas
|
||||
from gaphor.misc import uniqueid
|
||||
from uml2 import Namespace, PackageableElement
|
||||
|
||||
class DiagramCanvas(diacanvas.Canvas):
|
||||
"""Some additions to diacanvas.Canvas class.
|
||||
class DiagramCanvas(gaphas.Canvas):
|
||||
"""Some additions to gaphas.Canvas class.
|
||||
Esp. load and save functionallity.
|
||||
"""
|
||||
# Most of those properties come from diacanvas.Canvas
|
||||
_savable_canvas_properties = [ 'extents', 'static_extents',
|
||||
'snap_to_grid', 'grid_int_x', 'grid_int_y', 'grid_ofs_x',
|
||||
'grid_ofs_y', 'grid_color', 'grid_bg' ]
|
||||
# TODO: save or ignore properties coming from diacanvas.Canvas
|
||||
#_savable_canvas_properties = [ 'extents', 'static_extents',
|
||||
# 'snap_to_grid', 'grid_int_x', 'grid_int_y', 'grid_ofs_x',
|
||||
# 'grid_ofs_y', 'grid_color', 'grid_bg' ]
|
||||
|
||||
def __init__(self, diagram):
|
||||
self.__gobject_init__()
|
||||
super(DiagramCanvas, self).__init__()
|
||||
self._diagram = diagram
|
||||
|
||||
diagram = property(lambda d: d._diagram)
|
||||
|
||||
def save(self, save_func):
|
||||
for prop in DiagramCanvas._savable_canvas_properties:
|
||||
save_func(prop, self.get_property(prop))
|
||||
save_func('root_affine', self.root.get_property('affine'))
|
||||
#for prop in DiagramCanvas._savable_canvas_properties:
|
||||
# save_func(prop, self.get_property(prop))
|
||||
#save_func('root_affine', self.root.get_property('affine'))
|
||||
# Save child items:
|
||||
for item in self.root.children:
|
||||
for item in self.get_root_items():
|
||||
save_func(None, item)
|
||||
|
||||
def postload(self):
|
||||
self.set_property ("allow_undo", False)
|
||||
self.update_now()
|
||||
self.resolve_now()
|
||||
self.update_now()
|
||||
|
||||
# setting allow-undo to 1 here will cause update info from later
|
||||
# created elements to be put on the undo stack.
|
||||
#self.clear_undo()
|
||||
#self.clear_redo()
|
||||
self.set_property ("allow_undo", True)
|
||||
|
||||
def _select(self, item_list, expression=None):
|
||||
l = []
|
||||
if expression is None:
|
||||
expression = lambda e: 1
|
||||
CanvasGroupable = diacanvas.CanvasGroupable
|
||||
for item in item_list:
|
||||
if expression(item):
|
||||
l.append(item)
|
||||
if isinstance(item, CanvasGroupable):
|
||||
l.extend(self._select(item.groupable_iter(), expression))
|
||||
return l
|
||||
|
||||
def select(self, expression=None):
|
||||
def select(self, expression=lambda e: True):
|
||||
"""Return a list of all canvas items that match expression.
|
||||
"""
|
||||
return self._select(self.root.children, expression)
|
||||
|
||||
gobject.type_register(DiagramCanvas)
|
||||
return filter(expression, self.get_all_items())
|
||||
|
||||
|
||||
class Diagram(Namespace, PackageableElement):
|
||||
@ -74,8 +51,6 @@ class Diagram(Namespace, PackageableElement):
|
||||
def __init__(self, id=None, factory=None):
|
||||
super(Diagram, self).__init__(id, factory)
|
||||
self.canvas = DiagramCanvas(self)
|
||||
#self.canvas.set_undo_stack_depth(10)
|
||||
self.canvas.set_property ("allow_undo", 1)
|
||||
|
||||
def save(self, save_func):
|
||||
super(Diagram, self).save(save_func)
|
||||
@ -85,23 +60,16 @@ class Diagram(Namespace, PackageableElement):
|
||||
super(Diagram, self).postload()
|
||||
self.canvas.postload()
|
||||
|
||||
def create(self, type):
|
||||
def create(self, type, parent=None):
|
||||
"""Create a new canvas item on the canvas. It is created with
|
||||
a unique ID and it is attached to the diagram's root item.
|
||||
"""
|
||||
assert issubclass(type, diacanvas.CanvasItem)
|
||||
assert issubclass(type, gaphas.Item)
|
||||
obj = type(uniqueid.generate_id())
|
||||
#obj.set_property('parent', self.canvas.root)
|
||||
self.canvas.root.add(obj)
|
||||
self.canvas.root.add(obj, parent)
|
||||
return obj
|
||||
|
||||
# TODO: remove
|
||||
def substitute_item(self, item, new_item_type):
|
||||
"""Create a new item and replace item with the new item.
|
||||
"""
|
||||
|
||||
def unlink(self):
|
||||
self.canvas.set_property('allow_undo', False)
|
||||
# Make sure all canvas items are unlinked
|
||||
for item in self.canvas.select():
|
||||
try:
|
||||
@ -109,7 +77,4 @@ class Diagram(Namespace, PackageableElement):
|
||||
except:
|
||||
pass
|
||||
|
||||
#self.canvas.clear_undo()
|
||||
#self.canvas.clear_redo()
|
||||
|
||||
Namespace.unlink(self)
|
||||
super(Diagram, self).unlink()
|
||||
|
@ -44,9 +44,9 @@ def main(gaphor_file=None):
|
||||
|
||||
This involves importing plugins and creating the main window.
|
||||
"""
|
||||
# Import stuff here, since the user might not need all the GUI stuff
|
||||
# Import GUI stuff here, since the user might not need all the GUI stuff
|
||||
import gtk
|
||||
import diagram
|
||||
#import diagram
|
||||
# Load plugin definitions:
|
||||
import pluginmanager
|
||||
import ui
|
||||
|
@ -23,7 +23,7 @@ def tool_changed(action, window):
|
||||
return
|
||||
else:
|
||||
action.sensitive = True
|
||||
tool = view.get_property('tool')
|
||||
tool = None #view.tool
|
||||
if tool:
|
||||
id = tool.action_id
|
||||
else:
|
||||
|
@ -7,12 +7,13 @@ __author__ = "Arjan Molenaar"
|
||||
__date__ = "2002-08-28"
|
||||
|
||||
class Signal:
|
||||
"""The signal class is an implementation of the Observer pattern. It can be
|
||||
used to send signals to every function or method that connected to the
|
||||
signal object, with a variable amount of parameters. Note that the owner
|
||||
of the Signal instance should define a protocol for notifying the observers.
|
||||
The subject should provide methods for connecting and disconnecting
|
||||
observers (preferably 'connect()' and 'disconnect()'.
|
||||
"""The signal class is an implementation of the Observer pattern.
|
||||
|
||||
It can be used to send signals to every function or method that connected
|
||||
to the signal object, with a variable amount of parameters. Note that the
|
||||
owner of the Signal instance should define a protocol for notifying the
|
||||
observers. The subject should provide methods for connecting and
|
||||
disconnecting observers (preferably 'connect()' and 'disconnect()'.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -20,7 +20,7 @@ import sys
|
||||
import os.path
|
||||
import gc
|
||||
|
||||
import diacanvas
|
||||
import gaphas
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor import parser
|
||||
@ -94,13 +94,13 @@ def save_generator(writer=None, factory=None):
|
||||
"""Save attributes and references from items in the gaphor.UML module.
|
||||
A value may be a primitive (string, int), a gaphor.UML.collection
|
||||
(which contains a list of references to other UML elements) or a
|
||||
diacanvas.Canvas (which contains canvas items).
|
||||
gaphas.Canvas (which contains canvas items).
|
||||
"""
|
||||
if isinstance (value, (UML.Element, diacanvas.CanvasItem)):
|
||||
if isinstance (value, (UML.Element, gaphas.Item)):
|
||||
save_reference(name, value)
|
||||
elif isinstance(value, UML.collection):
|
||||
save_collection(name, value)
|
||||
elif isinstance(value, diacanvas.Canvas):
|
||||
elif isinstance(value, gaphas.Canvas):
|
||||
writer.startElement('canvas', {})
|
||||
value.save(save_canvasitem)
|
||||
writer.endElement('canvas')
|
||||
@ -116,7 +116,7 @@ def save_generator(writer=None, factory=None):
|
||||
save_reference(name, value)
|
||||
elif isinstance(value, UML.collection):
|
||||
save_collection(name, value)
|
||||
elif isinstance(value, diacanvas.CanvasItem):
|
||||
elif isinstance(value, gaphas.Item):
|
||||
writer.startElement('item', { 'id': value.id,
|
||||
'type': value.__class__.__name__ })
|
||||
value.save(save_canvasitem)
|
||||
|
@ -4,7 +4,7 @@ Commands related to the Diagram (DiaCanvas)
|
||||
# vim: sw=4
|
||||
|
||||
import gtk
|
||||
import diacanvas
|
||||
import gaphas
|
||||
|
||||
from gaphor import resource
|
||||
from gaphor import UML
|
||||
@ -327,7 +327,7 @@ class PasteAction(Action):
|
||||
elif isinstance(value, UML.collection):
|
||||
for item in value:
|
||||
self._load_element(name, item)
|
||||
elif isinstance(value, diacanvas.CanvasItem):
|
||||
elif isinstance(value, gaphas.Item):
|
||||
self._load_element(name, value)
|
||||
else:
|
||||
# Plain attribute
|
||||
@ -354,7 +354,6 @@ class PasteAction(Action):
|
||||
# Create new id's that have to be used to create the items:
|
||||
for ci in copy_items:
|
||||
self._new_items[ci.id] = diagram.create(type(ci))
|
||||
#self._new_items[ci.id].set_property('canvas', canvas)
|
||||
|
||||
# Copy attributes and references. References should be
|
||||
# 1. in the ElementFactory (hence they are model elements)
|
||||
@ -394,7 +393,8 @@ class ZoomInAction(Action):
|
||||
|
||||
def execute(self):
|
||||
view = self._window.get_current_diagram_view()
|
||||
view.set_zoom(view.get_zoom() + 0.1)
|
||||
view.zoom(1.2)
|
||||
#view.set_zoom(view.get_zoom() + 0.1)
|
||||
|
||||
register_action(ZoomInAction)
|
||||
|
||||
@ -415,7 +415,8 @@ class ZoomOutAction(Action):
|
||||
|
||||
def execute(self):
|
||||
view = self._window.get_current_diagram_view()
|
||||
view.set_zoom(view.get_zoom() - 0.1)
|
||||
#view.set_zoom(view.get_zoom() - 0.1)
|
||||
view.zoom(1 / 1.2)
|
||||
|
||||
register_action(ZoomOutAction)
|
||||
|
||||
@ -455,7 +456,8 @@ class SnapToGridAction(CheckAction):
|
||||
diagram_tab = self._window.get_current_diagram_tab()
|
||||
diagram_tab.get_canvas().props.snap_to_grid = self.active
|
||||
|
||||
register_action(SnapToGridAction)
|
||||
#register_action(SnapToGridAction)
|
||||
|
||||
|
||||
class ShowGridAction(CheckAction):
|
||||
id = 'ShowGrid'
|
||||
@ -473,9 +475,10 @@ class ShowGridAction(CheckAction):
|
||||
tab = self._window.get_current_diagram_tab()
|
||||
if tab:
|
||||
canvas = tab.get_canvas()
|
||||
canvas.set_property('grid_color', self.active and 255 or canvas.get_property('grid_bg'))
|
||||
#canvas.set_property('grid_color', self.active and 255 or canvas.get_property('grid_bg'))
|
||||
|
||||
#register_action(ShowGridAction)
|
||||
|
||||
register_action(ShowGridAction)
|
||||
|
||||
class ShowElementInTreeViewAction(Action):
|
||||
id = 'ShowElementInTreeView'
|
||||
@ -486,17 +489,16 @@ class ShowElementInTreeViewAction(Action):
|
||||
|
||||
def update(self):
|
||||
diagram_tab = self._window.get_current_diagram_tab()
|
||||
self.sensitive = diagram_tab and diagram_tab.get_view().focus_item and \
|
||||
diagram_tab.get_view().focus_item.item and \
|
||||
diagram_tab.get_view().focus_item.item.subject
|
||||
self.sensitive = diagram_tab and diagram_tab.get_view().focused_item and \
|
||||
diagram_tab.get_view().focused_item.subject
|
||||
|
||||
def execute(self):
|
||||
tab = self._window.get_current_diagram_tab()
|
||||
if tab:
|
||||
view = tab.get_view()
|
||||
fi = view.focus_item
|
||||
fi = view.focused_item
|
||||
assert fi
|
||||
self._window.select_element(fi.item.subject)
|
||||
self._window.select_element(fi.subject)
|
||||
|
||||
register_action(ShowElementInTreeViewAction)
|
||||
|
||||
|
@ -5,6 +5,7 @@ import gtk
|
||||
|
||||
from gaphor import UML
|
||||
from gaphor import resource
|
||||
from gaphor.i18n import _
|
||||
from gaphor.diagram.itemtool import ItemTool
|
||||
from gaphor.diagram import get_diagram_item
|
||||
from gaphor.undomanager import get_undo_manager
|
||||
@ -21,6 +22,7 @@ class DiagramTab(object):
|
||||
|
||||
def __init__(self, owning_window):
|
||||
self.diagram = None
|
||||
#self.view = None
|
||||
self.owning_window = owning_window
|
||||
|
||||
def get_diagram(self):
|
||||
@ -39,54 +41,50 @@ class DiagramTab(object):
|
||||
#self.diagram.canvas.disconnect(self.__snap_to_grid_id)
|
||||
self.diagram = diagram
|
||||
if diagram:
|
||||
diagram.canvas.set_property ('allow_undo', 1)
|
||||
diagram.connect(('name', '__unlink__'), self.__on_diagram_event)
|
||||
|
||||
if hasattr(self, 'view'):
|
||||
self.view.get_hadjustment().set_value(0.0)
|
||||
self.view.get_vadjustment().set_value(0.0)
|
||||
self.view.hadjustment.set_value(0.0)
|
||||
self.view.vadjustment.set_value(0.0)
|
||||
|
||||
|
||||
def construct(self):
|
||||
title = self.diagram and self.diagram.name or '<None>'
|
||||
title = self.diagram and self.diagram.name or _('<None>')
|
||||
|
||||
table = gtk.Table(2,2, False)
|
||||
#table.set_row_spacings (4)
|
||||
#table.set_col_spacings (4)
|
||||
#table.set_row_spacings(4)
|
||||
#table.set_col_spacings(4)
|
||||
|
||||
frame = gtk.Frame()
|
||||
frame.set_shadow_type (gtk.SHADOW_IN)
|
||||
table.attach (frame, 0, 1, 0, 1,
|
||||
gtk.EXPAND | gtk.FILL | gtk.SHRINK,
|
||||
gtk.EXPAND | gtk.FILL | gtk.SHRINK)
|
||||
frame.set_shadow_type(gtk.SHADOW_IN)
|
||||
table.attach(frame, 0, 1, 0, 1,
|
||||
gtk.EXPAND | gtk.FILL | gtk.SHRINK,
|
||||
gtk.EXPAND | gtk.FILL | gtk.SHRINK)
|
||||
|
||||
view = DiagramView (diagram=self.diagram)
|
||||
view.set_scroll_region(0, 0, 600, 450)
|
||||
view = DiagramView(diagram=self.diagram)
|
||||
#view.set_scroll_region(0, 0, 600, 450)
|
||||
|
||||
frame.add (view)
|
||||
frame.add(view)
|
||||
|
||||
sbar = gtk.VScrollbar(view.get_vadjustment())
|
||||
table.attach (sbar, 1, 2, 0, 1, gtk.FILL,
|
||||
gtk.EXPAND | gtk.FILL | gtk.SHRINK)
|
||||
sbar = gtk.VScrollbar(view.vadjustment)
|
||||
table.attach(sbar, 1, 2, 0, 1, gtk.FILL,
|
||||
gtk.EXPAND | gtk.FILL | gtk.SHRINK)
|
||||
|
||||
sbar = gtk.HScrollbar(view.get_hadjustment())
|
||||
table.attach (sbar, 0, 1, 1, 2, gtk.EXPAND | gtk.FILL | gtk.SHRINK,
|
||||
gtk.FILL)
|
||||
sbar = gtk.HScrollbar(view.hadjustment)
|
||||
table.attach(sbar, 0, 1, 1, 2, gtk.EXPAND | gtk.FILL | gtk.SHRINK,
|
||||
gtk.FILL)
|
||||
|
||||
view.connect('notify::tool', self.__on_view_notify_tool)
|
||||
#view.connect('notify::tool', self.__on_view_notify_tool)
|
||||
view.connect_after('event-after', self.__on_view_event_after)
|
||||
view.connect('focus-item', self.__on_view_focus_item)
|
||||
view.connect('select-item', self.__on_view_select_item)
|
||||
view.connect('unselect-item', self.__on_view_select_item)
|
||||
view.connect('focus-changed', self.__on_view_focus_changed)
|
||||
view.connect('selection-changed', self.__on_view_selection_changed)
|
||||
#view.connect('unselect-item', self.__on_view_selection_changed)
|
||||
view.connect_after('key-press-event', self.__on_key_press_event)
|
||||
view.connect('drag_data_received', self.__on_drag_data_received)
|
||||
self.view = view
|
||||
view.connect('drag-data-received', self.__on_drag_data_received)
|
||||
|
||||
item_tool = ItemTool(self.owning_window.get_action_pool())
|
||||
view.get_default_tool().set_item_tool(item_tool)
|
||||
|
||||
view.get_hadjustment().set_value(0.0)
|
||||
view.get_vadjustment().set_value(0.0)
|
||||
#view.get_default_tool().set_item_tool(item_tool)
|
||||
self.view = view
|
||||
|
||||
table.show_all()
|
||||
|
||||
@ -131,11 +129,11 @@ class DiagramTab(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __on_view_focus_item(self, view, focus_item):
|
||||
def __on_view_focus_changed(self, view, focus_item):
|
||||
self.owning_window.execute_action('ItemFocus')
|
||||
component.handle(DiagramItemFocused(focus_item))
|
||||
|
||||
def __on_view_select_item(self, view, select_item):
|
||||
def __on_view_selection_changed(self, view, selection):
|
||||
self.owning_window.execute_action('ItemSelect')
|
||||
|
||||
def __on_view_notify_tool(self, view, pspec):
|
||||
@ -171,7 +169,7 @@ class DiagramTab(object):
|
||||
|
||||
ix, iy = item.affine_point_w2i(max(0, wx), max(0, wy))
|
||||
item.move(ix, iy)
|
||||
item.set_property ('subject', element)
|
||||
item.subject = element
|
||||
get_undo_manager().commit_transaction()
|
||||
view.unselect_all()
|
||||
view.focus(view.find_view_item(item))
|
||||
@ -179,7 +177,7 @@ class DiagramTab(object):
|
||||
self.owning_window.execute_action('ItemDiagramDrop')
|
||||
|
||||
else:
|
||||
log.warning ('No graphical representation for UML element %s' % type(element).__name__)
|
||||
log.warning('No graphical representation for UML element %s' % type(element).__name__)
|
||||
context.finish(True, False, time)
|
||||
else:
|
||||
context.finish(False, False, time)
|
||||
|
@ -1,26 +1,29 @@
|
||||
# vim: sw=4
|
||||
"""
|
||||
"""
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
from diacanvas import CanvasView
|
||||
import gaphas
|
||||
|
||||
class DiagramView(gaphas.View):
|
||||
"""Displays a diagram (canvas) in a widget.
|
||||
|
||||
See also: DiagramTab
|
||||
"""
|
||||
|
||||
class DiagramView(CanvasView):
|
||||
TARGET_STRING = 0
|
||||
TARGET_ELEMENT_ID = 1
|
||||
DND_TARGETS = [
|
||||
('gaphor/element-id', 0, TARGET_ELEMENT_ID)]
|
||||
|
||||
def __init__(self, diagram=None):
|
||||
self.__gobject_init__()
|
||||
__gtype_name__ = 'GaphorDiagramView'
|
||||
|
||||
def __init__(self, diagram=None):
|
||||
super(DiagramView, self).__init__(diagram and diagram.canvas)
|
||||
self.diagram = diagram
|
||||
|
||||
if diagram:
|
||||
canvas = diagram.canvas
|
||||
else:
|
||||
canvas = None
|
||||
CanvasView.__init__(self, canvas)
|
||||
# self.diagram = diagram
|
||||
# Drop
|
||||
self.drag_dest_set (gtk.DEST_DEFAULT_ALL, DiagramView.DND_TARGETS,
|
||||
gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_LINK)
|
||||
|
||||
gobject.type_register(DiagramView)
|
||||
|
||||
# vim: sw=4:et
|
||||
|
@ -6,7 +6,6 @@ Commands related to the Editor
|
||||
from gaphor import UML
|
||||
from gaphor.misc.action import Action, CheckAction, RadioAction, register_action
|
||||
import gtk
|
||||
import diacanvas
|
||||
|
||||
class RunAction(Action):
|
||||
id = 'EditorRun'
|
||||
|
@ -228,8 +228,8 @@ class MainWindow(AbstractWindow):
|
||||
return answer == gtk.RESPONSE_YES
|
||||
|
||||
def show_diagram(self, diagram):
|
||||
"""Show a Diagram element in a new tab. If a tab is already open,
|
||||
show that one instead.
|
||||
"""Show a Diagram element in a new tab.
|
||||
If a tab is already open, show that one instead.
|
||||
"""
|
||||
# Try to find an existing window/tab and let it get focus:
|
||||
for tab in self.get_tabs():
|
||||
|
@ -1,4 +1,3 @@
|
||||
# vim:sw=4
|
||||
"""Icons that are used by Gaphor.
|
||||
"""
|
||||
|
||||
@ -110,8 +109,10 @@ class StockIconLoader(handler.ContentHandler):
|
||||
"""Read characters."""
|
||||
self.data = self.data + content
|
||||
|
||||
|
||||
def load_stock_icons():
|
||||
"""
|
||||
"""Load stock icon definitions from the DataDir location
|
||||
(usually /usr/local/share/gaphor).
|
||||
"""
|
||||
from xml.sax import make_parser
|
||||
parser = make_parser()
|
||||
@ -130,3 +131,4 @@ def load_stock_icons():
|
||||
|
||||
load_stock_icons()
|
||||
|
||||
# vim:sw=4:et
|
||||
|
13
setup.py
13
setup.py
@ -71,22 +71,9 @@ class config_Gaphor(Command):
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
|
||||
#self.pkg_config_check('gobject-2.0', '2.0.0')
|
||||
#self.pkg_config_check('gtk+-2.0', '2.0.0')
|
||||
#self.pkg_config_check('pygtk-2.0', '1.99.15')
|
||||
#self.pkg_config_check('gconf-2.0', '2.0.0')
|
||||
#self.pkg_config_check('libbonobo-2.0', '2.0.0')
|
||||
#self.pkg_config_check('libbonoboui-2.0', '2.0.0')
|
||||
#self.pkg_config_check('diacanvas2', '0.9.1')
|
||||
|
||||
self.module_check('xml.parsers.expat')
|
||||
#self.module_check('gobject', 'glib_version', (2, 8))
|
||||
self.module_check('gtk', ('gtk_version', (2, 8)),
|
||||
('pygtk_version', (2, 8)))
|
||||
#self.module_check('gnome')
|
||||
self.module_check('gnomecanvas')
|
||||
#self.module_check('gconf')
|
||||
self.module_check('diacanvas', ('diacanvas_version', (0, 14, 3)))
|
||||
|
||||
print ''
|
||||
if self.config_failed:
|
||||
|
Loading…
x
Reference in New Issue
Block a user