示例#1
0
def compile(input_file: Gio.File, output_file_path: str) -> int:
    if input_file is None:
        utils.error(_('No input file specified'))
        return 1

    output_file = Gio.File.new_for_commandline_arg(output_file_path)

    import turtlico.lib as lib

    project = lib.ProjectBuffer()
    project.load_from_file(input_file)

    compiler = lib.Compiler(project)

    code, debug_info = compiler.compile(project.code.lines)

    try:
        if output_file.query_exists():
            if not output_file.delete():
                utils.error(_('Failed to overwrite output file'))
        outs = output_file.create(Gio.FileCreateFlags.NONE, None)

        outs.write_all(code.encode(), None)

        outs.close()
    except GLib.Error as e:
        utils.error(_('Cannot save output file: {}').format(e))

    return 0
示例#2
0
 def _run_btn_set_running(self, running: bool):
     if running:
         self._run_btn_img.props.icon_name = 'media-playback-stop-symbolic'
         self._run_btn.props.tooltip_text = _('Stop')
     else:
         self._run_btn_img.props.icon_name = 'media-playback-start-symbolic'
         self._run_btn.props.tooltip_text = _('Run')
示例#3
0
    def do_command_line(self, command_line):
        options: GLib.VariantDict = command_line.get_options_dict()

        input_file: Union[Gio.File, None] = None

        argc = len(command_line.get_arguments())
        if argc == 2:
            input_file = Gio.File.new_for_commandline_arg(
                command_line.get_arguments()[1])
            if not input_file.query_exists():
                utils.warn(_('Input file does not exist'))
        elif argc > 2:
            utils.error(_('Please specify only one input file'))
            return 1

        if options.contains('compile'):
            outf = options.lookup_value('compile', None).get_string()
            return cli.compile(input_file, outf)

        win = self.make_window()
        win.present()
        if input_file is not None:
            win.open_local_file(input_file)

        return 0
示例#4
0
    def __init__(self, scene: lib.SceneInfo, scenebuffer: lib.SceneBuffer,
                 colors: lib.icon.CommandColorScheme):
        super().__init__()

        self.scene = scene
        self._scenebuffer = scenebuffer
        self._colors = colors

        self._label = Gtk.Label.new(scene.props.name)
        self.append(self._label)

        self._entry = Gtk.Entry.new()
        self._entry.props.visible = False
        self._entry.props.secondary_icon_name = 'emblem-ok-symbolic'
        self._entry.props.secondary_icon_tooltip_text = _('Rename')
        self._entry.props.primary_icon_name = 'user-trash-symbolic'
        self._entry.props.primary_icon_tooltip_text = _('Delete this scene')
        self._entry.connect('activate', self._on_entry_activate)
        self._entry.connect('icon-press', self._on_entry_icon_press)
        self.append(self._entry)

        self._drag_source = Gtk.DragSource.new()
        self._drag_source.set_actions(Gdk.DragAction.COPY)
        self._drag_source.connect('prepare', self._on_drag_prepare)
        self.add_controller(self._drag_source)
示例#5
0
    def open_local_file(self, file: Gio.File):
        self._notifications.clear()
        if file is None:
            return

        def error(e):
            self._notifications.add_simple(
                _('Cannot load project: ') + str(e), Gtk.MessageType.ERROR)
            self.buffer.load_from_file(None)

        ext = os.path.splitext(file.get_basename())[1]
        if ext != '.tcp':
            error(Exception(_('Wrong file extenstion')))
            return
        if not file.query_exists():
            error(
                Exception(
                    _('File "{}" is not available').format(file.get_uri())))
            return

        try:
            self.buffer.load_from_file(file)
        except lib.CorruptedFileException as e:
            error(e)
        except lib.MissingPluginException as e:
            error(e)
        except lib.UnavailableCommandException as e:
            error(e)
        except GLib.Error as e:
            error(e)
