Exemplo n.º 1
0
def search_for_page(notebook: wx.Notebook, page_name: str):
    """
	Search for a page with a specific name inside the notebook.
	"""
    n_pages = notebook.GetPageCount()
    l_pages = [notebook.GetPageText(i) for i in range(n_pages)]
    return page_name in l_pages
Exemplo n.º 2
0
def delete_all_excluding(notebook: wx.Notebook, exclusion_list: list):
    """
	Delete all pages in the notebook, except specified ones.
	:exclusion_list: is a list of string, containing some page names.
	"""
    if exclusion_list:
        l_index = []
        n_pages = notebook.GetPageCount()
        for i in range(n_pages):
            if notebook.GetPageText(i) not in exclusion_list:
                l_index.append(i)  # Gather index of pages to delete
        l_index.sort(reverse=True)
        for index in l_index:
            notebook.DeletePage(index)
    else:
        notebook.DeleteAllPages()
Exemplo n.º 3
0
class MainUI:
    """
    The main portion of the User Interface.
    Used by the main application frame (AppFrame) to host all UML frames,
    the notebook and the project tree.

    Handles the the project files, projects, documents and
    their relationship to the various UI Tree elements and the
    notebook tabs in the UI

    All actions called from AppFrame are executed on the current frame
    """
    MAX_NOTEBOOK_PAGE_NAME_LENGTH: int = 12

    def __init__(self, parent, mediator):
        """

        Args:
            parent:     An AppFrame
            mediator:   Our one and only mediator
        """
        self.logger: Logger = getLogger(__name__)

        from org.pyut.ui.AppFrame import AppFrame   # Prevent recursion import problem
        from org.pyut.general.Mediator import Mediator
        self.__parent:  AppFrame = parent
        self._mediator: Mediator = mediator

        self._projects:       List[PyutProject] = []
        self._currentProject: PyutProject       = cast(PyutProject, None)
        self._currentFrame:   UmlDiagramsFrame  = cast(UmlDiagramsFrame, None)

        if not self._mediator.isInScriptMode():

            self.__splitter:          SplitterWindow = cast(SplitterWindow, None)
            self.__projectTree:       TreeCtrl       = cast(TreeCtrl, None)
            self.__projectTreeRoot:   TreeItemId     = cast(TreeItemId, None)
            self.__notebook:          Notebook       = cast(Notebook, None)
            self.__projectPopupMenu:  Menu = cast(Menu, None)
            self.__documentPopupMenu: Menu = cast(Menu, None)

            self._initializeUIElements()

    def registerUmlFrame(self, frame):
        """
        Register the current UML Frame

        Args:
            frame:
        """
        self._currentFrame = frame
        self._currentProject = self.getProjectFromFrame(frame)

    def showFrame(self, frame):
        self._frame = frame
        frame.Show()

    def getProjects(self):
        """

        Returns:
            Return all projects
        """
        return self._projects

    def isProjectLoaded(self, filename) -> bool:
        """

        Args:
            filename:

        Returns:
            `True` if the project is already loaded
        """
        for project in self._projects:
            if project.getFilename == filename:
                return True
        return False

    def isDefaultFilename(self, filename: str) -> bool:
        """

        Args:
            filename:

        Returns:
            `True` if the filename is the default filename
        """
        return filename == PyutConstants.DefaultFilename

    def openFile(self, filename, project=None) -> bool:
        """
        Open a file

        Args:
            filename:
            project:

        Returns:
            `True` if operation succeeded
        """
        # Exit if the file is already loaded
        if not self.isDefaultFilename(filename) and self.isProjectLoaded(filename):
            PyutUtils.displayError(_("The selected file is already loaded !"))
            return False

        # Create a new project ?
        if project is None:
            project = PyutProject(PyutConstants.DefaultFilename, self.__notebook, self.__projectTree, self.__projectTreeRoot)

        #  print ">>>FileHandling-openFile-3"
        # Load the project and add it
        try:
            if not project.loadFromFilename(filename):
                PyutUtils.displayError(_("The specified file can't be loaded !"))
                return False
            self._projects.append(project)
            #  self._ctrl.registerCurrentProject(project)
            self._currentProject = project
        except (ValueError, Exception) as e:
            PyutUtils.displayError(_(f"An error occurred while loading the project ! {e}"))
            return False

        try:
            if not self._mediator.isInScriptMode():
                for document in project.getDocuments():
                    diagramTitle: str = document.getTitle()
                    shortName:    str = self.shortenNotebookPageFileName(diagramTitle)
                    self.__notebook.AddPage(document.getFrame(), shortName)

                self.__notebookCurrentPage = self.__notebook.GetPageCount()-1
                self.__notebook.SetSelection(self.__notebookCurrentPage)

            if len(project.getDocuments()) > 0:
                self._currentFrame = project.getDocuments()[0].getFrame()

        except (ValueError, Exception) as e:
            PyutUtils.displayError(_(f"An error occurred while adding the project to the notebook {e}"))
            return False
        return True

    def insertFile(self, filename):
        """
        Insert a file in the current project

        Args:
            filename: filename of the project to insert

        """
        # Get current project
        project = self._currentProject

        # Save number of initial documents
        nbInitialDocuments = len(project.getDocuments())

        # Load data...
        if not project.insertProject(filename):
            PyutUtils.displayError(_("The specified file can't be loaded !"))
            return False

        # ...
        if not self._mediator.isInScriptMode():
            try:
                for document in project.getDocuments()[nbInitialDocuments:]:
                    self.__notebook.AddPage(document.getFrame(), document.getFullyQualifiedName())

                self.__notebookCurrentPage = self.__notebook.GetPageCount()-1
                self.__notebook.SetSelection(self.__notebookCurrentPage)
            except (ValueError, Exception) as e:
                PyutUtils.displayError(_(f"An error occurred while adding the project to the notebook {e}"))
                return False

        # Select first frame as current frame
        if len(project.getDocuments()) > nbInitialDocuments:
            self._frame = project.getDocuments()[nbInitialDocuments].getFrame()

    def saveFile(self) -> bool:
        """
        save to the current filename

        Returns:
            `True` if the save succeeds else `False`
        """
        currentProject = self._currentProject
        if currentProject is None:
            PyutUtils.displayError(_("No diagram to save !"), _("Error"))
            return False

        if currentProject.getFilename() is None or currentProject.getFilename() == PyutConstants.DefaultFilename:
            return self.saveFileAs()
        else:
            return currentProject.saveXmlPyut()

    def saveFileAs(self):
        """
        Ask for a filename and save the diagram data

        Returns:
            `True` if the save succeeds else `False`
        """
        if self._mediator.isInScriptMode():
            PyutUtils.displayError(_("Save File As is not accessible in script mode !"))
            return

        # Test if no diagram exists
        if self._mediator.getDiagram() is None:
            PyutUtils.displayError(_("No diagram to save !"), _("Error"))
            return

        # Ask for filename
        filenameOK = False
        # TODO revisit this to figure out how to get rid of Pycharm warning 'dlg referenced before assignment'
        # Bad thing is dlg can be either a FileDialog or a MessageDialog
        dlg: DialogType = cast(DialogType, None)
        while not filenameOK:
            dlg = FileDialog(self.__parent,
                             defaultDir=self.__parent.getCurrentDir(),
                             wildcard=_("Pyut file (*.put)|*.put"),
                             style=FD_SAVE | FD_OVERWRITE_PROMPT)

            # Return False if canceled
            if dlg.ShowModal() != ID_OK:
                dlg.Destroy()
                return False

            # Find if a specified filename is already opened
            filename = dlg.GetPath()

            if len([project for project in self._projects if project.getFilename() == filename]) > 0:
                dlg = MessageDialog(self.__parent,
                                    _("Error ! The filename '%s" + "' correspond to a project which is currently opened !" +
                                      " Please choose another filename !") % str(filename),
                                    _("Save change, filename error"), OK | ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
                return
            filenameOK = True

        project = self._currentProject
        project.setFilename(dlg.GetPath())
        project.saveXmlPyut()

        # Modify notebook text
        for i in range(self.__notebook.GetPageCount()):
            frame = self.__notebook.GetPage(i)
            document = [document for document in project.getDocuments() if document.getFrame() is frame]
            if len(document) > 0:
                document = document[0]
                if frame in project.getFrames():
                    diagramTitle: str = document.getTitle()
                    shortName:    str = self.shortenNotebookPageFileName(diagramTitle)

                    self.__notebook.SetPageText(i, shortName)
            else:
                self.logger.info("Not updating notebook in FileHandling")

        self.__parent.updateCurrentDir(dlg.GetPath())

        project.setModified(False)
        dlg.Destroy()
        return True

    def newProject(self):
        """
        Begin a new project
        """
        project = PyutProject(PyutConstants.DefaultFilename, self.__notebook, self.__projectTree, self.__projectTreeRoot)
        self._projects.append(project)
        self._currentProject = project
        self._currentFrame = None

    def newDocument(self, docType: DiagramType):
        """
        Begin a new document

        Args:
            docType:  Type of document
        """
        project = self._currentProject
        if project is None:
            self.newProject()
            project = self.getCurrentProject()
        frame = project.newDocument(docType).getFrame()
        self._currentFrame  = frame
        self._currentProject = project

        if not self._mediator.isInScriptMode():
            shortName: str = self.shortenNotebookPageFileName(project.getFilename())
            self.__notebook.AddPage(frame, shortName)
            wxYield()
            self.__notebookCurrentPage  = self.__notebook.GetPageCount() - 1
            self.logger.info(f'Current notebook page: {self.__notebookCurrentPage}')
            self.__notebook.SetSelection(self.__notebookCurrentPage)

    def getCurrentFrame(self):
        """

        Returns:
            Get the current frame
        """
        return self._currentFrame

    def getCurrentProject(self) -> PyutProject:
        """
        Get the current working project

        Returns:
            the current project or None if not found
        """
        return self._currentProject

    def getProjectFromFrame(self, frame: UmlDiagramsFrame) -> PyutProject:
        """
        Return the project that owns a given frame

        Args:
            frame:  the frame to get This project

        Returns:
            PyutProject or None if not found
        """
        for project in self._projects:
            if frame in project.getFrames():
                return project
        return cast(PyutProject, None)

    def getCurrentDocument(self) -> PyutDocument:
        """
        Get the current document.

        Returns:
            the current document or None if not found
        """
        project = self.getCurrentProject()
        if project is None:
            return cast(PyutDocument, None)
        for document in project.getDocuments():
            if document.getFrame() is self._currentFrame:
                return document
        return cast(PyutDocument, None)

    def onClose(self) -> bool:
        """
        Close all files

        Returns:
            True if everything is ok
        """
        # Display warning if we are in scripting mode
        if self._mediator.isInScriptMode():
            print("WARNING : in script mode, the non-saved projects are closed without warning")

        # Close projects and ask for unsaved but modified projects
        if not self._mediator.isInScriptMode():
            for project in self._projects:
                if project.getModified() is True:
                    frames = project.getFrames()
                    if len(frames) > 0:
                        frame = frames[0]
                        frame.SetFocus()
                        wxYield()
                        # if self._ctrl is not None:
                            # self._ctrl.registerUMLFrame(frame)
                        self.showFrame(frame)
                    dlg = MessageDialog(self.__parent,
                                        _("Your diagram has not been saved! Would you like to save it ?"),
                                        _("Save changes ?"), YES_NO | ICON_QUESTION)
                    if dlg.ShowModal() == ID_YES:
                        # save
                        if self.saveFile() is False:
                            return False
                    dlg.Destroy()

        # dereference all
        self.__parent = None
        self._mediator = None
        self.__splitter = None
        self.__projectTree = None
        self.__notebook.DeleteAllPages()
        self.__notebook = None
        self.__splitter = None
        self._projects = None
        self._currentProject = None
        self._currentFrame = None

    def setModified(self, theNewValue: bool = True):
        """
        Set the Modified flag of the currently opened diagram

        Args:
            theNewValue:

        """
        if self._currentProject is not None:
            self._currentProject.setModified(theNewValue)
        self._mediator.updateTitle()

    def closeCurrentProject(self):
        """
        Close the current project

        Returns:
            True if everything is ok
        """
        if self._currentProject is None and self._currentFrame is not None:
            self._currentProject = self.getProjectFromFrame(self._currentFrame)
        if self._currentProject is None:
            PyutUtils.displayError(_("No frame to close !"), _("Error..."))
            return False

        # Display warning if we are in scripting mode
        if self._mediator.isInScriptMode():
            self.logger.warning("WARNING : in script mode, the non-saved projects are closed without warning")

        # Close the file
        if self._currentProject.getModified() is True and not self._mediator.isInScriptMode():
            frame = self._currentProject.getFrames()[0]
            frame.SetFocus()
            self.showFrame(frame)

            dlg = MessageDialog(self.__parent, _("Your project has not been saved. " 
                                                 "Would you like to save it ?"), _("Save changes ?"), YES_NO | ICON_QUESTION)
            if dlg.ShowModal() == ID_YES:
                if self.saveFile() is False:
                    return False

        # Remove the frame in the notebook
        if not self._mediator.isInScriptMode():
            # Python 3 update
            pages = list(range(self.__notebook.GetPageCount()))
            pages.reverse()
            for i in pages:
                pageFrame = self.__notebook.GetPage(i)
                if pageFrame in self._currentProject.getFrames():
                    self.__notebook.DeletePage(i)

        self._currentProject.removeFromTree()
        self._projects.remove(self._currentProject)

        self._currentProject = None
        self._currentFrame = None

        return True

    def removeAllReferencesToUmlFrame(self, umlFrame):
        """
        Remove all my references to a given uml frame

        Args:
            umlFrame:

        """
        # Current frame ?
        if self._currentFrame is umlFrame:
            self._currentFrame = None

        # Exit if we are in scripting mode
        if self._mediator.isInScriptMode():
            return

        for i in range(self.__notebook.GetPageCount()):
            pageFrame = self.__notebook.GetPage(i)
            if pageFrame is umlFrame:
                self.__notebook.DeletePage(i)
                break

    def getProjectFromOglObjects(self, oglObjects) -> PyutProject:
        """
        Get a project that owns oglObjects

        Args:
            oglObjects: Objects to find their parents

        Returns:
            PyutProject if found, None else
        """
        for project in self._projects:
            for frame in project.getFrames():
                diagram = frame.getDiagram()
                shapes = diagram.GetShapes()
                for obj in oglObjects:
                    if obj in shapes:
                        self.logger.info(f'obj: {obj} is part of project: {project}')
                        return project

        self.logger.warning(f'The oglObjects: {oglObjects} appear to not belong to any project')
        return cast(PyutProject, None)

    def _initializeUIElements(self):
        """
        Instantiate all the UI elements
        """
        self.__splitter        = SplitterWindow(self.__parent, ID_ANY)
        self.__projectTree     = TreeCtrl(self.__splitter, ID_ANY, style=TR_HIDE_ROOT + TR_HAS_BUTTONS)
        self.__projectTreeRoot = self.__projectTree.AddRoot(_("Root"))

        #  self.__projectTree.SetPyData(self.__projectTreeRoot, None)
        # Expand root, since wx.TR_HIDE_ROOT is not supported under windows
        # Not supported for hidden tree since wx.Python 2.3.3.1 ?
        #  self.__projectTree.Expand(self.__projectTreeRoot)

        # diagram container
        self.__notebook = Notebook(self.__splitter, ID_ANY, style=CLIP_CHILDREN)

        # Set splitter
        self.__splitter.SetMinimumPaneSize(20)
        self.__splitter.SplitVertically(self.__projectTree, self.__notebook, 160)

        self.__notebookCurrentPage = -1

        # Callbacks
        self.__parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED,      self.__onNotebookPageChanged)
        self.__parent.Bind(EVT_TREE_SEL_CHANGED,           self.__onProjectTreeSelChanged)
        self.__projectTree.Bind(EVT_TREE_ITEM_RIGHT_CLICK, self.__onProjectTreeRightClick)

    # noinspection PyUnusedLocal
    def __onNotebookPageChanged(self, event):
        """
        Callback for notebook page changed

        Args:
            event:
        """
        self.__notebookCurrentPage = self.__notebook.GetSelection()
        if self._mediator is not None:      # hasii maybe I got this right from the old pre PEP-8 code
            #  self._ctrl.registerUMLFrame(self._getCurrentFrame())
            self._currentFrame = self._getCurrentFrameFromNotebook()
            self.__parent.notifyTitleChanged()
        # self.__projectTree.SelectItem(getID(self.getCurrentFrame()))
        # TODO : how can I do getID ???

        # Register the current project
        self._currentProject = self.getProjectFromFrame(self._currentFrame)

    def __onProjectTreeSelChanged(self, event: TreeEvent):
        """
        Callback for notebook page changed

        Args:
            event:
        """
        itm: TreeItemId = event.GetItem()
        pyutData: TreeDataType = self.__projectTree.GetItemData(itm)
        self.logger.debug(f'Clicked on: `{pyutData}`')
        # Use our own base type
        if isinstance(pyutData, UmlDiagramsFrame):
            frame: UmlDiagramsFrame = pyutData
            self._currentFrame = frame
            self._currentProject = self.getProjectFromFrame(frame)

            # Select the frame in the notebook
            for i in range(self.__notebook.GetPageCount()):
                pageFrame = self.__notebook.GetPage(i)
                if pageFrame is frame:
                    self.__notebook.SetSelection(i)
                    return
        elif isinstance(pyutData, PyutProject):
            self._currentProject = pyutData

    def _getCurrentFrameFromNotebook(self):
        """
        Get the current frame in the notebook

        Returns:
        """
        # Return None if we are in scripting mode
        if self._mediator.isInScriptMode():
            return None

        noPage = self.__notebookCurrentPage
        if noPage == -1:
            return None
        frame = self.__notebook.GetPage(noPage)
        return frame

    def __onProjectTreeRightClick(self, treeEvent: TreeEvent):

        itemId: TreeItemId = treeEvent.GetItem()
        data = self.__projectTree.GetItemData(item=itemId)
        self.logger.info(f'Item Data: `{data}`')
        if isinstance(data, PyutProject):
            self.__popupProjectMenu()
        elif isinstance(data, UmlDiagramsFrame):
            self.__popupProjectDocumentMenu()

    def __popupProjectMenu(self):

        self._mediator.resetStatusText()

        if self.__projectPopupMenu is None:
            self.logger.info(f'Create the project popup menu')
            [closeProjectMenuID] = PyutUtils.assignID(1)
            popupMenu: Menu = Menu('Actions')
            popupMenu.AppendSeparator()
            popupMenu.Append(closeProjectMenuID, 'Close Project', 'Remove project from tree', ITEM_NORMAL)
            popupMenu.Bind(EVT_MENU, self.__onCloseProject, id=closeProjectMenuID)
            self.__projectPopupMenu = popupMenu

        self.logger.info(f'currentProject: `{self._currentProject}`')
        self.__parent.PopupMenu(self.__projectPopupMenu)

    def __popupProjectDocumentMenu(self):

        if self.__documentPopupMenu is None:

            self.logger.info(f'Create the document popup menu')

            [editDocumentNameMenuID, removeDocumentMenuID] = PyutUtils.assignID(2)

            popupMenu: Menu = Menu('Actions')
            popupMenu.AppendSeparator()
            popupMenu.Append(editDocumentNameMenuID, 'Edit Document Name', 'Change document name', ITEM_NORMAL)
            popupMenu.Append(removeDocumentMenuID,   'Remove Document',    'Delete it',            ITEM_NORMAL)

            popupMenu.Bind(EVT_MENU, self.__onEditDocumentName, id=editDocumentNameMenuID)
            popupMenu.Bind(EVT_MENU, self.__onRemoveDocument,   id=removeDocumentMenuID)

            self.__documentPopupMenu = popupMenu

        self.logger.info(f'Current Document: `{self.getCurrentDocument()}`')
        self.__parent.PopupMenu(self.__documentPopupMenu)

    # noinspection PyUnusedLocal
    def __onCloseProject(self, event: CommandEvent):
        self.closeCurrentProject()

    # noinspection PyUnusedLocal
    def __onEditDocumentName(self, event: CommandEvent):

        self.logger.info(f'self.__notebookCurrentPage: {self.__notebookCurrentPage} nb Selection: {self.__notebook.GetSelection()}')
        if self.__notebookCurrentPage == -1:
            self.__notebookCurrentPage = self.__notebook.GetSelection()    # must be default empty project

        currentDocument: PyutDocument = self.getCurrentDocument()
        dlgEditDocument: DlgEditDocument = DlgEditDocument(parent=self.getCurrentFrame(),
                                                           dialogIdentifier=ID_ANY,
                                                           document=currentDocument)
        dlgEditDocument.Destroy()

        self.__notebook.SetPageText(page=self.__notebookCurrentPage, text=currentDocument.title)
        currentDocument.updateTreeText()

    # noinspection PyUnusedLocal
    def __onRemoveDocument(self, event: CommandEvent):
        """
        Invoked from the popup menu in the tree

        Args:
            event:
        """
        project:         PyutProject  = self.getCurrentProject()
        currentDocument: PyutDocument = self.getCurrentDocument()
        project.removeDocument(currentDocument)

    def shortenNotebookPageFileName(self, filename: str) -> str:
        """
        Return a shorter filename to display; For file names longer
        than `MAX_NOTEBOOK_PAGE_NAME_LENGTH` this method takes the first
        four characters and the last eight as the shortened file name

        Args:
            filename:  The file name to display

        Returns:
            A better file name
        """
        justFileName: str = osPath.split(filename)[1]
        if len(justFileName) > MainUI.MAX_NOTEBOOK_PAGE_NAME_LENGTH:
            firstFour: str = justFileName[:4]
            lastEight: str = justFileName[-8:]
            return f'{firstFour}{lastEight}'
        else:
            return justFileName
