"""
@package dbmgr.dialogs

@brief DBM-related dialogs

List of classes:
 - dialogs::DisplayAttributesDialog
 - dialogs::ModifyTableRecord
 - dialogs::AddColumnDialog

(C) 2007-2013 by the GRASS Development Team

This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.

@author Martin Landa <landa.martin gmail.com>
@author Refactoring by Stepan Turek <stepan.turek seznam.cz>
        (GSoC 2012, mentor: Martin Landa)
"""

import wx
import wx.lib.scrolledpanel as scrolled

from core.gcmd import RunCommand, GError
from core.debug import Debug
from dbmgr.vinfo import VectorDBInfo, GetUnicodeValue, GetDbEncoding
from gui_core.widgets import IntegerValidator, FloatValidator
from gui_core.wrap import SpinCtrl, Button, StaticText, StaticBox, TextCtrl


class DisplayAttributesDialog(wx.Dialog):
    def __init__(
        self,
        parent,
        map,
        query=None,
        cats=None,
        line=None,
        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
        pos=wx.DefaultPosition,
        action="add",
        ignoreError=False,
    ):
        """Standard dialog used to add/update/display attributes linked
        to the vector map.

        Attribute data can be selected based on layer and category number
        or coordinates.

        :param parent:
        :param map: vector map
        :param query: query coordinates and distance (used for v.edit)
        :param cats: {layer: cats}
        :param line: feature id (requested for cats)
        :param style:
        :param pos:
        :param action: (add, update, display)
        :param ignoreError: True to ignore errors
        """
        self.parent = parent  # mapdisplay.BufferedWindow
        self.map = map
        self.action = action

        # ids/cats of selected features
        # fid : {layer : cats}
        self.cats = {}
        self.fid = -1  # feature id

        # get layer/table/column information
        self.mapDBInfo = VectorDBInfo(self.map)

        layers = self.mapDBInfo.layers.keys()  # get available layers

        # check if db connection / layer exists
        if len(layers) <= 0:
            if not ignoreError:
                dlg = wx.MessageDialog(
                    parent=self.parent,
                    message=_(
                        "No attribute table found.\n\n"
                        "Do you want to create a new attribute table "
                        "and defined a link to vector map <%s>?"
                    )
                    % self.map,
                    caption=_("Create table?"),
                    style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION,
                )
                if dlg.ShowModal() == wx.ID_YES:
                    lmgr = self.parent.lmgr
                    lmgr.OnShowAttributeTable(event=None, selection="layers")

                dlg.Destroy()

            self.mapDBInfo = None

        wx.Dialog.__init__(
            self, parent=self.parent, id=wx.ID_ANY, title="", style=style, pos=pos
        )

        # dialog body
        mainSizer = wx.BoxSizer(wx.VERTICAL)

        # notebook
        self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)

        self.closeDialog = wx.CheckBox(
            parent=self, id=wx.ID_ANY, label=_("Close dialog on submit")
        )
        self.closeDialog.SetValue(True)
        if self.action == "display":
            self.closeDialog.Enable(False)

        # feature id (text/choice for duplicates)
        self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY, size=(150, -1))
        self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
        self.fidText = StaticText(parent=self, id=wx.ID_ANY)

        self.noFoundMsg = StaticText(
            parent=self, id=wx.ID_ANY, label=_("No attributes found")
        )

        self.UpdateDialog(query=query, cats=cats)

        # set title
        if self.action == "update":
            self.SetTitle(_("Update attributes"))
        elif self.action == "add":
            self.SetTitle(_("Define attributes"))
        else:
            self.SetTitle(_("Display attributes"))

        # buttons
        btnCancel = Button(self, wx.ID_CANCEL)
        btnReset = Button(self, wx.ID_UNDO, _("&Reload"))
        btnSubmit = Button(self, wx.ID_OK, _("&Submit"))
        if self.action == "display":
            btnSubmit.Enable(False)

        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(btnCancel)
        btnSizer.AddButton(btnReset)
        btnSizer.SetNegativeButton(btnReset)
        btnSubmit.SetDefault()
        btnSizer.AddButton(btnSubmit)
        btnSizer.Realize()

        mainSizer.Add(self.noFoundMsg, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
        mainSizer.Add(self.notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        fidSizer = wx.BoxSizer(wx.HORIZONTAL)
        fidSizer.Add(
            StaticText(parent=self, id=wx.ID_ANY, label=_("Feature id:")),
            proportion=0,
            border=5,
            flag=wx.ALIGN_CENTER_VERTICAL,
        )
        fidSizer.Add(self.fidMulti, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
        fidSizer.Add(self.fidText, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
        mainSizer.Add(
            fidSizer, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5
        )
        mainSizer.Add(
            self.closeDialog,
            proportion=0,
            flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
            border=5,
        )
        mainSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)

        # bindigs
        btnReset.Bind(wx.EVT_BUTTON, self.OnReset)
        btnSubmit.Bind(wx.EVT_BUTTON, self.OnSubmit)
        btnCancel.Bind(wx.EVT_BUTTON, self.OnClose)
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

        # set min size for dialog
        w, h = self.GetBestSize()
        w += 50
        if h < 200:
            self.SetMinSize((w, 200))
        else:
            self.SetMinSize((w, h))

        if self.notebook.GetPageCount() == 0:
            Debug.msg(2, "DisplayAttributesDialog(): Nothing found!")
            # self.mapDBInfo = None

    def OnSQLStatement(self, event):
        """Update SQL statement"""
        pass

    def IsFound(self):
        """Check for status

        :return: True on attributes found
        :return: False attributes not found
        """
        return bool(self.mapDBInfo and self.notebook.GetPageCount() > 0)

    def GetSQLString(self, updateValues=False):
        """Create SQL statement string based on self.sqlStatement

        Show error message when invalid values are entered.

        If updateValues is True, update dataFrame according to values
        in textfields.
        """
        sqlCommands = []
        # find updated values for each layer/category
        for layer in self.mapDBInfo.layers.keys():  # for each layer
            table = self.mapDBInfo.GetTable(layer)
            key = self.mapDBInfo.GetKeyColumn(layer)
            columns = self.mapDBInfo.GetTableDesc(table)
            for idx in range(len(columns[key]["values"])):  # for each category
                updatedColumns = []
                updatedValues = []
                for name in columns.keys():
                    if name == key:
                        cat = columns[name]["values"][idx]
                        continue
                    ctype = columns[name]["ctype"]
                    value = columns[name]["values"][idx]
                    id = columns[name]["ids"][idx]
                    try:
                        newvalue = self.FindWindowById(id).GetValue()
                    except:
                        newvalue = self.FindWindowById(id).GetLabel()

                    if newvalue:
                        try:
                            if ctype == int:
                                newvalue = int(newvalue)
                            elif ctype == float:
                                newvalue = float(newvalue)
                        except ValueError:
                            GError(
                                parent=self,
                                message=_(
                                    "Column <%(col)s>: Value '%(value)s' needs to be "
                                    "entered as %(type)s."
                                )
                                % {
                                    "col": name,
                                    "value": str(newvalue),
                                    "type": columns[name]["type"].lower(),
                                },
                                showTraceback=False,
                            )
                            sqlCommands.append(None)
                            continue
                    else:
                        if self.action == "add":
                            continue

                    if newvalue != value:
                        updatedColumns.append(name)
                        if newvalue == "":
                            updatedValues.append("NULL")
                        else:
                            if ctype != str:
                                updatedValues.append(str(newvalue))
                            else:
                                updatedValues.append(
                                    "'" + newvalue.replace("'", "''") + "'"
                                )
                        columns[name]["values"][idx] = newvalue

                if self.action != "add" and len(updatedValues) == 0:
                    continue

                if self.action == "add":
                    sqlString = "INSERT INTO %s (%s," % (table, key)
                else:
                    sqlString = "UPDATE %s SET " % table

                for idx in range(len(updatedColumns)):
                    name = updatedColumns[idx]
                    if self.action == "add":
                        sqlString += name + ","
                    else:
                        sqlString += name + "=" + updatedValues[idx] + ","

                sqlString = sqlString[:-1]  # remove last comma

                if self.action == "add":
                    sqlString += ") VALUES (%s," % cat
                    for value in updatedValues:
                        sqlString += value + ","
                    sqlString = sqlString[:-1]  # remove last comma
                    sqlString += ")"
                else:
                    sqlString += " WHERE %s=%s" % (key, cat)
                sqlCommands.append(sqlString)
            # for each category
        # for each layer END

        Debug.msg(3, "DisplayAttributesDialog.GetSQLString(): %s" % sqlCommands)

        return sqlCommands

    def OnReset(self, event=None):
        """Reset form"""
        for layer in self.mapDBInfo.layers.keys():
            table = self.mapDBInfo.layers[layer]["table"]
            key = self.mapDBInfo.layers[layer]["key"]
            columns = self.mapDBInfo.tables[table]
            for idx in range(len(columns[key]["values"])):
                for name in columns.keys():
                    type = columns[name]["type"]
                    value = columns[name]["values"][idx]
                    if value is None:
                        value = ""
                    try:
                        id = columns[name]["ids"][idx]
                    except IndexError:
                        id = wx.NOT_FOUND

                    if name != key and id != wx.NOT_FOUND:
                        self.FindWindowById(id).SetValue(str(value))

    def OnClose(self, event):
        """Closes dialog and removes query layer."""
        frame = self.parent.parent
        frame.dialogs["attributes"] = None
        if hasattr(self, "digit"):
            self.parent.digit.GetDisplay().SetSelected([])
            if frame.IsAutoRendered():
                self.parent.UpdateMap(render=False)
        elif frame.IsAutoRendered():
            frame.RemoveQueryLayer()
            self.parent.UpdateMap(render=True)
        if self.IsModal():
            self.EndModal(wx.ID_OK)
        else:
            self.Destroy()

    def OnSubmit(self, event):
        """Submit records"""
        layer = 1
        close = True
        enc = GetDbEncoding()

        for sql in self.GetSQLString(updateValues=True):
            if not sql:
                close = False
                continue
            sql = sql.encode(enc)

            driver, database = self.mapDBInfo.GetDbSettings(layer)
            Debug.msg(1, "SQL: %s" % sql)
            RunCommand(
                "db.execute",
                parent=self,
                quiet=True,
                input="-",
                stdin=sql,
                driver=driver,
                database=database,
            )

            layer += 1

        if close and self.closeDialog.IsChecked():
            self.OnClose(event)

    def OnFeature(self, event):
        self.fid = int(event.GetString())
        self.UpdateDialog(cats=self.cats, fid=self.fid)

    def GetCats(self):
        """Get id of selected vector object or 'None' if nothing selected

        :param id: if true return ids otherwise cats
        """
        if self.fid < 0:
            return None

        return self.cats[self.fid]

    def GetFid(self):
        """Get selected feature id"""
        return self.fid

    def UpdateDialog(self, map=None, query=None, cats=None, fid=-1, action=None):
        """Update dialog

        :param map: name of vector map
        :param query:
        :param cats:
        :param fid: feature id
        :param action: add, update, display or None

        :return: True if updated
        :return: False
        """
        if action:
            self.action = action
            if action == "display":
                enabled = False
            else:
                enabled = True
            self.closeDialog.Enable(enabled)
            self.FindWindowById(wx.ID_OK).Enable(enabled)

        if map:
            self.map = map
            # get layer/table/column information
            self.mapDBInfo = VectorDBInfo(self.map)

        if not self.mapDBInfo:
            return False

        self.mapDBInfo.Reset()

        layers = self.mapDBInfo.layers.keys()  # get available layers

        # id of selected line
        if query:  # select by position
            data = self.mapDBInfo.SelectByPoint(query[0], query[1])
            self.cats = {}
            if data and "Layer" in data:
                idx = 0
                for layer in data["Layer"]:
                    layer = int(layer)
                    if data["Id"][idx] is not None:
                        tfid = int(data["Id"][idx])
                    else:
                        tfid = 0  # Area / Volume
                    if tfid not in self.cats:
                        self.cats[tfid] = {}
                    if layer not in self.cats[tfid]:
                        self.cats[tfid][layer] = []
                    cat = int(data["Category"][idx])
                    self.cats[tfid][layer].append(cat)
                    idx += 1
        else:
            self.cats = cats

        if fid > 0:
            self.fid = fid
        elif len(self.cats.keys()) > 0:
            self.fid = list(self.cats.keys())[0]
        else:
            self.fid = -1

        if len(self.cats.keys()) == 1:
            self.fidMulti.Show(False)
            self.fidText.Show(True)
            if self.fid > 0:
                self.fidText.SetLabel("%d" % self.fid)
            else:
                self.fidText.SetLabel(_("Unknown"))
        else:
            self.fidMulti.Show(True)
            self.fidText.Show(False)
            choices = []
            for tfid in self.cats.keys():
                choices.append(str(tfid))
            self.fidMulti.SetItems(choices)
            self.fidMulti.SetStringSelection(str(self.fid))

        # reset notebook
        self.notebook.DeleteAllPages()

        for layer in layers:  # for each layer
            if not query:  # select by layer/cat
                if self.fid > 0 and layer in self.cats[self.fid]:
                    for cat in self.cats[self.fid][layer]:
                        nselected = self.mapDBInfo.SelectFromTable(
                            layer,
                            where="%s=%d" % (self.mapDBInfo.layers[layer]["key"], cat),
                        )
                else:
                    nselected = 0

            # if nselected <= 0 and self.action != "add":
            #    continue # nothing selected ...

            if self.action == "add":
                if nselected <= 0:
                    if layer in self.cats[self.fid]:
                        table = self.mapDBInfo.layers[layer]["table"]
                        key = self.mapDBInfo.layers[layer]["key"]
                        columns = self.mapDBInfo.tables[table]
                        for name in columns.keys():
                            if name == key:
                                for cat in self.cats[self.fid][layer]:
                                    self.mapDBInfo.tables[table][name]["values"].append(
                                        cat
                                    )
                            else:
                                self.mapDBInfo.tables[table][name]["values"].append(
                                    None
                                )
                else:  # change status 'add' -> 'update'
                    self.action = "update"

            table = self.mapDBInfo.layers[layer]["table"]
            key = self.mapDBInfo.layers[layer]["key"]
            columns = self.mapDBInfo.tables[table]

            for idx in range(len(columns[key]["values"])):
                for name in columns.keys():
                    if name == key:
                        cat = int(columns[name]["values"][idx])
                        break

                # use scrolled panel instead (and fix initial max height of the
                # window to 480px)
                panel = scrolled.ScrolledPanel(
                    parent=self.notebook, id=wx.ID_ANY, size=(-1, 150)
                )
                panel.SetupScrolling(scroll_x=False)

                self.notebook.AddPage(
                    page=panel,
                    text=" %s %d / %s %d" % (_("Layer"), layer, _("Category"), cat),
                )

                # notebook body
                border = wx.BoxSizer(wx.VERTICAL)

                flexSizer = wx.FlexGridSizer(cols=3, hgap=3, vgap=3)
                flexSizer.AddGrowableCol(2)
                # columns (sorted by index)
                names = [""] * len(columns.keys())
                for name in columns.keys():
                    names[columns[name]["index"]] = name

                for name in names:
                    if name == key:  # skip key column (category)
                        continue

                    vtype = columns[name]["type"].lower()
                    ctype = columns[name]["ctype"]

                    if columns[name]["values"][idx] is not None:
                        if not isinstance(columns[name]["ctype"], str):
                            value = str(columns[name]["values"][idx])
                        else:
                            value = columns[name]["values"][idx]
                    else:
                        value = ""

                    colName = StaticText(parent=panel, id=wx.ID_ANY, label=name)
                    colType = StaticText(
                        parent=panel, id=wx.ID_ANY, label="[%s]:" % vtype
                    )
                    colValue = TextCtrl(parent=panel, id=wx.ID_ANY, value=value)
                    colValue.SetName(name)
                    if ctype == int:
                        colValue.SetValidator(IntegerValidator())
                    elif ctype == float:
                        colValue.SetValidator(FloatValidator())

                    self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
                    if self.action == "display":
                        colValue.SetWindowStyle(wx.TE_READONLY)

                    flexSizer.Add(colName, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
                    flexSizer.Add(
                        colType,
                        proportion=0,
                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT,
                    )
                    flexSizer.Add(
                        colValue,
                        proportion=1,
                        flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL,
                    )
                    # add widget reference to self.columns
                    columns[name]["ids"].append(
                        colValue.GetId()
                    )  # name, type, values, id
                # for each attribute (including category) END
                border.Add(flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
                panel.SetSizer(border)
            # for each category END
        # for each layer END

        if self.notebook.GetPageCount() == 0:
            self.noFoundMsg.Show(True)
        else:
            self.noFoundMsg.Show(False)

        self.Layout()

        return True

    def SetColumnValue(self, layer, column, value):
        """Set attrbute value

        :param column: column name
        :param value: value
        """
        table = self.mapDBInfo.GetTable(layer)
        columns = self.mapDBInfo.GetTableDesc(table)

        for key, col in columns.items():
            if key == column:
                col["values"] = [
                    col["ctype"](value),
                ]
                break


class ModifyTableRecord(wx.Dialog):
    def __init__(
        self,
        parent,
        title,
        data,
        keyEditable=(-1, True),
        id=wx.ID_ANY,
        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
    ):
        """Dialog for inserting/updating table record

        :param data: a list: [(column, value)]
        :param keyEditable: (id, editable?) indicates if textarea for
                            key column is editable(True) or not
        """
        # parent -> VDigitWindow
        wx.Dialog.__init__(self, parent, id, title, style=style)

        self.CenterOnParent()

        self.keyId = keyEditable[0]

        box = StaticBox(parent=self, id=wx.ID_ANY)
        box.Hide()
        self.dataPanel = scrolled.ScrolledPanel(
            parent=self, id=wx.ID_ANY, style=wx.TAB_TRAVERSAL
        )
        self.dataPanel.SetupScrolling(scroll_x=False)

        # buttons
        self.btnCancel = Button(self, wx.ID_CANCEL)
        self.btnSubmit = Button(self, wx.ID_OK, _("&Submit"))
        self.btnSubmit.SetDefault()

        # data area
        self.widgets = []
        cId = 0
        self.usebox = False
        self.cat = None
        winFocus = False

        for column, ctype, ctypeStr, value in data:
            if self.keyId == cId:
                self.cat = int(value)
                if not keyEditable[1]:
                    self.usebox = True
                    box.SetLabel(" %s %d " % (_("Category"), self.cat))
                    box.Show()
                    self.boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
                    cId += 1
                    continue
                else:
                    valueWin = SpinCtrl(
                        parent=self.dataPanel,
                        id=wx.ID_ANY,
                        value=value,
                        min=-1e9,
                        max=1e9,
                        size=(250, -1),
                    )
            else:
                valueWin = TextCtrl(
                    parent=self.dataPanel, id=wx.ID_ANY, value=value, size=(250, -1)
                )
                if ctype == int:
                    valueWin.SetValidator(IntegerValidator())
                elif ctype == float:
                    valueWin.SetValidator(FloatValidator())
                if not winFocus:
                    wx.CallAfter(valueWin.SetFocus)
                    winFocus = True

            label = StaticText(parent=self.dataPanel, id=wx.ID_ANY, label=column)
            ctype = StaticText(
                parent=self.dataPanel, id=wx.ID_ANY, label="[%s]:" % ctypeStr.lower()
            )
            self.widgets.append((label.GetId(), ctype.GetId(), valueWin.GetId()))

            cId += 1

        self._layout()

    def _layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)

        # data area
        dataSizer = wx.FlexGridSizer(cols=3, hgap=3, vgap=3)
        dataSizer.AddGrowableCol(2)

        for labelId, ctypeId, valueId in self.widgets:
            label = self.FindWindowById(labelId)
            ctype = self.FindWindowById(ctypeId)
            value = self.FindWindowById(valueId)

            dataSizer.Add(label, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
            dataSizer.Add(
                ctype, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT
            )
            dataSizer.Add(
                value, proportion=0, flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL
            )

        self.dataPanel.SetAutoLayout(True)
        self.dataPanel.SetSizer(dataSizer)
        dataSizer.Fit(self.dataPanel)

        if self.usebox:
            self.boxSizer.Add(
                self.dataPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5
            )

        # buttons
        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(self.btnCancel)
        btnSizer.AddButton(self.btnSubmit)
        btnSizer.Realize()

        if not self.usebox:
            sizer.Add(self.dataPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        else:
            sizer.Add(self.boxSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)

        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)

        framewidth = self.GetBestSize()[0] + 25
        self.SetMinSize((framewidth, 250))

        self.SetAutoLayout(True)
        self.SetSizer(sizer)
        sizer.Fit(self)

        self.Layout()

    def GetValues(self, columns=None):
        """Return list of values (casted to string).

        If columns is given (list), return only values of given columns.
        """
        valueList = list()
        for labelId, ctypeId, valueId in self.widgets:
            column = self.FindWindowById(labelId).GetLabel()
            if columns is None or column in columns:
                value = GetUnicodeValue(self.FindWindowById(valueId).GetValue())
                valueList.append(value)

        # add key value
        if self.usebox:
            valueList.insert(self.keyId, GetUnicodeValue(str(self.cat)))

        return valueList


class AddColumnDialog(wx.Dialog):
    def __init__(
        self,
        parent,
        title,
        id=wx.ID_ANY,
        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
    ):
        """Dialog for adding column into table"""
        wx.Dialog.__init__(self, parent, id, title, style=style)

        self.CenterOnParent()

        self.data = {}
        self.data["addColName"] = TextCtrl(
            parent=self,
            id=wx.ID_ANY,
            value="",
            size=(150, -1),
            style=wx.TE_PROCESS_ENTER,
        )

        self.data["addColType"] = wx.Choice(
            parent=self, id=wx.ID_ANY, choices=["integer", "double", "varchar", "date"]
        )  # FIXME
        self.data["addColType"].SetSelection(0)
        self.data["addColType"].Bind(wx.EVT_CHOICE, self.OnTableChangeType)

        self.data["addColLength"] = SpinCtrl(
            parent=self, id=wx.ID_ANY, size=(65, -1), initial=250, min=1, max=1e6
        )
        self.data["addColLength"].Enable(False)

        # buttons
        self.btnCancel = Button(self, wx.ID_CANCEL)
        self.btnOk = Button(self, wx.ID_OK)
        self.btnOk.SetDefault()

        self._layout()

    def _layout(self):
        sizer = wx.BoxSizer(wx.VERTICAL)
        addSizer = wx.BoxSizer(wx.HORIZONTAL)

        addSizer.Add(
            StaticText(parent=self, id=wx.ID_ANY, label=_("Column")),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
            border=5,
        )
        addSizer.Add(
            self.data["addColName"],
            proportion=1,
            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
            border=5,
        )

        addSizer.Add(
            StaticText(parent=self, id=wx.ID_ANY, label=_("Type")),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
            border=5,
        )
        addSizer.Add(
            self.data["addColType"],
            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
            border=5,
        )

        addSizer.Add(
            StaticText(parent=self, id=wx.ID_ANY, label=_("Length")),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
            border=5,
        )
        addSizer.Add(
            self.data["addColLength"],
            flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
            border=5,
        )

        sizer.Add(addSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)

        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(self.btnCancel)
        btnSizer.AddButton(self.btnOk)
        btnSizer.Realize()

        sizer.Add(btnSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)

        self.SetSizer(sizer)

        self.Fit()

    def GetData(self):
        """Get inserted data from dialog's widgets"""
        values = {}
        values["name"] = self.data["addColName"].GetValue()
        values["ctype"] = self.data["addColType"].GetStringSelection()
        values["length"] = int(self.data["addColLength"].GetValue())

        return values

    def OnTableChangeType(self, event):
        """Data type for new column changed. Enable or disable
        data length widget"""
        if event.GetString() == "varchar":
            self.data["addColLength"].Enable(True)
        else:
            self.data["addColLength"].Enable(False)