示例#6
0
 def __str__(self) -> str:
     if len(self.missing_commands) > 5:
         commands = (', '.join([
             f'"{c.definition.help}"'
             for c in list(self.missing_commands)[:5]
         ]) + _(' and others'))
     else:
         commands = ', '.join(
             [f'"{c.definition.help}"' for c in self.missing_commands])
     return _(
         'Command(s) {} from plugin "{}" are present in the program. Please remove them before disabling the plugin.'
     ).format(  # noqa: E501
         commands, self.plugin.name)
    def __init__(self):
        super().__init__(use_header_bar=sys.platform == 'linux')
        self.set_modal(True)
        self.add_button(_('Cancel'), Gtk.ResponseType.CANCEL)
        self.add_button(_('Done'), Gtk.ResponseType.OK)
        self.set_default_response(Gtk.ResponseType.OK)
        self.props.title = ''

        content = self.get_content_area()
        content.props.valign = Gtk.Align.CENTER
        content.props.vexpand = True
        content.props.margin_start = 10
        content.props.margin_end = 10
        content.props.margin_top = 10
        content.props.margin_bottom = 10
示例#8
0
    def _on_plugin_folder_btn_clicked(self, btn):
        folder = os.path.join(GLib.get_user_data_dir(),
                              'turtlico/turtlico/plugins')

        try:
            if os.path.exists(folder):
                if not os.path.isdir(folder):
                    raise Exception(
                        _('Path "{}" does exist but it is not a folder.'))
            else:
                os.makedirs(folder)
        except Exception as e:
            dialog = Gtk.MessageDialog(modal=True,
                                       transient_for=self,
                                       text=str(e),
                                       buttons=Gtk.ButtonsType.OK)

            def callback(dialog, reposne):
                dialog.close()

            dialog.connect('response', callback)

            dialog.show()
            return

        Gtk.show_uri(self, f'file://{folder}', Gdk.CURRENT_TIME)
示例#9
0
 def _on_new_scene_btn_clicked(self, btn):
     try:
         self._scenebuffer.add_scene()
     except GLib.Error as e:
         self._notifications.add_simple(
             _('Cannot create new scene: ') + e.message,
             Gtk.MessageType.ERROR)