Exemplo n.º 4
0
class ConfigFrame(Frame):  # pylint: disable=too-many-public-methods
    """ConfigFrame is used as the primary app context"""

    BOUND_ACTIONS = 12

    PROMPTS = {
        'dirty_values': 'You have unsaved changes. ',
        'probably_modified': 'File has been modified. '
    }

    config = None
    dirty_values = []
    groups = None
    menu_bar = None
    notebook = None

    def __init__(self, parent, title=""):
        Frame.__init__(self,
                       parent=parent,
                       id=ID_ANY,
                       size=(800, 640),
                       title=title)
        self.construct_config()
        self.construct_gui()
        self.bind_events()

    def construct_config(self, config_path=None):
        """Constucts the Rofi config object and parses its groups"""
        self.config = Rofi()
        self.config.build(config_path)
        self.groups = {}
        for _, entry in self.config.config.items():
            if entry.group in self.groups:
                self.groups[entry.group].append(entry)
            else:
                self.groups[entry.group] = [entry]

    def construct_tabs(self):
        """Constructs all available tabs"""
        for key, config_list in self.groups.items():
            page = ConfigPage(self.notebook, config_list)
            self.notebook.AddPage(page, key)
        self.clean_edit_state()

    def construct_notebook(self):
        """Constructs the main Notebook panel"""
        panel = Panel(self)
        self.notebook = Notebook(panel, style=NB_LEFT)
        self.construct_tabs()
        sizer = BoxSizer(HORIZONTAL)
        sizer.Add(self.notebook, 1, EXPAND)
        panel.SetSizer(sizer)

    def construct_gui(self):
        """Constructs ConfigFrame's GUI"""
        self.menu_bar = ConfigFrameMenuBar()
        self.SetMenuBar(self.menu_bar)
        self.status_bar = ConfigFrameStatusBar(self)
        self.SetStatusBar(self.status_bar)
        self.construct_notebook()
        self.toggle_restoration()

    def bind_events(self):
        """Binds events on ConfigFrame"""
        self.Bind(EVT_MENU, self.open, self.menu_bar.open_menu_item)
        self.Bind(EVT_MENU, self.force_refresh_config,
                  self.menu_bar.refresh_menu_item)
        self.Bind(EVT_MENU, self.restore, self.menu_bar.restore_menu_item)
        self.Bind(EVT_MENU, self.save_as, self.menu_bar.save_as_menu_item)
        self.Bind(EVT_MENU, self.save, self.menu_bar.save_menu_item)
        self.Bind(EVT_MENU, self.menu_bar.exit, self.menu_bar.exit_menu_item)
        self.Bind(EVT_MENU, self.modi_launcher, self.menu_bar.launch_menu_item)
        self.Bind(EVT_MENU, self.menu_bar.toggle_display,
                  self.menu_bar.help_values_menu_item)
        self.Bind(EVT_MENU, self.menu_bar.toggle_display,
                  self.menu_bar.man_values_menu_item)
        self.Bind(EVT_CHECKBOX, self.dirty_edit_state)
        self.Bind(EVT_SPINCTRL, self.dirty_edit_state)
        self.Bind(EVT_TEXT, self.dirty_edit_state)

    def modi_launcher(self, event=None):  # pylint: disable=unused-argument
        """Launches a modi selection dialog"""
        ModiLauncher(self.config.available_modi)

    def update_config_entry(self, key_name, entry):
        """Updates the value for a single entry"""
        widget = FindWindowByName(key_name)
        if hasattr(widget, 'GetValue'):
            value = widget.GetValue()
        elif hasattr(widget, 'GetLabel'):
            value = widget.GetLabel()
        else:
            value = entry.current
        self.config.config[key_name].current = value

    def update_config(self):
        """Updates the entire config object"""
        for key_name, entry in self.config.config.items():
            self.update_config_entry(key_name, entry)

    def save(self, event=None):  # pylint: disable=unused-argument
        """Saves the config file"""
        self.update_config()
        self.config.save(backup=self.menu_bar.backup_on_menu_item.IsChecked())
        send('status_update', message='Saved!')
        self.clean_edit_state()
        self.toggle_refresh()
        self.toggle_restoration()

    def toggle_restoration(self, event=None):  # pylint: disable=unused-argument
        """Enables/disables the restore menu item"""
        self.menu_bar.restore_menu_item.Enable(self.config.can_restore())

    def refresh_config(self, event=None, config_path=None):  # pylint: disable=unused-argument
        """Refreshes the config object and controls"""
        current_page = self.notebook.GetSelection()
        self.construct_config(config_path)
        while self.notebook.GetPageCount() > 0:
            self.notebook.DeletePage(0)
        self.construct_tabs()
        if current_page >= 0 and current_page < self.notebook.GetPageCount():
            self.notebook.SetSelection(current_page)
        self.toggle_refresh()
        self.toggle_restoration()

    def restore(self, event=None):  # pylint: disable=unused-argument
        """Restores a previously backed up config"""
        if self.config.can_restore():
            self.config.backup(restore=True)
            self.refresh_config()

    def clean_edit_state(self):
        """Resets the dirty value list"""
        self.dirty_values = []

    def dirty_edit_state(self, event=None):
        """Updates the dirty value list"""
        if event is None:
            return
        control_value = event.EventObject.GetValue()
        control_name = event.EventObject.GetName()
        config_value = self.config.config[control_name].current
        is_dirty = control_value != config_value
        if is_dirty:
            if not control_name in self.dirty_values:
                self.dirty_values.append(control_name)
        else:
            self.dirty_values = [
                key for key in self.dirty_values if control_name != key
            ]
        self.toggle_refresh()

    def toggle_refresh(self):
        """Toggle refresh availability"""
        self.menu_bar.refresh_menu_item.Enable(
            len(self.dirty_values) > 0 or self.config.probably_modified())

    @staticmethod
    def ignore_dirty_state(prompt=None):
        """Checks if dirty state can be abandoned"""
        with MessageDialog(None, "%sContinue?" % prompt, 'Confirm overwrite',
                           YES_NO | ICON_QUESTION) as dialog:
            if ID_YES == dialog.ShowModal():
                return True
        return False

    def force_refresh_config(self, event=None):  # pylint: disable=unused-argument
        """Forces a config refresh"""
        if self.dirty_values:
            if self.ignore_dirty_state(self.PROMPTS['dirty_values']):
                self.refresh_config()
        elif self.config.probably_modified():
            if self.ignore_dirty_state(self.PROMPTS['probably_modified']):
                self.refresh_config()

    def file_dialog(self, style=None):
        """Opens a dialog to find a file"""
        with FileDialog(
                None,
                'Choose a file',
                dirname(self.config.active_file),
                wildcard='Rasi files (*.rasi)|*.rasi|All Files (*.*)|*.*',
                style=style) as dialog:
            if ID_OK == dialog.ShowModal():
                return dialog.GetPath()
        return None

    def pick_save_file(self):
        """Launches a dialog to pick the save location"""
        return self.file_dialog(FD_SAVE | FD_OVERWRITE_PROMPT)

    def save_as(self, event=None):  # pylint: disable=unused-argument
        """Saves the config as an arbitrary file"""
        new_location = self.pick_save_file()
        if new_location:
            self.config.active_file = new_location
        self.save()

    def pick_open_file(self):
        """Launches a dialog to pick the open location"""
        return self.file_dialog(FD_OPEN | FD_FILE_MUST_EXIST)

    def open(self, event=None):  # pylint: disable=unused-argument
        """Opens the chosen config for editing"""
        new_location = self.pick_open_file()
        if new_location:
            self.refresh_config(config_path=new_location)
