# -*- coding: UTF-8 -*-
'''
================================================================================
Project: SiriL-ic ( SiriL Image Companion )

This script structures the SiriL work folder into a subfolder, copies the
astronomical images into the subfolders, and builds the associated SiriL
script. It can also group scripts.
================================================================================
   Author:  M27trognondepomme <pebe92 (at) gmail.com>

This program is provided without any guarantee.

The license is  LGPL-v3
For details, see GNU General Public License, version 3 or later.
                       "https://www.gnu.org/licenses/gpl.html"
================================================================================
'''
import wx.lib.newevent
import wx.adv

import sys
import os
import glob
import time

from sirilic.lib import changelog, tools, buildscript
from sirilic.lib import tabprocessus, tabfiles, tabproperties, tablog
from sirilic.lib import actionsiril, actioncopy, actionclrtmp
from sirilic.lib.constantes import TAB_PROCESSUS, TAB_LOG
from sirilic.lib.constantes import IMAGE, OFFSET, DARK, FLAT, DFLAT, NB_IODF, IODF_SORTED, TYPENAME
from sirilic.lib.constantes import NoLayer
from sirilic.lib.constantes import COMPATIBILITY_SIRIL
from sirilic.ui import gui
from sirilic.lib import scripteditor
from sirilic.lib.tools import GetIconPath


# ==============================================================================
def darkRowFormatter(listctrl, dark=False, toggle=0):
    listItems = [listctrl.GetItem(ii) for ii in range(listctrl.GetItemCount())]
    bgcolor = ("Dark Grey", "Light Grey") if dark else ("Light Blue", "white")
    fgcolor = ("white", "black") if dark else ("black", "black")
    if toggle != 0:
        toggle = 1
    for item in listItems:
        item.SetBackgroundColour(bgcolor[toggle])
        item.SetTextColour(fgcolor[toggle])
        listctrl.SetItem(item)
        toggle = 1 - toggle