示例#10
0
    def __init__(self, version: str):
        super().__init__(application_id='io.gitlab.Turtlico',
                         flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
        self._main_window = None
        self.version = version
        self.settings = Gio.Settings.new('io.gitlab.Turtlico')

        # CLI options
        self.set_option_context_parameter_string('[{}]'.format(_('FILE')))
        self.add_main_option('version', b'v', GLib.OptionFlags.NONE,
                             GLib.OptionArg.NONE,
                             _('Show version of the application'), None)
        self.add_main_option(
            'compile', b'c', GLib.OptionFlags.NONE, GLib.OptionArg.STRING,
            _('Compile file and save ouput to FILE (overwrites if exists)'),
            _('FILE'))
示例#11
0
    def _update_window_title(self):
        project = self.buffer.props.project_file
        if project is None:
            file_name = _('Unnamed')
        else:
            file_name = os.path.splitext(project.get_basename())[0]
        changed = '● ' if self.buffer.props.changed else ''

        self.props.title = f'{changed}{file_name} - Turtlico'
示例#12
0
    def __init__(self, application: app.Application, parent: Gtk.Window):
        translator_credits = _('[Name of translators]')
        if translator_credits == '[Name of translators]':
            translator_credits = None

        super().__init__(
            logo_icon_name='io.gitlab.Turtlico',
            program_name=_('Turtlico'),
            version=application.version,
            comments=_('The easy programming tool'),
            website='https://turtlico.gitlab.io',
            license_type=Gtk.License.GPL_3_0,
            authors=['saytamkenorh https://gitlab.com/matyas5'],
            copyright='Copyright © 2018-2021 saytamkenorh',
            translator_credits=translator_credits,
            transient_for=parent,
            modal=True,
        )
示例#13
0
 def _save(self) -> bool:
     if self._scenebuffer.scene_name is not None:
         try:
             self._scenebuffer.save_scene()
         except GLib.Error as e:
             self._notifications.add_simple(
                 _('Cannot save the scene: ') + e.message,
                 Gtk.MessageType.ERROR)
             return False
     return True
示例#14
0
        def check(ok):
            if not ok:
                return

            def open(file: Gio.File):
                self.open_local_file(file)

            utils.filedialog(self, _('Open project'),
                             Gtk.FileChooserAction.OPEN,
                             [lib.MIME_TURTLICO_PROJECT_FILTER], open)
示例#15
0
    def load_scene(self, name: str):
        if name is None:
            self._clear_scene()
            self.emit('scene-changed')
            return

        if name.strip() == '':
            raise Exception('Invalid scene name')

        self.scene_name = name
        pdir: Gio.File = self._project_file.get_parent()
        file = pdir.get_child(f'{self._project_prefix}{name}.tcs')
        if not file.query_exists():
            self.props.scene_width = 1280
            self.props.scene_height = 720
            self.scene_sprites.clear()
            self.emit('scene-changed')
            return

        file_dis = Gio.DataInputStream.new(file.read())
        content = file_dis.read_upto('\0', 1)[0]
        file_dis.close()

        try:
            struct = json.loads(content)
        except json.decoder.JSONDecodeError:
            raise GLib.Error.new_literal(0, _('File syntax is corrupted'), -1)
        try:
            self.props.scene_width = struct['width']
            self.props.scene_height = struct['height']

            self.scene_sprites.clear()
            for s in struct['sprites']:
                sprite = SceneSprite()
                sprite.x = s['x']
                sprite.y = s['y']
                sprite.name = s['name']
                sprite.id = s['id']
                self.scene_sprites.append(sprite)
        except KeyError as e:
            raise GLib.Error.new_literal(0, _('Missing entry: ') + str(e), -1)

        self.emit('scene-changed')
示例#16
0
 def on_checked(ok):
     if ok:
         try:
             self._scenebuffer.load_scene(selected_scene)
         except GLib.Error as e:
             self._notifications.add_simple(
                 _('Cannot load the scene: ') + e.message,
                 Gtk.MessageType.ERROR)
             self._scenebuffer.load_scene(None)
     else:
         self._select_scene(self._scenebuffer.scene_name)
示例#17
0
    def __update(self):
        # Nothing opened
        if self._sceneview is None:
            self._disable_all()
            return
        buffer: lib.SceneBuffer = self._sceneview.scenebuffer
        sprite: lib.SceneSprite = self._sceneview.props.selection
        if buffer.scene_name is None:
            self._disable_all()
            return

        # Scene properties
        if sprite is None:
            self._id_entry.props.text = _('Scene')
            self._id_entry.props.sensitive = False
            self._reset_spinbutton(self._x_spin)
            self._reset_spinbutton(self._y_spin)
            self._reset_spinbutton(self._width_spin,
                                   True,
                                   buffer.scene_width,
                                   step=10)
            self._reset_spinbutton(self._height_spin,
                                   True,
                                   buffer.scene_height,
                                   step=10)
            self._move_up_btn.props.sensitive = False
            self._move_down_btn.props.sensitive = False
            self._delete_action.set_enabled(False)
            return

        # Sprite properties
        size = self._sceneview.get_sprite_info(sprite)[0]
        width = size.get_width()
        height = size.get_height()

        min_x = math.floor(-buffer.scene_width / 2 + width / 2)
        max_x = math.ceil(buffer.scene_width / 2 + width / 2)
        min_y = math.floor(-buffer.scene_height / 2 + height / 2)
        max_y = math.ceil(buffer.scene_height / 2 + height / 2)

        if self._id_entry.props.text != sprite.id:
            self._id_entry.props.text = sprite.id
        self._id_entry.props.sensitive = True
        self._reset_spinbutton(self._x_spin, True, sprite.x, min_x, max_x)
        self._reset_spinbutton(self._y_spin, True, sprite.y, min_y, max_y)
        self._reset_spinbutton(self._width_spin, False, width)
        self._reset_spinbutton(self._height_spin, False, height)

        layer = self._sceneview.scenebuffer.scene_sprites.index(sprite)
        self._move_up_btn.props.sensitive = layer < len(
            self._sceneview.scenebuffer.scene_sprites) - 1
        self._move_down_btn.props.sensitive = layer > 0

        self._delete_action.set_enabled(True)
示例#18
0
    def __init__(self, debug_info: lib.DebugInfo, stderr: str):
        super().__init__()
        if stderr:
            self.props.program_failed = True
            debug_info = dict(sorted(debug_info.items()))
            stderr_lines = stderr.splitlines()
            utils.debug(f'Program stderr:\n{stderr}')

            coord = None
            # Get program line
            trace_prefix = 'TURTLICO_TRACE:'
            for line in stderr_lines:
                if line.startswith(trace_prefix):
                    trace = line[len(trace_prefix):].split(',')
                    if 'IGNORE' in trace:
                        self.props.program_failed = False
                    # Remove TURTLICO_TRACE from error message
                    stderr_lines = stderr_lines[1:]
                    for t in trace:
                        try:
                            python_line = int(t)
                        except Exception:
                            pass
                        else:
                            t_coord = self._python_to_icons_coord(
                                debug_info, python_line)
                            if t_coord is not None:
                                coord = t_coord
                            break
                    break

            if coord is None:
                coord_msg = _('Error occured outside of your program.')
            else:
                coord_msg = _('Error occured on line {} column {}.').format(
                    coord[1] + 1, coord[0] + 1)

            self.error_message = '{}\n{}'.format(
                '\n'.join(stderr_lines), coord_msg)
        else:
            self.program_failed = False
示例#19
0
    def check_saved(self, callback: Callable[[bool], None], can_cancel=True):
        if ((self._scenebuffer.scene_name is None)
                or (not self._scenebuffer.props.scene_modified)):
            callback(True)
            return
        dialog = Gtk.MessageDialog(
            modal=True,
            transient_for=utils.get_parent_window(self),
            text=(_('The scene contains unsaved changed.') + '\n' +
                  _('Would you like to save the scene?')),
            secondary_text=_('Otherwise the unsaved changed will be lost.'),
            message_type=Gtk.MessageType.QUESTION)
        dialog.add_buttons(_('Yes'), Gtk.ResponseType.YES, _('No'),
                           Gtk.ResponseType.NO)

        if can_cancel:
            dialog.add_buttons(_('Cancel'), Gtk.ResponseType.CANCEL)

        def response(dialog, resposne):
            dialog.close()
            if resposne == Gtk.ResponseType.YES:
                callback(self._save())
                return
            elif resposne == Gtk.ResponseType.NO:
                callback(True)
                return
            callback(False)

        dialog.connect('response', response)
        dialog.show()
示例#20
0
    def _check_saved(self, callback: Callable[[bool], None]):
        """Checks whether the project is saved.
        If it is not then it asks the user whether they want to save it.
        If cancel option is chosen then returns False and the caller should
        stop its operation.
        """
        if not self.buffer.props.changed:
            callback(True)
            return
        dialog = Gtk.MessageDialog(
            modal=True,
            transient_for=self,
            text=(_('The program contains unsaved changed.') + '\n' +
                  _('Would you like to save the project?')),
            secondary_text=_('Otherwise the unsaved changed will be lost.'),
            message_type=Gtk.MessageType.QUESTION)
        dialog.add_buttons(_('Yes'),
                           Gtk.ResponseType.YES, _('No'), Gtk.ResponseType.NO,
                           _('Cancel'), Gtk.ResponseType.CANCEL)

        def response(dialog, response):
            dialog.close()
            if response == Gtk.ResponseType.YES:
                self.save(callback)
                return
            elif response == Gtk.ResponseType.NO:
                callback(True)
                return
            callback(False)

        dialog.connect('response', response)
        dialog.show()
示例#21
0
    def save_as(self, callback: Callable[[bool], None]):
        def save(file: Gio.File):
            if file is None:
                callback(False)
                return
            try:
                ok = self.buffer.save_as(file)
                callback(ok)
            except GLib.Error:
                callback(False)

        utils.filedialog(self, _('Save project as'),
                         Gtk.FileChooserAction.SAVE,
                         [lib.MIME_TURTLICO_PROJECT_FILTER], save)
示例#22
0
    def _delete(self):
        def __delete(dialog, response: Gtk.ResponseType):
            dialog.close()
            if response != Gtk.ResponseType.YES:
                return
            try:
                self.scene.delete()
            except GLib.Error as e:
                err_dialog = Gtk.MessageDialog(
                    modal=True,
                    buttons=Gtk.ButtonsType.CANCEL,
                    transient_for=utils.get_parent_window(self),
                    text=_('Cannot delete the scene'),
                    secondary_text=e.message,
                    message_type=Gtk.MessageType.ERROR)

                def close(dialog, reposne):
                    err_dialog.close()

                err_dialog.connect('response', close)
                err_dialog.show()
            self.unparent()

        dialog = Gtk.MessageDialog(
            modal=True,
            transient_for=utils.get_parent_window(self),
            text=_('Do you really want to permanently delete the scene "{}"?'
                   ).format(self.scene.name),
            message_type=Gtk.MessageType.QUESTION)
        dialog.add_buttons(_('Delete'), Gtk.ResponseType.YES, _('Cancel'),
                           Gtk.ResponseType.CANCEL)
        delete_btn = dialog.get_widget_for_response(Gtk.ResponseType.YES)
        delete_btn.get_style_context().add_class('destructive-action')

        dialog.connect('response', __delete)
        dialog.show()
示例#23
0
 def _parse_def(self):
     self.keyword_level += 1
     if self.line_start_command.id != 'def':
         err = _('Functions have to start on a separate line.')
         self._append_line(f"{self.indentation}raise SyntaxError('{err}')")
         return
     fn_name = self._next_icon(1)
     block_start = self._next_icon(2)
     if (fn_name.definition.id == 'obj'
             and block_start.definition.id == ':'):
         self._append_line(f'{self.indentation}def {fn_name.data}()')
         self.x += 1
         return
     self._append_line(f'{self.indentation}def ')
     return
示例#24
0
    def _on_debugging_done(self, debugger, result: DebuggingReuslt):
        self.debugger.dispose()
        self.debugger = None
        self._run_btn_set_running(False)

        if result.props.program_failed:
            dialog = Gtk.MessageDialog(
                transient_for=self,
                modal=True,
                buttons=Gtk.ButtonsType.OK,
                text=_('Program crashed'),
                secondary_text=result.props.error_message,
                message_type=Gtk.MessageType.ERROR)
            dialog.show()

            def close(dialog, reposne):
                dialog.close()

            dialog.connect('response', close)
示例#25
0
    def _rename(self):
        if self._entry.props.text.strip() == '':
            return
        try:
            self.scene.rename(self._entry.props.text)
        except GLib.Error as e:
            dialog = Gtk.MessageDialog(
                modal=True,
                buttons=Gtk.ButtonsType.CANCEL,
                transient_for=utils.get_parent_window(self),
                text=_('Cannot rename the scene'),
                secondary_text=e.message,
                message_type=Gtk.MessageType.ERROR)

            def close(dialog, reposne):
                dialog.close()

            dialog.connect('response', close)
            dialog.show()
        self.cancel_edit()
示例#26
0
    def _on_state_changed(self, obj, prop) -> bool:
        try:
            self._project.set_plugin_enabled(self.plugin.id,
                                             self.widget.switch.props.active)
        except lib.RemovedUsedPluginException as e:
            dialog = Gtk.MessageDialog(modal=True,
                                       transient_for=utils.get_parent_window(
                                           self.widget),
                                       text=_('Cannot disable the plugin'),
                                       secondary_text=str(e),
                                       message_type=Gtk.MessageType.ERROR,
                                       buttons=Gtk.ButtonsType.OK)

            def response(dialog, reposne):
                dialog.close()

            dialog.connect('response', response)

            dialog.show()
        self._set_state()
示例#27
0
        def __delete(dialog, response: Gtk.ResponseType):
            dialog.close()
            if response != Gtk.ResponseType.YES:
                return
            try:
                self.scene.delete()
            except GLib.Error as e:
                err_dialog = Gtk.MessageDialog(
                    modal=True,
                    buttons=Gtk.ButtonsType.CANCEL,
                    transient_for=utils.get_parent_window(self),
                    text=_('Cannot delete the scene'),
                    secondary_text=e.message,
                    message_type=Gtk.MessageType.ERROR)

                def close(dialog, reposne):
                    err_dialog.close()

                err_dialog.connect('response', close)
                err_dialog.show()
            self.unparent()
示例#28
0
 def _parse_data(self, offset=0, toplevel=True) -> Union[str, None]:
     next_icon = self._next_icon(offset)
     if next_icon is None:
         return None
     if (next_icon.definition.command_type == lib.CommandType.LITERAL_CONST
         ):
         return next_icon.definition.function
     if next_icon.data is not None:
         if (next_icon.definition.command_type == lib.CommandType.LITERAL):
             code, modules = next_icon.definition.function(
                 next_icon.data, toplevel)
             assert isinstance(modules, tuple)
             self.modules_to_load.update(
                 dict(zip(modules, [None] * len(modules))))
             return code
         elif (next_icon.definition.command_type !=
               lib.CommandType.INTERNAL):
             raise Exception(
                 _('Command {} can not have any data (has "{}")').format(
                     next_icon.definition, next_icon.data))
     return None
示例#29
0
    def _on_sidebar_list_box_row_activated(self, box, row: Gtk.ListBoxRow):
        if row == self._sidebar_project_props_row:
            dialog = windows.ProjectPropsWindow(self.props.project_buffer,
                                                self.props.window)
            dialog.show()
        if row == self._sidebar_scene_editor_row:
            if self.props.project_buffer.props.project_file is None:
                self.props.window.remove_app_notifications()
                self.props.window.add_notification(
                    _('Please save the project before using scenes'))
            else:
                if self._scene_editor is not None:
                    self._scene_editor.present_with_time(Gdk.CURRENT_TIME)
                    return
                self._scene_editor = windows.SceneEditorWindow(
                    self.props.project_buffer, self.props.window, self._colors)

                def clear(win):
                    self._scene_editor = None

                self._scene_editor.connect('unmap', clear)
                self._scene_editor.show()
示例#30
0
    def run(self):
        if self.props.running:
            return
        # Sets something to subprocpid in order to prevent
        # from starting two threads at once
        self.subprocpid_lock.acquire()
        self.subprocpid = -1
        self.subprocpid_lock.release()

        self.props.running = True
        # The child program is run as a separate process due to safety reasons
        launcher = _launcher.format(self.path)
        if self.props.use_idle:
            launcher += _idle_exit.format(
                _('Press enter to close this window'))

        args = [_get_python()]
        if self.props.use_idle:
            args.extend(['-m', 'idlelib', '-t', 'Turtlico'])
        args.extend(['-c', launcher])

        thread = threading.Thread(
            target=self._run_child, args=[args], daemon=True)
        thread.start()