Exemplo n.º 5
0
class SettingsFrame(Frame):
    def __init__(self, parent, title, server):
        Frame.__init__(self, parent, title=title, size=(500, 400))
        global SETTINGS_FRAME
        SETTINGS_FRAME = self
        self.server = server
        self.setup_xmlrpc_server()
        self.completed = False
        self.notebook = Notebook(self, style=NB_MULTILINE)
        file_menu = Menu()
        self.next_page = file_menu.Append(ID_ANY, '&Next page\tRAWCTRL+TAB',
                                          'Next page')
        self.prev_page = file_menu.Append(ID_ANY,
                                          '&Prev page\tRAWCTRL+SHIFT+TAB',
                                          'Prev page')
        self.Bind(EVT_MENU, self.OnTab, self.next_page)
        self.Bind(EVT_MENU, self.OnTab, self.prev_page)
        exit_item = file_menu.Append(ID_EXIT, '&Exit...',
                                     'Exit Settings Window')
        self.Bind(EVT_MENU, self.prepare_for_exit, exit_item)
        menu_bar = MenuBar()
        menu_bar.Append(file_menu, '&File')
        self.SetMenuBar(menu_bar)
        self.fields = []
        for top in sorted(settings.SETTINGS.keys()):  # pylint: disable=no-member
            self.make_page(top)
        self.CenterOnScreen()
        self.Show()
        self.Bind(EVT_ON_KILL, self.OnKill)
        self.Bind(EVT_ON_COMPLETE, self.OnComplete)
        self.Bind(EVT_CLOSE, self.xmlrpc_kill)
        self.expiration = threading.Timer(300, self.xmlrpc_kill)
        self.expiration.start()

    def OnTab(self, event):
        the_id = event.GetId()
        curr = self.notebook.GetSelection()
        next = curr + 1 if the_id == self.next_page.GetId() else curr - 1
        page_count = self.notebook.GetPageCount()
        next = 0 if next == page_count else page_count - 1 if next < 0 else next
        self.notebook.ChangeSelection(next)

    def OnKill(self, event):
        self.expiration.cancel()
        self.server.shutdown()
        self.Destroy()

    def OnComplete(self, event):
        self.completed = True
        self.Hide()

    def prepare_for_exit(self, e):
        self.Hide()
        self.completed = True
        threading.Timer(10, self.xmlrpc_kill).start()

    def tree_to_dictionary(self, t=None):
        d = {}
        children = self.fields if t is None else t.children
        for field in children:
            value = None
            if isinstance(field.widget, TextCtrl):
                value = field.widget.GetValue()
                if field.text_type == STRING_LIST_SETTING:
                    d[field.original] = [
                        x for x in value.replace(", ", ",").split(",") if x
                    ]  # don't count empty strings
                elif field.text_type == NUMBER_LIST_SETTING:
                    temp_list = (float(x)
                                 for x in value.replace(", ", ",").split(",")
                                 if x)  # don't count empty strings
                    d[field.original] = [
                        int(x) if x.is_integer() else x for x in temp_list
                    ]
                elif field.text_type == NUMBER_SETTING:
                    value = float(value)
                    if value.is_integer():
                        value = int(value)
                    d[field.original] = float(value)
                else:
                    d[field.original] = value.replace("\\", "/")
            elif isinstance(field.widget, (Panel, ScrolledPanel)):
                d[field.original] = self.tree_to_dictionary(field)
            elif isinstance(field.widget, CheckBox):
                d[field.original] = field.widget.GetValue()
        return d

    def setup_xmlrpc_server(self):
        self.server.register_function(self.xmlrpc_get_message, "get_message")
        self.server.register_function(self.xmlrpc_complete, "complete")
        self.server.register_function(self.xmlrpc_kill, "kill")
        server_thread = threading.Thread(target=self.server.serve_forever)
        server_thread.daemon = True
        server_thread.start()

    def xmlrpc_kill(self, e=None):
        wx.PostEvent(SETTINGS_FRAME, OnKillEvent())

    def xmlrpc_get_message(self):
        if self.completed:
            threading.Timer(1, self.xmlrpc_kill).start()
            return self.tree_to_dictionary()
        else:
            return None

    def xmlrpc_complete(self):
        wx.PostEvent(SETTINGS_FRAME, OnCompleteEvent())

    def make_page(self, title):
        page = ScrolledPanel(parent=self.notebook, id=-1)
        vbox = BoxSizer(VERTICAL)
        field = Field(page, title)
        self.get_fields(page, vbox, field)
        self.fields.append(field)
        page.SetupScrolling()
        page.SetSizer(vbox)
        self.notebook.AddPage(page, title)

    def get_fields(self, page, vbox, field):
        for label in sorted(settings.SETTINGS[field.original].keys()):
            hbox = BoxSizer(HORIZONTAL)
            value = settings.SETTINGS[field.original][label]
            lbl = StaticText(page, label=label)
            hbox.Add(lbl, flag=RIGHT, border=8)
            subfield = Field(None, label)
            item = self.field_from_value(page, value, subfield)
            field.add_child(subfield)
            if item is not None:
                hbox.Add(item, proportion=1)
            vbox.Add(hbox, flag=EXPAND | LEFT | RIGHT | TOP, border=5)
            vbox.Add((-1, 5))

    def field_from_value(self, window, value, field):
        item = None
        if isinstance(value, six.string_types):
            item = TextCtrl(window, value=value)
            field.text_type = STRING_SETTING
        elif isinstance(value, list):
            if isinstance(value[0], six.string_types):
                item = TextCtrl(window, value=", ".join(value))
                field.text_type = STRING_LIST_SETTING
            elif isinstance(value[0], numbers.Real):
                item = TextCtrl(window,
                                value=", ".join((str(x) for x in value)))
                field.text_type = NUMBER_LIST_SETTING
        elif isinstance(value, bool):
            item = CheckBox(window, -1, '', (120, 75))
            item.SetValue(value)
        elif isinstance(value, numbers.Real):
            item = TextCtrl(window, value=str(value))
            field.text_type = NUMBER_SETTING
        elif isinstance(value, dict):
            subpage = Panel(window)
            vbox = BoxSizer(VERTICAL)
            for lbl in sorted(value.keys()):
                hbox = BoxSizer(HORIZONTAL)
                value2 = value[lbl]
                label = StaticText(subpage, label=lbl)
                hbox.Add(label, flag=RIGHT, border=8)
                subfield = Field(None, lbl)
                item = self.field_from_value(subpage, value2, subfield)
                field.add_child(subfield)
                if item is not None:
                    hbox.Add(item, proportion=1)
                vbox.Add(hbox, flag=EXPAND | LEFT | RIGHT | TOP, border=5)
                vbox.Add((-1, 5))
            subpage.SetSizer(vbox)
            subpage.Show()
            item = subpage
        else:
            # This is left for bug reporting purposes.
            printer.out(("{} from the field {} was not assigned to " +
                         "{} because type {} wasn't properly handled.").format(
                             value, field, window, type(value)))
        field.widget = item
        return item