# ==============================================================================
class CCallbacks():
    def __init__(self, i_gui, i_db, i_prefs):
        self.th_run = None
        self.i_gui = i_gui
        self.i_proc = i_gui.wprocess
        self.i_log = i_gui.wlog
        self.i_db = i_db
        self.prefs = i_prefs
        self.listProject = i_gui.listProject

        self.prefs.Load()

        self.i_tabfiles = tabfiles.TabFiles(i_gui, i_prefs, self.ChangeOnglet)
        self.i_properties = tabproperties.TabProperties(i_gui, i_prefs)
        self.log = tablog.LogConsole(self.i_log, self.i_log.tlog, self.i_log.lProgress,
                                     self.i_gui.lTraitement, self.i_gui.gProgress, self.CB_run_siril)

        self.log.LogTrace(True, self.prefs.Get('workdir'))

        self.BaseTitle = i_gui.GetTitle()
        self.mem_stdout = sys.stdout
        self.mem_stderr = sys.stderr
        self.fd_stddbg = None
        self.DebugTrace(self.prefs.Get('debug'))

        self.CurrentProject = ""
        self.CurrentImage = ""
        self.bExitWin = False
        self.MemoTreeStr = ""
        self.MemoListProjetStr = ""
        self.ModifiedProject = False
        self.ModifiedDB = False

        self.CurTab = TAB_PROCESSUS

        # Event pour reconstruire la liste des images et l'arbre
        self.RebuildList, EVT_REBUILD_LIST = wx.lib.newevent.NewEvent()
        i_gui.Bind(EVT_REBUILD_LIST, self.CB_RebuildList)

        i_gui.Bind(wx.EVT_CLOSE, self.CB_closed)
        i_gui.Bind(wx.EVT_MENU, self.CB_closed, id=wx.ID_EXIT)

        # Add Icons

        mb = i_gui.menubar
        if sys.platform.startswith('win'):
            mb.mNew.SetBitmap(wx.Bitmap(GetIconPath(
                "m_document-new.png"), wx.BITMAP_TYPE_PNG))
            mb.mLoad.SetBitmap(wx.Bitmap(GetIconPath(
                "m_document-open.png"), wx.BITMAP_TYPE_PNG))
            mb.mSave.SetBitmap(wx.Bitmap(GetIconPath(
                "m_document-save.png"), wx.BITMAP_TYPE_PNG))
            mb.mSaveas.SetBitmap(wx.Bitmap(GetIconPath(
                "m_document-save-as.png"), wx.BITMAP_TYPE_PNG))
            mb.mReverseProject.SetBitmap(wx.Bitmap(GetIconPath(
                "m_edit-find-replace.png"), wx.BITMAP_TYPE_PNG))
            mb.mPrefs.SetBitmap(wx.Bitmap(GetIconPath(
                "m_preferences-desktop.png"), wx.BITMAP_TYPE_PNG))
            mb.mExit.SetBitmap(wx.Bitmap(GetIconPath(
                "m_application-exit.png"), wx.BITMAP_TYPE_PNG))

            mb.mModifyLight.SetBitmap(wx.Bitmap(GetIconPath(
                "m_insert-image.png"), wx.BITMAP_TYPE_PNG))
            mb.mAddLight.SetBitmap(
                wx.Bitmap(GetIconPath("m_list-add.png"), wx.BITMAP_TYPE_PNG))
            mb.mDeleteLight.SetBitmap(
                wx.Bitmap(GetIconPath("m_list-remove.png"), wx.BITMAP_TYPE_PNG))
            mb.mEditProp.SetBitmap(wx.Bitmap(GetIconPath(
                "m_document-page-setup.png"), wx.BITMAP_TYPE_PNG))
            mb.mSetMasters.SetBitmap(wx.Bitmap(GetIconPath(
                "m_document-properties.png"), wx.BITMAP_TYPE_PNG))

            mb.mCpyFiles.SetBitmap(wx.Bitmap(GetIconPath(
                "m_folder-new.png"), wx.BITMAP_TYPE_PNG))
            mb.mRunAll.SetBitmap(wx.Bitmap(GetIconPath(
                "m_applications-system.png"), wx.BITMAP_TYPE_PNG))
            mb.mDelTmpFile.SetBitmap(
                wx.Bitmap(GetIconPath("m_edit-clear.png"), wx.BITMAP_TYPE_PNG))
            mb.mAbortAction.SetBitmap(wx.Bitmap(GetIconPath(
                "m_process-stop.png"), wx.BITMAP_TYPE_PNG))
            mb.mBuildScript.SetBitmap(
                wx.Bitmap(GetIconPath("m_system-run.png"), wx.BITMAP_TYPE_PNG))
            mb.mPartialScript.SetBitmap(
                wx.Bitmap(GetIconPath("m_system-run.png"), wx.BITMAP_TYPE_PNG))
            mb.mEditScript.SetBitmap(wx.Bitmap(GetIconPath(
                "m_accessories-text-editor.png"), wx.BITMAP_TYPE_PNG))
            mb.mRunScript.SetBitmap(wx.Bitmap(GetIconPath(
                "m_applications-system.png"), wx.BITMAP_TYPE_PNG))
            mb.myRunSiril.SetBitmap(
                wx.Bitmap(GetIconPath("m_siril.png"), wx.BITMAP_TYPE_PNG))

            mb.mAbout.SetBitmap(wx.Bitmap(GetIconPath(
                "m_help-about.png"), wx.BITMAP_TYPE_PNG))

        mb.mExpertMode.SetItemLabel(
            "****** " + mb.mExpertMode.GetItemLabel() + " ******")

        i_gui.wlog.bsearchprev.SetBitmap(
            wx.Bitmap(GetIconPath("m_go-previous.png"), wx.BITMAP_TYPE_PNG))
        i_gui.wlog.bsearchnext.SetBitmap(
            wx.Bitmap(GetIconPath("m_go-next.png"), wx.BITMAP_TYPE_PNG))
        i_gui.wlog.bclear.SetBitmap(
            wx.Bitmap(GetIconPath("m_edit-clear.png"), wx.BITMAP_TYPE_PNG))
        i_gui.wlog.bSiril.SetBitmap(
            wx.Bitmap(GetIconPath("m_siril.png"), wx.BITMAP_TYPE_PNG))

        # bind with callback

        i_gui.Bind(wx.EVT_MENU, self.CB_MenuNew, mb.mNew)
        i_gui.Bind(wx.EVT_MENU, self.CB_LoadProject, mb.mLoad)
        i_gui.Bind(wx.EVT_MENU, self.CB_SaveProject, mb.mSave)
        i_gui.Bind(wx.EVT_MENU, self.CB_SaveAsProject, mb.mSaveas)
        i_gui.Bind(wx.EVT_MENU, self.CB_ReverseProject, mb.mReverseProject)
        i_gui.Bind(wx.EVT_MENU, self.CB_Prefs, mb.mPrefs)

        i_gui.Bind(wx.EVT_MENU, self.CB_MenuModify, mb.mModifyLight)
        i_gui.Bind(wx.EVT_MENU, self.CB_MenuAdd, mb.mAddLight)
        i_gui.Bind(wx.EVT_MENU, self.CB_MenuDel, mb.mDeleteLight)
        i_gui.Bind(wx.EVT_MENU, self.CB_GlobProp, mb.mEditProp)
        i_gui.Bind(wx.EVT_MENU, self.CB_DefaultMaster, mb.mSetMasters)

        i_gui.Bind(wx.EVT_MENU, self.CB_filecopy, mb.mCpyFiles)
        i_gui.Bind(wx.EVT_MENU, self.CB_run_all, mb.mRunAll)
        i_gui.Bind(wx.EVT_MENU, self.CB_Clr_tmpfile, mb.mDelTmpFile)
        i_gui.Bind(wx.EVT_MENU, self.CB_kill, mb.mAbortAction)

        i_gui.Bind(wx.EVT_MENU, self.CB_BuildScript, mb.mBuildScript)
        i_gui.Bind(wx.EVT_MENU, self.CB_PartialBuildScript, mb.mPartialScript)
        i_gui.Bind(wx.EVT_MENU, self.CB_edit, mb.mEditScript)
        i_gui.Bind(wx.EVT_MENU, self.CB_run, mb.mRunScript)
        i_gui.Bind(wx.EVT_MENU, self.CB_run_siril, mb.myRunSiril)

        i_gui.Bind(wx.EVT_MENU, self.CB_About, mb.mAbout)

        for ii in range(gui.NB_HISTO):
            i_gui.Bind(wx.EVT_MENU, self.CB_LastProject,
                       id=gui.myID_LASTPRJ[ii])

        i_gui.listProject.Bind(wx.EVT_LIST_ITEM_SELECTED,
                               self.CB_LoadProperties)
        i_gui.listProject.Bind(wx.EVT_LIST_ITEM_DESELECTED,
                               lambda evt, clr=True: self.CB_SaveProperties(evt, clr))
        # i_gui.treeProject.Bind(wx.EVT_SET_FOCUS           , lambda evt, clr=False : self.CB_SaveProperties(evt,clr) )

        self.i_gui.SetAcceleratorTable(wx.AcceleratorTable([
            (wx.ACCEL_NORMAL, wx.WXK_F1, wx.ID_EXIT),
        ]))

        # gestion manuelle des onglets car bug sur MAC OS
        i_gui.b_nb_process.Bind(
            wx.EVT_TOGGLEBUTTON, lambda evt, target_No=0: self.CB_Onglet(evt, target_No))
        i_gui.b_nb_files.Bind(wx.EVT_TOGGLEBUTTON, lambda evt,
                              target_No=1: self.CB_Onglet(evt, target_No))
        i_gui.b_nb_properties.Bind(
            wx.EVT_TOGGLEBUTTON, lambda evt, target_No=2: self.CB_Onglet(evt, target_No))
        i_gui.b_nb_log.Bind(wx.EVT_TOGGLEBUTTON, lambda evt,
                            target_No=3: self.CB_Onglet(evt, target_No))
        self.onglets = [(i_gui.b_nb_process, self.i_proc),
                        (i_gui.b_nb_files, i_gui.wfiles),
                        (i_gui.b_nb_properties, i_gui.wproperties),
                        (i_gui.b_nb_log, self.i_log)]

        self.i_tabfiles.ClrAllListBoxFiles()

        for ii in range(gui.NB_HISTO):
            self.i_gui.mLastProject[ii].SetItemLabel(_("empty history"))

        if type(self.prefs.Get('last_project')) is str:
            # retro compatibility
            self.prefs.Set('last_project', [self.prefs.Get('last_project')])

        if len(self.prefs.Get('last_project')) == 0:
            self.prefs.Set('last_project', [_("empty history")] * gui.NB_HISTO)

        self.SetHisto()

        self.processus = tabprocessus.CProcessus(self.i_proc.f_draw, i_db)
        self.load_project(self.prefs.Get('last_project')[-1], False)
        self.ChangeOnglet(TAB_PROCESSUS)
        self._scripteditor = None

    # --------------------------------------------------------------------------
    def SetProjectTitle(self, projectfilename):
        if len(projectfilename) == 0:
            self.i_gui.SetTitle(self.BaseTitle)
        else:
            prjname = os.path.basename(projectfilename)
            if self.ModifiedProject:
                prjname += " <" + _("modified") + ">"
            self.i_gui.SetTitle(
                self.BaseTitle + "           *** " + _("Project") + " : " + prjname + " ***")

    # --------------------------------------------------------------------------
    def SaveAuto(self):
        self.ChangeOnglet(TAB_PROCESSUS)
        if self.ModifiedProject is True:
            if self.CurrentProject != "":
                dlg = wx.MessageDialog(None, _("Do you save the current project?"),
                                       self.CurrentProject, wx.YES_NO | wx.ICON_QUESTION)
                if dlg.ShowModal() == wx.ID_YES:
                    self.save_project()
            else:
                self.CB_SaveAsProject(None)
                self.RefreshHistoProject()
        self.ModifiedProject = False
        self.ModifiedDB = False

    # --------------------------------------------------------------------------
    def CB_closed(self, _event):
        self.SaveAuto()
        self.DebugTrace(False)
        self.bExitWin = True
        self.SaveHisto()
        self.prefs.Save()
        self.log.LogTrace(False, self.prefs.Get('workdir'))
        self.i_gui.Destroy()

    # --------------------------------------------------------------------------
    def CB_Onglet(self, _event, target_No):
        target_obj = self.onglets[target_No][0]
        value = target_obj.GetValue()
        if value is True:
            self.ChangeOnglet(target_No)

    def ChangeOnglet(self, target_No):
        if target_No == -1:
            target_No = self.CurTab
        if self.CurrentImage == "" and target_No != TAB_LOG:
            target_No = -2

        for ii in range(4):
            target_obj, panel_obj = self.onglets[ii]
            target_obj.InvalidateBestSize()

            if ii == target_No:
                target_obj.SetValue(True)
                panel_obj.Show(True)
                self.CurTab = target_No
            else:
                panel_obj.Show(False)
                target_obj.SetValue(False)
        self.i_gui.SendSizeEvent()
        if (self.CurrentImage == "") or self.bExitWin:
            return
        self.save_properties(self.CurrentImage, False)
        self.UpdateStatus()
        self.i_proc.f_draw.Refresh()
        self.RefreshListTree(False)

    # --------------------------------------------------------------------------
    def SaveHisto(self):
        ll = len(self.prefs.Get('last_project'))
        ll = min(ll, gui.NB_HISTO)
        if ll > 0:
            self.prefs.Set('last_project', self.prefs.Get(
                'last_project')[-ll:])
        else:
            self.prefs.Set('last_project', "")
        return self.prefs

    def SetHisto(self):
        nb = min(gui.NB_HISTO, len(self.prefs.Get('last_project')))
        for jj in range(1, nb + 1):
            if len(self.prefs.Get('last_project')[-jj]) != 0:
                self.i_gui.mLastProject[jj -
                                        1].SetItemLabel(self.prefs.Get('last_project')[-jj])

    # --------------------------------------------------------------------------
    def CB_SaveProperties(self, _event=None, clr=True):
        if self.CurrentImage == "":
            return
        self.save_properties(self.CurrentImage, clr)
        self.CurrentImage = ""
        self.processus.SetCurrentImage("")
        self.ChangeOnglet(-1)
        self.i_proc.f_draw.Refresh()
        self.UpdateStatus()
        if self.ModifiedDB:
            evt = self.RebuildList(attr1="CB_SaveProp")
            wx.PostEvent(self.i_gui, evt)

    def CB_LoadProperties(self, _event):
        item = self.listProject.GetFirstSelected()
        if (item == -1):
            return
        objet = self.listProject.GetItemText(item, 0)
        layer = self.listProject.GetItemText(item, 1)
        session = self.listProject.GetItemText(item, 2)
        SelectedImage = self.i_db.KeyStr(objet, layer, session)
        if self.CurrentImage == SelectedImage:
            return
        if self.CurrentImage != "":
            self.save_properties(self.CurrentImage)
        self.load_properties(SelectedImage)
        self.CurrentImage = SelectedImage
        self.ChangeOnglet(-1)
        self.processus.SetCurrentImage(SelectedImage)
        self.UpdateStatus()
        self.i_proc.f_draw.Refresh()
        if self.ModifiedDB:
            evt = self.RebuildList(attr1="CB_LoadProp")
            wx.PostEvent(self.i_gui, evt)

    def CB_RebuildList(self, _event):
        self.RefreshListTree(False)

    def load_properties(self, imagename):
        files = self.i_db.GetFiles(imagename)
        iodf = self.i_db.GetItem(imagename).Recopie_dB()
        self.i_tabfiles.Set(files)
        self.i_properties.Set(iodf, self.i_db.IsSameProperties())

    def save_properties(self, imagename, clear=True):
        files = self.i_tabfiles.Get()
        modified = self.i_db.SetFiles(imagename, files)
        if clear:
            self.i_tabfiles.ClrAllListBoxFiles()

        if self.i_db.IsSameProperties():
            imagenames = []
            for item in self.i_db.GetItems():
                imagenames.append(self.i_db.KeyStr(item[0], item[1], item[2]))
        else:
            imagenames = [imagename]

        for item in imagenames:
            iodf = self.i_db.GetItemDb(item)
            # Images Properties
            img_prop = self.i_properties.GetImages(IMAGE)
            img_prop["files"] = iodf['Images'][IMAGE].GetFiles()
            modified |= iodf['Images'][IMAGE].SetData(img_prop)
            # Offsets Properties
            offset_prop = self.i_properties.GetImages(OFFSET)
            offset_prop["files"] = iodf['Images'][OFFSET].GetFiles()
            modified |= iodf['Images'][OFFSET].SetData(offset_prop)
            # Darks Properties
            dark_prop = self.i_properties.GetImages(DARK)
            dark_prop["files"] = iodf['Images'][DARK].GetFiles()
            modified |= iodf['Images'][DARK].SetData(dark_prop)
            # Flats Properties
            flat_prop = self.i_properties.GetImages(FLAT)
            flat_prop["files"] = iodf['Images'][FLAT].GetFiles()
            modified |= iodf['Images'][FLAT].SetData(flat_prop)
            # Dark-Flats Properties
            dflat_prop = self.i_properties.GetImages(DFLAT)
            dflat_prop["files"] = iodf['Images'][DFLAT].GetFiles()
            modified |= iodf['Images'][DFLAT].SetData(dflat_prop)

        if (self.ModifiedProject is False) and (modified is True):
            self.ModifiedProject |= modified
            self.SetProjectTitle(self.CurrentProject)
        self.ModifiedProject |= modified
        self.ModifiedDB = modified

    def UpdateStatus(self):
        item = -1
        while 1:
            item = self.listProject.GetNextItem(item, wx.LIST_NEXT_ALL)
            if item == -1:
                return
            ObjectName = self.listProject.GetItemText(item, 0)
            LayerName = self.listProject.GetItemText(item, 1)
            SessionName = self.listProject.GetItemText(item, 2)
            keystr = self.i_db.KeyStr(ObjectName, LayerName, SessionName)
            etat = self.i_db.Item_IsInitialised(keystr)
            litem = wx.ListItem()
            litem.SetId(item)
            litem.SetText(etat)
            litem.SetColumn(3)
            self.listProject.SetItem(litem)
    # --------------------------------------------------------------------------

    def GetLine(self, SelectedOnly=False):
        item = self.listProject.GetFirstSelected()
        if (item == -1) and (SelectedOnly is False):
            item = self.listProject.GetNextItem(-1)
        if item == -1:
            return []
        NB_COL = 4
        line = [self.listProject.GetItemText(
            item, ii) for ii in range(0, NB_COL)]
        return line

    def GetDlgNewProject(self, dlg):
        objname = dlg.eObject.GetLineText(0)
        session = dlg.eSession.GetLineText(0)
        nbsession = dlg.eNbSession.GetValue()
        layer_list = []
        for name, layer in dlg.cbLayer.items():
            if layer.GetValue() == 1:
                layer_list.append(name)

        for nb in range(1, nbsession + 1):
            for layername in layer_list:
                sessionname = session if nbsession == 1 else "%s%02d" % (
                    session, nb)
                omaster = self.prefs.Get('offsetmaster')
                dmaster = self.prefs.Get('darkmaster')
                keystr = self.i_db.KeyStr(objname, layername, sessionname)
                self.i_db.Add(keystr)
                files = self.i_db.GetFiles(keystr)
                if len(omaster) != 0:
                    files[OFFSET] = [omaster]
                if len(dmaster) != 0:
                    files[DARK] = [dmaster]
                if (len(omaster) != 0) or (len(dmaster) != 0):
                    self.i_db.SetFiles(keystr, files)
        self.MemoListProjetStr = ""  # force le refresh
        self.MemoTreeStr = ""
        self.RefreshListTree()

    def CB_MenuNew(self, _event):
        self.SaveAuto()
        with gui.CNewProject(None, -1, "") as dlg:
            size = dlg.eSession.GetSizeFromTextSize(
                dlg.eSession.GetTextExtent('X' * 15))
            dlg.eObject.SetMinSize(size)
            dlg.eSession.SetMinSize(size)
            if dlg.ShowModal() == wx.ID_CANCEL:
                return
            self.Unselect()
            self.CurrentProject = ""
            self.ModifiedProject = True
            self.ModifiedDB = False
            self.SetProjectTitle(self.CurrentProject)
            self.listProject.DeleteAllItems()
            self.i_db.Clr()
            self.GetDlgNewProject(dlg)

    def CB_MenuAdd(self, _event):
        Old = self.GetLine()
        with gui.CModifyProject(None, -1, "") as dlg:
            size = dlg.eSession.GetSizeFromTextSize(
                dlg.eSession.GetTextExtent('X' * 15))
            dlg.eObject.SetMinSize(size)
            dlg.eSession.SetMinSize(size)
            if Old:
                dlg.eObject.SetValue(Old[0])
                dlg.eSession.SetValue(Old[2])
                for name, layer in dlg.rbLayer.items():
                    layer.SetValue(name == Old[1])
            if dlg.ShowModal() == wx.ID_CANCEL:
                return
            New = [None] * 3
            New[0] = dlg.eObject.GetValue()
            New[2] = dlg.eSession.GetValue()
            for name, layer in dlg.rbLayer.items():
                if layer.GetValue():
                    New[1] = name

            self.Unselect()
            keystr_n = self.i_db.KeyStr(New[0], New[1], New[2])
            item_n = self.i_db.GetItemDb(keystr_n)
            if item_n:
                return  # exist deja pas d'ajout
            self.i_db.Add(keystr_n)
            item_n = self.i_db.GetItemDb(keystr_n)
            if Old:
                # copie  de l'item selectionne
                keystr_o = self.i_db.KeyStr(Old[0], Old[1], Old[2])
                self.i_db.Recopie(keystr_n, keystr_o)
            self.CurrentImage = ""
            self.ModifiedProject = True
            self.RefreshListTree()

    def CB_MenuModify(self, _event):
        Old = self.GetLine()
        if not Old:
            return
        with gui.CModifyProject(None, -1, "") as dlg:
            size = dlg.eSession.GetSizeFromTextSize(
                dlg.eSession.GetTextExtent('X' * 15))
            dlg.eObject.SetMinSize(size)
            dlg.eSession.SetMinSize(size)
            dlg.eObject.SetValue(Old[0])
            dlg.eSession.SetValue(Old[2])
            for name, layer in dlg.rbLayer.items():
                layer.SetValue(name == Old[1])
            if dlg.ShowModal() == wx.ID_CANCEL:
                return
            New = [None] * 3
            New[0] = dlg.eObject.GetValue()
            New[2] = dlg.eSession.GetValue()
            for name, layer in dlg.rbLayer.items():
                if layer.GetValue():
                    New[1] = name
            if New == Old:
                return
            self.Unselect()
            keystr_n = self.i_db.KeyStr(New[0], New[1], New[2])
            keystr_o = self.i_db.KeyStr(Old[0], Old[1], Old[2])
            item_o = self.i_db.GetItemDb(keystr_o)
            item_n = self.i_db.GetItemDb(keystr_n)
            if not item_n:
                self.i_db.Add(keystr_n)
                item_n = self.i_db.GetItemDb(keystr_n)
            item_n['Images'] = item_o['Images']
            self.i_db.DelItem(keystr_o)
            self.CurrentImage = ""
            self.ModifiedProject = True
            self.RefreshListTree()

    def CB_MenuDel(self, _event):
        item = self.listProject.GetFirstSelected()
        if item == -1:
            return
        info = self.GetLine()
        keystr = self.i_db.KeyStr(info[0], info[1], info[2])
        self.i_db.DelItem(keystr)
        self.CurrentImage = ""
        self.ModifiedProject = True
        self.RefreshListTree()

    def RefreshListTree(self, select_first=True):
        idx_item = self.listProject.GetFirstSelected()
        linesuniq = sorted(self.i_db.GetItems(
        ), key=lambda xx: xx[0] + "%d" % NoLayer[xx[1]] + xx[2], reverse=True)
        linesuniq_rev = sorted(self.i_db.GetItems(
        ), key=lambda xx: xx[0] + "%d" % NoLayer[xx[1]] + xx[2], reverse=False)

        # Rafraichissement de liste des images si necessaire
        MemoListProjetStr = ""
        for line in linesuniq_rev:
            keystr = self.i_db.KeyStr(line[0], line[1], line[2])
            MemoListProjetStr += line[0] + line[1] + line[2] + line[3]
        if MemoListProjetStr != self.MemoListProjetStr:
            self.MemoListProjetStr = MemoListProjetStr
            self.listProject.DeleteAllItems()
            if sys.platform.startswith('win32'):
                for line in linesuniq:
                    self.listProject.Append(line)
            else:
                for line in linesuniq_rev:
                    self.listProject.Append(line)
            darkRowFormatter(self.listProject)
            if select_first:
                idx_item = 0
            if idx_item >= 0:
                self.listProject.Select(idx_item)
                self.listProject.Focus(idx_item)

        # Rafraichissement de l'arbre des images si necessaire
        MemoTreeStr = ""
        for line in linesuniq_rev:
            keystr = line[0] + line[1] + line[2]
            nbFiles = self.GetNbImages(line[0], line[1], line[2])
            MemoTreeStr += keystr + str(nbFiles)
        if MemoTreeStr != self.MemoTreeStr:
            self.MemoTreeStr = MemoTreeStr
            self.i_gui.treeProject.DeleteAllItems()
            root = self.i_gui.treeProject.AddRoot(
                "Work directory: " + self.prefs.Get('workdir'))
            for line in linesuniq_rev:
                nbFiles = self.GetNbImages(line[0], line[1], line[2])
                self.CreateTree(root, line[0], line[1], line[2], nbFiles)

        self.ModifiedDB = False

    def GetNbImages(self, objet, layer, session):
        keystr = self.i_db.KeyStr(objet, layer, session)
        item = self.i_db.GetItem(keystr)
        NbFiles = []
        for typeimg in IODF_SORTED:
            dbimg = item.GetIodf(typeimg)
            NbFiles.append(len(dbimg.GetFilesExpanded()))
        return NbFiles

    def CreateTree(self, root, objet, layer, session, nbFiles):
        child1 = self.CreateNoeud(objet, root)
        child2 = self.CreateNoeud(layer, child1)
        child3 = self.CreateNoeud(session, child2)
        self.i_gui.treeProject.Expand(root)
        self.i_gui.treeProject.Expand(child1)
        self.i_gui.treeProject.Expand(child2)
        # self.i_gui.treeProject.expand(child3)

        for no in range(NB_IODF):
            nb = nbFiles[no]
            if nb != 0:
                typeimg = IODF_SORTED[no]
                StrImage = str(nb) + " " + TYPENAME[typeimg].lower()
                self.CreateNoeud(StrImage, child3)

    def CreateNoeud(self, name, root):
        item, cookie = self.i_gui.treeProject.GetFirstChild(root)
        while item.IsOk():
            label = self.i_gui.treeProject.GetItemText(item)
            if label == name:
                return item
            item, cookie = self.i_gui.treeProject.GetNextChild(root, cookie)
        return self.i_gui.treeProject.AppendItem(root, name)

    # --------------------------------------------------------------------------
    def CB_GlobProp(self, _event):
        with gui.CGlobProp(None, -1, "") as dlg:
            prop = self.i_db.GetProp()
            dlg.cb_multisession.SetValue(prop['multisession'])
            dlg.cb_stack_intermed.SetValue(prop['stack_intermed'])
            dlg.cb_CFAequa.SetValue(prop['CFAequa'])
            dlg.cb_FastNorm.SetValue(prop['FastNorm'])
            dlg.cb_prop_unique.SetValue(prop['SameProperties'])
            if dlg.ShowModal() == wx.ID_CANCEL:
                return
            changed = self.i_db.SetProp(dlg.cb_multisession.GetValue(),
                                        dlg.cb_stack_intermed.GetValue(),
                                        dlg.cb_CFAequa.GetValue(),
                                        dlg.cb_FastNorm.GetValue(),
                                        dlg.cb_prop_unique.GetValue()
                                        )
            if changed is True:
                self.ModifiedProject = True
                self.ModifiedDB = True
                self.SetProjectTitle(self.CurrentProject)
                self.i_properties.DisplayMode(prop['SameProperties'])

    # --------------------------------------------------------------------------
    def CB_ReverseProject(self, _event):
        self.SaveAuto()
        dir_object = self.prefs.Get('workdir')
        with wx.DirDialog(None, 'Choose work directory', dir_object, style=wx.DD_DEFAULT_STYLE | wx.FD_FILE_MUST_EXIST) as openDirDialog:
            if openDirDialog.ShowModal() == wx.ID_CANCEL:
                return
            dir_object = openDirDialog.GetPath()

        objectname = os.path.basename(dir_object)
        len_dobj = len(dir_object)

        self.Unselect()
        self.CurrentProject = ""
        self.ModifiedProject = True
        self.ModifiedDB = False
        self.SetProjectTitle(self.CurrentProject)
        self.listProject.DeleteAllItems()
        self.i_db.Clr()
        self.MemoListProjetStr = ""
        self.MemoTreeStr = ""

        DirLayerName = NoLayer.keys()
        dirs_layer = []
        for dd in os.listdir(dir_object):
            if os.path.isdir(dir_object + os.sep + dd):
                if dd in DirLayerName:
                    dirs_layer.append(os.path.join(dir_object, dd))

        dirs_session = []
        for dl in dirs_layer:
            for dd in os.listdir(dl):
                if os.path.isdir(dl + os.sep + dd):
                    if dd != "GROUP":
                        ss = os.path.join(dl, dd)
                        dirs_session.append(ss)
                        ss = ss[len_dobj + 1:].split(os.sep)
                        keystr = self.i_db.KeyStr(objectname, ss[0], ss[1])
                        self.i_db.Add(keystr)

        DirTypeName = TYPENAME
        for ds in dirs_session:
            lfiles = [None] * NB_IODF
            for dd in os.listdir(ds):
                if os.path.isdir(ds + os.sep + dd):
                    if dd in DirTypeName:
                        ldir = glob.glob(os.path.join(ds, dd, "SRC", "*.*"))
                        for nb in range(NB_IODF):
                            if TYPENAME[nb] == dd:
                                lfiles[nb] = ldir
                                break
            dm = "MASTER"
            if os.path.isdir(ds + os.sep + dm):
                for nb in range(NB_IODF):
                    if lfiles[nb] is None:
                        dd = TYPENAME[nb]
                        fichier = os.path.join(
                            ds, dm, "master-" + dd.lower() + ".fit")
                        if os.path.exists(fichier):
                            lfiles[nb] = [fichier]

            ss = ds[len_dobj + 1:].split(os.sep)
            keystr = self.i_db.KeyStr(objectname, ss[0], ss[1])
            self.i_db.SetFiles(keystr, lfiles)

        self.RefreshListTree()

    # --------------------------------------------------------------------------
    def CB_LastProject(self, event):
        self.ChangeOnglet(TAB_PROCESSUS)
        for ii in range(gui.NB_HISTO):
            if (event.GetId() == gui.myID_LASTPRJ[ii]):
                self.load_project(self.i_gui.mLastProject[ii].GetItemLabel())
                return

    def CB_LoadProject(self, _event):
        typefile = "Project files |*.prj|All file|*.*"
        flag = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
        defaultdir = os.path.join(self.prefs.Get('workdir'), "Config")
        with wx.FileDialog(None, "Open", defaultdir, "", typefile, flag) as openFileDialog:
            if openFileDialog.ShowModal() == wx.ID_CANCEL:
                return
            self.ChangeOnglet(TAB_PROCESSUS)
            self.load_project(openFileDialog.GetPath())

    def load_project(self, filename, select_first=True):
        self.SaveAuto()
        if not os.path.exists(filename):
            return
        with open(filename, "r", encoding="utf-8") as fd:
            lines = fd.readlines()
            if self.i_db.String2data(lines):
                return
            self.CurrentImage = ""
            self.ModifiedProject = False
            self.ModifiedDB = False
            self.CurrentProject = filename
            self.SetProjectTitle(self.CurrentProject)
            self.RefreshListTree(select_first)
            self.RefreshHistoProject()

    def RefreshHistoProject(self):
        if len(self.CurrentProject) != 0 and self.CurrentProject != self.prefs.Get('last_project')[-1]:
            self.prefs.Get('last_project').append(self.CurrentProject)
        self.SetHisto()

    def Unselect(self):
        for xx in range(self.listProject.GetItemCount()):
            self.listProject.Select(xx, 0)

    def CB_SaveProject(self, event):
        if self.CurrentProject == "":
            self.CB_SaveAsProject(event)
            self.RefreshHistoProject()
            return
        self.save_project()
        self.Unselect()

    def CB_SaveAsProject(self, _event):
        extprj = ".prj"
        typefile = "Project files |*" + extprj + "|All file|*.*"
        flag = wx.FD_SAVE
        defaultdir = os.path.join(self.prefs.Get('workdir'), "Config")
        tools.mkdirs(defaultdir)
        # Nomme le fichier par defaut avec l'objet de la première ligne
        item = self.listProject.GetFirstSelected()
        info = ["noname"] if item == -1 else self.GetLine()

        defaultcfg = info[0] + "_" + _("object") + extprj
        with wx.FileDialog(None, "Save As", defaultdir, defaultcfg, typefile, flag) as openFileDialog:
            if openFileDialog.ShowModal() == wx.ID_CANCEL:
                return
            self.CurrentProject = openFileDialog.GetPath()
            if self.CurrentProject[-4:] != extprj:
                self.CurrentProject = self.CurrentProject + extprj
            self.save_project()
            self.Unselect()

    def save_project(self):
        self.CB_SaveProperties()
        with open(self.CurrentProject, "w", encoding="utf-8") as fd:
            fd.write("# " + _("Don't edit the file") + "\n")
            fd.write(self.i_db.Stringify())
            self.ModifiedProject = False
            self.ModifiedDB = False
        self.SetProjectTitle(self.CurrentProject)

    def CB_Prefs(self, _event):
        with gui.CPrefs(None, -1, "") as dlg:
            dlg.l_version_dbg.GetStaticBox().SetLabel(COMPATIBILITY_SIRIL[1])
            if sys.platform.startswith('win32'):
                width = 65  # gestion des spinctrl avec une taille adaptee a Windows
                dlg.spCPU.SetMinSize((width, -1))
                dlg.spMem.SetMinSize((width, -1))
                dlg.sp_quantif.SetMinSize((width, -1))
                dlg.spCPU.SetInitialSize(
                    dlg.spCPU.GetSizeFromTextSize(dlg.spCPU.GetTextExtent("99")))
                dlg.spMem.SetInitialSize(dlg.spMem.GetSizeFromTextSize(
                    dlg.spMem.GetTextExtent("9.99")))

            dlg.b_workdir.Bind(wx.EVT_BUTTON, lambda evt,
                               target_obj=dlg.textWorkDir: self.CB_SelectDir(evt, target_obj))
            dlg.b_sirilexe.Bind(
                wx.EVT_BUTTON, lambda evt, target_obj=dlg.textSirilExe: self.CB_SelectExe(evt, target_obj))
            dlg.bDetect.Bind(wx.EVT_BUTTON, lambda evt,
                             target_obj=dlg.textSirilExe: self.CB_DetectExe(evt, target_obj))
            dlg.textWorkDir.SetValue(self.prefs.Get('workdir'))
            dlg.cb_WorkDirFinal.SetValue(self.prefs.Get('workdirreturn'))
            dlg.cb_RunSirilAfter.SetValue(self.prefs.Get('siril_autorun'))
            dlg.textSirilExe.SetValue(self.prefs.Get('sirilexe'))
            dlg.cb_cleandir.SetValue(self.prefs.Get('cleandir'))
            dlg.cb_links.SetValue(self.prefs.Get('links'))
            dlg.spCPU.SetValue(self.prefs.Get('nbcpu'))
            dlg.spMem.SetValue(self.prefs.Get('nbmem'))
            dlg.cbDebug.SetValue(self.prefs.Get('debug'))

            dlg.cb_siril_dev.SetValue(self.prefs.Get('siril_dev'))
            dlg.rb_seqmode.SetSelection(self.prefs.Get('seqmode'))
            dlg.cb_savetif.SetValue(self.prefs.Get('savetif'))
            dlg.cb_set32b.SetValue(self.prefs.Get('float32b'))
            dlg.cb_compress.SetValue(self.prefs.Get('compress'))
            dlg.cb_typecomp.SetValue(self.prefs.Get('compress_type'))
            dlg.sp_quantif.SetValue(self.prefs.Get('compress_quantif'))
            dlg.cb_siril_dev.Bind(
                wx.EVT_CHECKBOX, lambda evt, dlgbox=dlg: self.OnToggleEnable(evt, dlgbox))
            self.OnToggleEnable(None, dlg)

            if dlg.ShowModal() == wx.ID_CANCEL:
                return

            self.prefs.Set('workdir', dlg.textWorkDir.GetValue())
            self.prefs.Set('workdirreturn', dlg.cb_WorkDirFinal.GetValue())
            self.prefs.Set('siril_autorun', dlg.cb_RunSirilAfter.GetValue())
            self.prefs.Set('sirilexe', dlg.textSirilExe.GetValue())
            self.prefs.Set('cleandir', dlg.cb_cleandir.GetValue())
            self.prefs.Set('links', dlg.cb_links.GetValue())
            self.prefs.Set('nbcpu', dlg.spCPU.GetValue())
            self.prefs.Set('nbmem', dlg.spMem.GetValue())
            self.prefs.Set('siril_dev', dlg.cb_siril_dev.GetValue())
            self.prefs.Set('seqmode', dlg.rb_seqmode.GetSelection())
            self.prefs.Set('savetif', dlg.cb_savetif.GetValue())
            self.prefs.Set('float32b', dlg.cb_set32b.GetValue())
            self.prefs.Set('compress', dlg.cb_compress.GetValue())
            self.prefs.Set('compress_type', dlg.cb_typecomp.GetValue())
            self.prefs.Set('compress_quantif', dlg.sp_quantif.GetValue())

            self.prefs.Set('debug', dlg.cbDebug.GetValue())
            self.DebugTrace(self.prefs.Get('debug'))
            self.log.LogTrace(True, self.prefs.Get('workdir'))

    def OnToggleEnable(self, _event, dlgbox):
        '''
        if dlgbox.cb_siril_dev.GetValue() :
            # gestion si le mode dev actif
            continue
        else:
            # gestion si le mode dev est inactif
            continue
        '''

    def DebugTrace(self, enable):
        if enable:
            try:
                self.fd_stddbg = open(os.path.join(
                    self.prefs.Get('workdir'), "sirilic-trace.log"), "w",
                    encoding="utf-8")
                sys.stdout = self.fd_stddbg
                sys.stderr = self.fd_stddbg
            except Exception:   # pylint: disable=broad-except
                self.fd_stddbg = None
        else:
            if self.fd_stddbg is not None:
                sys.stdout = self.mem_stdout
                sys.stderr = self.mem_stderr
                self.fd_stddbg.close()
                self.fd_stddbg = None

    def CB_SelectDir(self, _event, target_obj):
        defaultdir = target_obj.GetValue()
        if not os.path.exists(defaultdir):
            defaultdir = self.prefs.Get('workdir')
        dirstyle = wx.DD_DEFAULT_STYLE | wx.FD_FILE_MUST_EXIST
        with wx.DirDialog(None, 'Choose work directory', defaultdir, style=dirstyle) as openDirDialog:
            if openDirDialog.ShowModal() == wx.ID_CANCEL:
                return
            target_obj.SetValue(openDirDialog.GetPath())

    def CB_DetectExe(self, _event, target_obj):
        siril_exe = None
        if sys.platform.startswith('win32'):
            siril_exe = "C:\\Program Files\\SiriL\\bin\\siril.exe"
            if not os.path.exists(siril_exe):
                siril_exe = "C:\\Program Files (x86)\\SiriL\\bin\\siril.exe"
        if sys.platform.startswith('darwin'):
            siril_exe = "/Applications/Siril.app/Contents/Resources/bin/Siril"
            if not os.path.exists(siril_exe):
                siril_exe = "/Applications/SiriL.app/Contents/MacOS/siril"    # SiriL Dev
        if sys.platform.startswith('linux'):
            siril_exe = "/usr/bin/siril"
            if not os.path.exists(siril_exe):
                siril_exe = "/usr/local/bin/siril"
        if siril_exe is not None:
            target_obj.SetValue(siril_exe)

    def CB_DetectEditor(self, _event, target_obj):
        editor = None
        if sys.platform.startswith('win32'):
            editor = "C:\\Program Files\\Notepad++\\notepad++.exe"
            if not os.path.exists(editor):
                editor = "C:\\Windows\\notepad.exe"
        if sys.platform.startswith('darwin'):
            editor = "open -a TextEdit"
        if sys.platform.startswith('linux'):
            editor = "/usr/bin/mousepad"
            if not os.path.exists(editor):
                editor = "/usr/bin/gedit"
        if editor is not None:
            target_obj.SetValue(editor)

    def CB_SelectExe(self, _event, target_obj):
        defaultdir = os.path.dirname(target_obj.GetValue())
        if not os.path.exists(defaultdir):
            defaultdir = self.prefs.Get('workdir')
        flag = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
        typefile = "All files|*.*;*"
        with wx.FileDialog(None, _('Choose Siril executable (full path)'), defaultdir, "", typefile, flag) as openFileDialog:
            if openFileDialog.ShowModal() == wx.ID_CANCEL:
                return
            target_obj.SetValue(openFileDialog.GetPath())

    def CB_SelectMaster(self, _event, target_obj):
        defaultdir = os.path.dirname(target_obj.GetValue())
        if not os.path.exists(defaultdir):
            defaultdir = self.prefs.Get('workdir')
        flag = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
        typefile = "Fit files |*.fit;*.fts;*.fits|All file|*.*"
        with wx.FileDialog(None, _('Choose Master File'), defaultdir, "", typefile, flag) as openFileDialog:
            if openFileDialog.ShowModal() == wx.ID_CANCEL:
                return
            target_obj.SetValue(openFileDialog.GetPath())

    # --------------------------------------------------------------------------
    def CB_kill(self, _event):
        task = tools.GestionThread()
        task.Abort()
        time.sleep(1)
        if self.IsRunningThread():
            self.th_run.raise_exception()

    def IsRunningThread(self):
        if self.th_run is not None:
            if self.th_run.is_alive():
                return True
        return False

    # --------------------------------------------------------------------------
    def CB_Clr_tmpfile(self, _event):
        self.ChangeOnglet(TAB_LOG)
        self.log.clear()
        self.i_gui.Update()
        if self.IsRunningThread():
            self.log.insert("\n*** " + _("task already in progress") + "\n\n")
            return
        self.th_run = actionclrtmp.ClrtmpThread(
            self.log, self.i_db, workdir=self.prefs.Get('workdir'))
        self.th_run.start()

    def CB_filecopy(self, _event):
        self.ChangeOnglet(TAB_LOG)
        self.log.clear()
        self.i_gui.Update()
        if self.IsRunningThread():
            self.log.insert("\n*** " + _("task already in progress") + "\n\n")
            return

        extensionDest = ".fit"
        if self.prefs.Get('compress'):
            extensionDest += ".fz"

        self.th_run = actioncopy.CopyThread(self.log, self.i_db, workdir=self.prefs.Get('workdir'),
                                            flagCopy=not self.prefs.Get(
                                                'links'),
                                            flagClean=self.prefs.Get(
                                                'cleandir'),
                                            extDest=extensionDest)
        self.th_run.start()

    def CB_edit(self, _event,):
        script = self.GetScriptFile()
        if self._scripteditor is not None:
            try:
                self._scripteditor.Destroy()
            except Exception:   # pylint: disable=broad-except
                pass
        self._scripteditor = scripteditor.MyScriptEditorFrame(filename=script)
        self._scripteditor.Show()

    def CB_run_all(self, _event,):
        self.ChangeOnglet(TAB_LOG)
        self.i_gui.Update()
        if self.IsRunningThread():
            self.log.insert("\n*** " + _("task already in progress") + "\n\n")
            return

        script, last_processed_image = self.CB_BuildScript(None)
        if script is None:
            return

        siril_exe = self.prefs.Get('sirilexe')
        self.th_run = actionsiril.SirilScript(self.log, siril_exe, script, last_processed_image,
                                              self.prefs.Get('siril_autorun'), self.prefs.Get('siril_dev'))
        self.th_run.start()

    def CB_run(self, _event):
        self.ChangeOnglet(TAB_LOG)
        self.i_gui.Update()
        if self.IsRunningThread():
            self.log.insert("\n*** " + _("task already in progress") + "\n\n")
            return

        script = self.GetScriptFile()
        if not os.path.exists(script):
            return

        siril_exe = self.prefs.Get('sirilexe')
        self.th_run = actionsiril.SirilScript(self.log, siril_exe, script, "",
                                              False, self.prefs.Get('siril_dev'))
        self.th_run.start()

    def GetScriptFile(self):
        return os.path.join(self.GetScriptDir(), "sirilic.ssf")

    def GetScriptDir(self):
        return os.path.join(self.prefs.Get('workdir'), 'script')

    def CB_BuildScript(self, _event=None):
        self.ChangeOnglet(TAB_LOG)
        self.log.clear()
        tools.mkdirs(self.GetScriptDir())
        script = self.GetScriptFile()
        GProp = self.i_db.GetProp()
        builder = buildscript.CScriptBuilder(
            self.log, script, self.prefs, self.i_db, GProp)
        last_processed_image = builder.Build()
        if last_processed_image is None:
            return (None, None)

        self.log.SetStepNumber(builder.getStepNumber())

        self.log.insert('\n')
        self.log.print_ligne('.')
        self.log.print_titre('.', _("Script building: FINISHED"))
        self.log.print_ligne('.')
        self.log.insert('\n')

        return (script, last_processed_image)

    def CB_run_siril(self, _event):
        self.ChangeOnglet(TAB_LOG)
        actionsiril.run_alone(self.log, self.prefs.Get('sirilexe'))

    # --------------------------------------------------------------------------
    def CB_PartialBuildScript(self, _event):
        self.ChangeOnglet(TAB_LOG)
        script, _last_processed_image = self.CB_BuildScript(None)
        if script is None:
            return

        tag_list = []
        with open(script, "r", encoding="utf-8") as fd:
            lines = fd.readlines()
            for line in lines:
                if line[0:7] == "#TAG#{ "[0:7]:
                    tag_list.append(line[7:-5])

        with gui.CTagProcess(None, -1, "") as dlg:
            dlg.cklb_tag.Clear()
            dlg.cklb_tag.Append(tag_list[:-1])
            dlg.bSelectAll.Bind(wx.EVT_BUTTON, lambda evt,
                                cklb=dlg.cklb_tag: self.OnSelectAll(evt, cklb))
            dlg.bUnSelectAll.Bind(
                wx.EVT_BUTTON, lambda evt, cklb=dlg.cklb_tag: self.OnDeselectAll(evt, cklb))
            if dlg.ShowModal() == wx.ID_CANCEL:
                return
            uncheck_tag = []
            for ii in range(dlg.cklb_tag.GetCount()):
                if not dlg.cklb_tag.IsChecked(ii):
                    uncheck_tag.append(dlg.cklb_tag.GetString(ii))

        comment = ""
        script_w = script[:-4] + "-partial.ssf"
        with open(script_w, "w", newline='', encoding='utf8') as fdw:
            with open(script, "r", encoding='utf8') as fdr:
                lines = fdr.readlines()
                for line in lines:
                    if line[0:7] == "#TAG#{ "[0:7]:
                        tag = line[7:-5]
                        comment = "#" if tag in uncheck_tag else ""
                    fdw.write(comment + line)
                    if line[0:7] == "#TAG#} "[0:7]:
                        comment = ""

        os.unlink(script)
        os.rename(script_w, script)

    def OnSelectAll(self, _event, cklb):
        cklb.SetCheckedItems(range(cklb.GetCount()))

    def OnDeselectAll(self, _event, cklb):
        for ii in range(cklb.GetCount()):
            cklb.Check(ii, check=False)

    # --------------------------------------------------------------------------
    def CB_DefaultMaster(self, _event):
        self.ChangeOnglet(TAB_PROCESSUS)
        self.i_gui.Update()
        self.Unselect()
        with gui.CDlgDefaultMaster(None, -1, "") as dlg:

            dlg.bOffsetMaster.Bind(
                wx.EVT_BUTTON, lambda evt, target_obj=dlg.tOffsetMaster: self.CB_SelectMaster(evt, target_obj))
            dlg.bDarkMaster.Bind(
                wx.EVT_BUTTON, lambda evt, target_obj=dlg.tDarkMaster: self.CB_SelectMaster(evt, target_obj))
            dlg.tOffsetMaster.SetValue(self.prefs.Get('offsetmaster'))
            dlg.tDarkMaster.SetValue(self.prefs.Get('darkmaster'))

            if dlg.ShowModal() == wx.ID_CANCEL:
                return
            omaster = dlg.tOffsetMaster.GetValue()
            dmaster = dlg.tDarkMaster.GetValue()

            self.prefs.Set('offsetmaster', omaster)
            self.prefs.Set('darkmaster', dmaster)

            modified = False
            liste_keystr = self.i_db.GetKeyStrSorted()
            for imagename in liste_keystr:
                files = self.i_db.GetFiles(imagename)
                files[OFFSET] = None
                files[DARK] = None
                if len(omaster) != 0:
                    files[OFFSET] = [omaster]
                if len(dmaster) != 0:
                    files[DARK] = [dmaster]
                self.i_db.SetFiles(imagename, files)
                modified = True
            if modified:
                self.ModifiedDB = True
                self.ModifiedProject = True
                self.SetProjectTitle(self.CurrentProject)
                self.UpdateStatus()

    # --------------------------------------------------------------------------
    def CB_About(self, _event):
        description = _("SiriLic ( SiriL Image Converter) is a software for preparing\n" +
                        "acquisition files (raw, Offset, Flat and Dark) for processing with SiriL software.\n\n" +
                        "It does 4 things:\n\n" +
                        " 1. Structuring the SiriL working directory into sub-folders\n" +
                        " 2. Convert Raw, Offset, Dark or Flat files into SiriL sequence\n" +
                        " 3. Automatically generate the SiriL script according to the files present and the options\n" +
                        " 4. Preprocess directly the images\n\n" +
                        "It can also process the multi-session\n\n"
                        ) + changelog.CHANGELOG

        licence = """
This program is provided without any guarantee.
The license is  LGPL-v3
For details, see GNU General Public License, version 3 or later.
"https://www.gnu.org/licenses/gpl.html"

"""
        info = wx.adv.AboutDialogInfo()

        iconfullname = GetIconPath("sirilic.png")
        info.SetIcon(wx.Icon(iconfullname, wx.BITMAP_TYPE_PNG))
        info.SetName('Sirilic')
        info.SetVersion(changelog.NO_VERSION)
        info.SetDescription(description)
        info.SetCopyright('(C) ' + changelog.DATE_VERSION +
                          ' M27trognondepomme')
        info.SetWebSite('https://gitlab.com/free-astro/sirilic')
        info.SetLicence(licence)
        info.AddDeveloper('M27trognondepomme')
        info.AddDocWriter('M27trognondepomme')
        # info.AddArtist('The Tango crew')
        info.AddTranslator('M27trognondepomme')
        wx.adv.AboutBox(info)
