Beispiel #1
0
    def __init__(self, *args, **kwargs):
        BitmapDisplayFrame.__init__(self, *args, **kwargs)
        for w in (self.hsb, self.vsb, self.root_canvas):
            w.pack_forget()

        self.preview_label = tk.Label(self, text="Bitmap preview\t")
        self.image_canvas = tk.Canvas(self, highlightthickness=0)
        self.channel_menu = ScrollMenu(self,
                                       menu_width=9,
                                       can_scroll=True,
                                       variable=self.channel_index)
        self.save_button = ttk.Button(self,
                                      width=11,
                                      text="Save as...",
                                      command=self.save_as)

        padx = self.horizontal_padx
        pady = self.horizontal_pady

        self.image_canvas.config(width=1, height=1)
        self.preview_label.pack(side='left', padx=padx, pady=pady, fill='x')
        self.channel_menu.pack(side='left', padx=padx, pady=pady)
        self.save_button.pack(side='left', padx=padx, pady=pady)
        self.image_canvas.pack(side='left',
                               padx=padx,
                               pady=pady,
                               fill='x',
                               expand=True)

        self.apply_style()
Beispiel #2
0
    def __init__(self, master, bitmap_tag=None, *args, **kwargs):
        self.bitmap_tag = bitmap_tag
        textures = kwargs.get('textures', ())
        BitmapDisplayFrame.__init__(self, master, *args, **kwargs)
        self.sequence_index = tk.IntVar(self)
        self.sprite_index   = tk.IntVar(self)

        labels = []
        labels.append(tk.Label(self.controls_frame0, text="Sequence index"))
        labels.append(tk.Label(self.controls_frame1, text="Sprite index"))
        for lbl in labels:
            lbl.config(width=15, anchor='w')

        self.sequence_menu = ScrollMenu(self.controls_frame0, menu_width=7,
                                        variable=self.sequence_index)
        self.sprite_menu = ScrollMenu(self.controls_frame1, menu_width=7,
                                      variable=self.sprite_index)

        padx = self.horizontal_padx
        pady = self.horizontal_pady
        for lbl in labels:
            lbl.pack(side='left', padx=(15, 0), pady=pady)
        self.sequence_menu.pack(side='left', padx=padx, pady=pady)
        self.sprite_menu.pack(side='left', padx=padx, pady=pady)
        self.write_trace(self.sequence_index, self.sequence_changed)
        self.write_trace(self.sprite_index,   self.sprite_changed)

        self.change_textures(textures)
        self.apply_style()
Beispiel #3
0
    def setup_window(self, *args, **kwargs):
        ConverterBase.setup_window(self, *args, **kwargs)

        self.settings_frame = tk.LabelFrame(self, text="Conversion settings")
        self.from_label = tk.Label(self.settings_frame, text="from")
        self.src_menu = ScrollMenu(
            self.settings_frame,
            options=self.object_types,
            menu_width=20,
            sel_index=0,
        )
        self.to_label = tk.Label(self.settings_frame, text="to")
        self.dst_menu = ScrollMenu(
            self.settings_frame,
            options=self.object_types,
            menu_width=20,
            sel_index=10,
        )

        self.pack_widgets()
        self.apply_style()
Beispiel #4
0
    def __init__(self, *args, **kwargs):
        kwargs.update(relief='flat', bd=0, highlightthickness=0)
        data_frame.DataFrame.__init__(self, *args, **kwargs)

        try:
            sel_index = self.node.get_index()
        except Exception:
            sel_index = -1

        # make the widgets
        self.content = tk.Frame(self, relief='flat', bd=0)

        self.display_comment()

        self.title_label = tk.Label(
            self.content,
            text=self.gui_name,
            justify='left',
            anchor='w',
            width=self.title_size,
            disabledforeground=self.text_disabled_color)
        self.sel_menu = ScrollMenu(self.content,
                                   f_widget_parent=self,
                                   menu_width=self.widget_width,
                                   sel_index=sel_index,
                                   max_index=self.desc.get('ENTRIES', 0) - 1,
                                   disabled=self.disabled,
                                   default_text="<INVALID>",
                                   option_getter=self.get_options,
                                   callback=self.select_option)

        if self.gui_name != '':
            self.title_label.pack(side="left", fill="x")
        self.content.pack(fill="x", expand=True)
        self.sel_menu.pack(side="left", fill="x")
        self.populate()
        self.pose_fields()
        self._initialized = True
Beispiel #5
0
    def __init__(self, *args, **kwargs):
        kwargs.update(relief='flat', bd=0, highlightthickness=0)
        data_frame.DataFrame.__init__(self, *args, **kwargs)

        sel_index = -1 if self.node is None else self.node + 1

        # make the widgets
        self.content = tk.Frame(self, relief='flat', bd=0)
        self.title_label = tk.Label(
            self.content,
            text=self.gui_name,
            justify='left',
            anchor='w',
            width=self.title_size,
            disabledforeground=self.text_disabled_color)
        self.sel_menu = ScrollMenu(self.content,
                                   f_widget_parent=self,
                                   menu_width=self.widget_width,
                                   sel_index=sel_index,
                                   max_index=0,
                                   disabled=self.disabled,
                                   default_text="<INVALID>",
                                   option_getter=self.get_options,
                                   callback=self.select_option)

        self.sel_menu.bind('<FocusIn>', self.flag_sanity_change)
        self.sel_menu.arrow_button.bind('<FocusIn>', self.flag_sanity_change)
        self.sel_menu.bind('<Enter>', self.flag_sanity_change)
        self.sel_menu.arrow_button.bind('<Enter>', self.flag_sanity_change)
        self.sel_menu.options_volatile = 'DYN_NAME_PATH' in self.desc

        if self.gui_name != '':
            self.title_label.pack(side="left", fill="x")
        self.content.pack(fill="x", expand=True)
        self.sel_menu.pack(side="left", fill="x")
        self.populate()
        self._initialized = True
Beispiel #6
0
class UnionFrame(container_frame.ContainerFrame):
    '''Used for enumerator nodes. When clicked, creates
    a dropdown box of all available enumerator options.'''

    option_cache = None
    u_node_widgets_by_u_index = ()

    def __init__(self, *args, **kwargs):
        field_widget.FieldWidget.__init__(self, *args, **kwargs)
        self.u_node_widgets_by_u_index = {}

        if self.f_widget_parent is None:
            self.pack_padx = self.pack_pady = 0

        kwargs.update(relief='flat',
                      bd=0,
                      highlightthickness=0,
                      bg=self.default_bg_color)

        show_frame = bool(
            kwargs.pop('show_frame', not self.blocks_start_hidden))
        if self.is_empty and self.hide_if_blank:
            show_frame = False

        tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs))

        self.show = tk.BooleanVar(self)
        self.show.set(show_frame)

        max_u_index = len(self.desc['CASE_MAP'])
        u_index = getattr(self.node, "u_index", None)
        if u_index is None:
            u_index = max_u_index

        toggle_text = '-' if show_frame else '+'

        self.title = tk.Frame(self, relief='raised')
        self.show_btn = ttk.Checkbutton(self.title,
                                        width=3,
                                        text=toggle_text,
                                        command=self.toggle_visible,
                                        style='ShowButton.TButton')
        self.title_label = tk.Label(self.title,
                                    text=self.gui_name,
                                    anchor='w',
                                    width=self.title_size,
                                    justify='left')
        self.title_label.font_type = "frame_title"
        self.sel_menu = ScrollMenu(self.title,
                                   f_widget_parent=self,
                                   sel_index=u_index,
                                   max_index=max_u_index,
                                   disabled=self.disabled,
                                   callback=self.select_option,
                                   option_getter=self.get_options)

        self.show_btn.pack(side="left")
        self.title_label.pack(side="left", fill="x", expand=True)
        self.sel_menu.pack(side="left", fill="x")
        self.title.pack(fill="x", expand=True)

        self.content = tk.Frame(self, relief="sunken")

        # make the default raw bytes union frame
        self.raw_frame = tk.Frame(self.content, relief="flat", bd=0)
        self.raw_label = tk.Label(self.raw_frame,
                                  text='DataUnion',
                                  width=self.title_size,
                                  anchor='w',
                                  disabledforeground=self.text_disabled_color)
        self.import_btn = ttk.Button(self.raw_frame,
                                     text='Import',
                                     command=self.import_node,
                                     width=7)
        self.export_btn = ttk.Button(self.raw_frame,
                                     text='Export',
                                     command=self.export_node,
                                     width=7)

        self.raw_label.pack(side="left", expand=True, fill='x')
        for w in (self.export_btn, self.import_btn):
            w.pack(side="left", padx=(0, 4), pady=2)

        self.populate()
        self._initialized = True

    def apply_style(self, seen=None):
        container_frame.ContainerFrame.apply_style(self, seen)
        self.title.config(bd=self.frame_depth, bg=self.frame_bg_color)
        self.title_label.config(bg=self.frame_bg_color)
        self.content.config(bd=self.frame_depth)

    def load_child_node_data(self):
        desc = self.desc
        sub_node = None
        for wid in self.f_widgets:
            # try and load any existing FieldWidgets with appropriate node data
            w = self.f_widgets[wid]
            attr_index = self.f_widget_ids_map_inv.get(wid)
            if attr_index is None:
                continue
            elif self.node:
                sub_node = self.node.u_node

            w.load_node_data(self.node, sub_node, attr_index)

        return False

    def set_disabled(self, disable=True):
        disable = disable or not self.editable
        if self.node is None and not disable:
            return

        if bool(disable) != self.disabled and getattr(self, "sel_menu", None):
            self.sel_menu.set_disabled(disable)

        container_frame.ContainerFrame.set_disabled(self, disable)

    def get_options(self, opt_index=None):
        '''
        Returns a list of the option strings sorted by option index.
        '''
        if self.option_cache is None:
            return self.generate_options(opt_index)

        if opt_index is None:
            return self.option_cache
        elif opt_index == e_c.ACTIVE_ENUM_NAME:
            opt_index = getattr(self.node, "u_index", None)
            if opt_index is None:
                opt_index = len(self.option_cache) - 1

        if opt_index is None:
            opt_index = -1

        return self.option_cache.get(opt_index, None)

    def generate_options(self, opt_index=None):
        options = {i: c for c, i in self.desc['CASE_MAP'].items()}
        options[len(options)] = e_c.RAW_BYTES

        if opt_index is None:
            self.option_cache = options
            if self.sel_menu is not None:
                self.sel_menu.options_menu_sane = False
            return options
        return options.get(opt_index, None)

    def edit_apply(self=None, *, edit_state, undo=True):
        edit_info = edit_state.edit_info

        w, node = field_widget.FieldWidget.get_widget_and_node(
            nodepath=edit_state.nodepath, tag_window=edit_state.tag_window)

        if undo:
            node.u_index = edit_info.get('undo_u_index')
            u_node = edit_state.undo_node
        else:
            node.u_index = edit_info.get('redo_u_index')
            u_node = edit_state.redo_node

        if node.u_index is None:
            node.u_node = None
            node[:] = u_node
        else:
            node.u_node = u_node

        if w is not None:
            try:
                if w.desc is not edit_state.desc:
                    return

                w.needs_flushing = False
                w.set_edited()
                w.reload()
            except Exception:
                print(format_exc())

    def select_option(self, opt_index=None, force=False, reload=True):
        self.flush()
        if self.node is None:
            return

        node = self.node
        curr_index = self.sel_menu.sel_index

        if (opt_index < 0 or opt_index > self.sel_menu.max_index
                or opt_index is None or force):
            return

        undo_u_index = node.u_index
        undo_node = node.u_node
        if node.u_index is None:
            undo_node = node[:]

        if opt_index == self.sel_menu.max_index:
            # setting to rawdata
            self.node.set_active()
        else:
            self.node.set_active(opt_index)

        self.set_edited()
        # make an edit state
        if undo_u_index != node.u_index:
            self.edit_create(
                undo_u_index=undo_u_index,
                redo_u_index=node.u_index,
                redo_node=node[:] if node.u_index is None else node.u_node,
                undo_node=undo_node)

        self.sel_menu.sel_index = opt_index
        if reload:
            self.reload()

    def populate(self):
        try:
            old_u_node_frames = []
            for u_index in self.u_node_widgets_by_u_index:
                # delete any existing widgets
                if u_index is not None:
                    old_u_node_frames.append(
                        self.u_node_widgets_by_u_index[u_index])

            self.u_node_widgets_by_u_index.clear()
            self.u_node_widgets_by_u_index[None] = self.raw_frame

            for w in (self, self.content, self.title_label):
                w.tooltip_string = self.desc.get('TOOLTIP')

            self.display_comment(self.content)
            self.reload()

            # do things in this order to prevent the window from scrolling up
            for w in old_u_node_frames:
                w.destroy()
        except Exception:
            print(format_exc())

    def reload(self):
        try:
            # clear the f_widget_ids list
            self.f_widget_ids = []
            self.f_widget_ids_map = {}
            self.f_widget_ids_map_inv = {}
            u_index = u_node = None
            if self.node is not None:
                self.raw_label.config(text='DataUnion: %s raw bytes' %
                                      self.node.get_size())
                sel_index = self.sel_menu.sel_index
                u_index = self.node.u_index
                u_node = self.node.u_node

                new_sel_index = (self.sel_menu.max_index
                                 if u_index is None else u_index)
                if new_sel_index != self.sel_menu.sel_index:
                    self.sel_menu.sel_index = new_sel_index

            u_desc = self.desc.get(u_index)
            if hasattr(u_node, 'desc'):
                u_desc = u_node.desc

            active_widget = self.u_node_widgets_by_u_index.get(u_index)
            if active_widget is None:
                if u_index is not None:
                    widget_cls = self.widget_picker.get_widget(u_desc)
                    kwargs = dict(show_title=False,
                                  tag_window=self.tag_window,
                                  attr_index=u_index,
                                  disabled=self.disabled,
                                  f_widget_parent=self,
                                  desc=u_desc,
                                  show_frame=self.show.get(),
                                  dont_padx_fields=True)

                    try:
                        active_widget = widget_cls(self.content, **kwargs)
                    except Exception:
                        print(format_exc())
                        active_widget = data_frame.NullFrame(
                            self.content, **kwargs)
                else:
                    active_widget = self.raw_frame

                self.u_node_widgets_by_u_index[u_index] = active_widget

            wid = id(active_widget)
            self.f_widget_ids.append(wid)
            self.f_widget_ids_map[u_index] = wid
            self.f_widget_ids_map_inv[wid] = u_index
            if hasattr(active_widget, "load_node_data"):
                if active_widget.load_node_data(self.node, u_node, u_index,
                                                u_desc):
                    self.populate()
                    return
                active_widget.reload()
                active_widget.apply_style()

            self.build_f_widget_cache()
            # now that the field widgets are created, position them
            if self.show.get():
                self.pose_fields()
        except Exception:
            print(format_exc())

        self.sel_menu.update_label()
        if self.node is None:
            self.set_disabled(True)
        else:
            self.set_children_disabled(not self.node)

    def pose_fields(self):
        u_index = None if self.node is None else self.node.u_index
        w = self.u_node_widgets_by_u_index.get(u_index)
        for child in self.content.children.values():
            if child not in (w, self.comment_frame):
                child.pack_forget()

        if w:
            # by adding a fixed amount of padding, we fix a problem
            # with difficult to predict padding based on nesting
            w.pack(fill='x',
                   anchor='nw',
                   padx=self.vertical_padx,
                   pady=self.vertical_pady)

        self.content.pack(fill='x', anchor='nw', expand=True)
Beispiel #7
0
class RefineryActionsWindow(tk.Toplevel, BinillaWidget):
    app_root = None
    settings = None
    renamable = True
    propagatable = False
    accept_rename = None
    accept_settings = None
    tag_index_ref = None

    rename_string = None
    newtype_string = None
    recursive_rename = None

    original_name = ""

    def __init__(self, *args, **kwargs):
        title = kwargs.pop('title', None)
        self.renamable = kwargs.pop('renamable', self.renamable)
        self.settings = settings = kwargs.pop('settings', {})
        self.tag_index_ref = kwargs.pop('tag_index_ref', self.tag_index_ref)
        # Zatarita, added propagatable kwarg for displaying checkbox
        self.propagatable = kwargs.pop('propagatable', self.propagatable)
        BinillaWidget.__init__(self, *args, **kwargs)
        tk.Toplevel.__init__(self, *args, **kwargs)

        try:
            self.iconbitmap(e_c.REFINERY_ICON_PATH)
        except Exception:
            if not e_c.IS_LNX:
                print("Could not load window icon.")

        self.bind('<Escape>', lambda e=None, s=self, *a, **kw: s.destroy())

        if self.app_root is None and hasattr(self.master, 'app_root'):
            self.app_root = self.master.app_root

        self.accept_rename = settings.get('accept_rename', tk.IntVar(self))
        self.accept_settings = settings.get('accept_settings', tk.IntVar(self))
        self.rename_string = settings.get('rename_string', tk.StringVar(self))
        self.newtype_string = settings.get('newtype_string',
                                           tk.StringVar(self))
        self.extract_to_dir = settings.get('out_dir', tk.StringVar(self))
        self.tagslist_path = settings.get('tagslist_path', tk.StringVar(self))
        self.extract_mode = settings.get('extract_mode', tk.StringVar(self))
        self.recursive_rename = tk.IntVar(self)
        self.resizable(1, 0)

        if title is None:
            title = self.rename_string.get()
            if not title:
                title = "Options"
        self.title(title)

        self.original_name = PureWindowsPath(self.rename_string.get())
        if self.tag_index_ref is not None:
            # this ActionsWindow is displaying a single tag. the
            # original name will have an extension. remove it
            self.original_name = self.original_name.with_suffix("")

        self.rename_string.set(str(self.original_name))
        self.newtype_string.set("")

        self.accept_rename.set(0)
        self.accept_settings.set(0)

        # frames
        self.rename_frame = tk.LabelFrame(self, text="Rename to")
        self.rename_frame_inner0 = tk.Frame(self.rename_frame)
        self.rename_frame_inner1 = tk.Frame(self.rename_frame)
        self.tags_list_frame = tk.LabelFrame(
            self, text="Tags list log(erase to disable logging)")
        self.extract_to_frame = tk.LabelFrame(self,
                                              text="Directory to extract to")
        self.settings_frame = tk.LabelFrame(self, text="Extract settings")

        self.button_frame = tk.Frame(self)
        self.accept_frame = tk.Frame(self.button_frame)
        self.cancel_frame = tk.Frame(self.button_frame)

        # rename
        self.rename_entry = tk.Entry(self.rename_frame_inner0,
                                     width=50,
                                     textvariable=self.rename_string)
        self.rename_button = tk.Button(self.rename_frame_inner0,
                                       text="Rename",
                                       command=self.rename,
                                       width=6)
        self.class_scroll_menu = ScrollMenu(self.rename_frame_inner1,
                                            menu_width=35)
        self.recursive_rename_cbtn = tk.Checkbutton(
            self.rename_frame_inner1,
            text="Recursive",
            variable=self.recursive_rename)

        if self.tag_index_ref:
            # populate the class_scroll_menu options
            opts = sorted([n for n in self.tag_index_ref.class_1.NAME_MAP])
            self.class_scroll_menu.set_options(opts)
            try:
                self.class_scroll_menu.sel_index = opts.index(
                    self.tag_index_ref.class_1.enum_name)
            except ValueError:
                pass

        # tags list
        self.tags_list_entry = tk.Entry(self.tags_list_frame,
                                        width=50,
                                        textvariable=self.tagslist_path)
        self.browse_tags_list_button = tk.Button(self.tags_list_frame,
                                                 text="Browse",
                                                 command=self.tags_list_browse)

        # extract to dir
        self.extract_to_entry = tk.Entry(self.extract_to_frame,
                                         width=50,
                                         textvariable=self.extract_to_dir)
        self.browse_extract_to_button = tk.Button(
            self.extract_to_frame,
            text="Browse",
            command=self.extract_to_browse)

        # settings
        self.recursive_cbtn = tk.Checkbutton(self.settings_frame,
                                             text="Recursive extraction",
                                             variable=settings.get(
                                                 "recursive", tk.IntVar(self)))
        self.overwrite_cbtn = tk.Checkbutton(
            self.settings_frame,
            text="Overwrite tags(not recommended)",
            variable=settings.get("overwrite", tk.IntVar(self)))
        self.do_printout_cbtn = tk.Checkbutton(
            self.settings_frame,
            text="Print extracted tag names",
            variable=settings.get("do_printout", tk.IntVar(self)))
        # zatarita added do_propegate_settings to place the check box in frame, and link value to variable
        self.do_propegate_settings = tk.Checkbutton(
            self.settings_frame,
            text="Use these settings for each item",
            variable=settings.get("propagate_settings", tk.IntVar(self)))

        # accept/cancel
        self.accept_button = tk.Button(self.accept_frame,
                                       text="Add to queue",
                                       command=self.add_to_queue,
                                       width=14)
        self.cancel_button = tk.Button(self.cancel_frame,
                                       text="Cancel",
                                       command=self.destroy,
                                       width=14)
        self.show_meta_button = tk.Button(self,
                                          text="Display metadata",
                                          command=self.show_meta)

        # pack everything
        # frames
        if self.renamable:
            self.rename_frame.pack(padx=4, pady=2, expand=True, fill="x")
        self.rename_frame_inner0.pack(expand=True, fill="x")
        self.rename_frame_inner1.pack(expand=True, fill="x")
        self.tags_list_frame.pack(padx=4, pady=2, expand=True, fill="x")
        self.extract_to_frame.pack(padx=4, pady=2, expand=True, fill="x")
        self.settings_frame.pack(padx=4, pady=2, expand=True, fill="x")

        self.button_frame.pack(pady=2, expand=True, fill="x")
        self.accept_frame.pack(padx=4, side='left', fill='x', expand=True)
        self.cancel_frame.pack(padx=4, side='right', fill='x', expand=True)

        # rename
        self.rename_entry.pack(padx=4, side='left', fill='x', expand=True)
        self.rename_button.pack(padx=4, side='left', fill='x')
        if self.tag_index_ref:
            self.class_scroll_menu.pack(padx=4, side='left', fill='x')
        #self.recursive_rename_cbtn.pack(padx=4, side='left', fill='x')

        # extract to
        self.extract_to_entry.pack(padx=4, side='left', fill='x', expand=True)
        self.browse_extract_to_button.pack(padx=4, side='left', fill='x')

        # tags list
        self.tags_list_entry.pack(padx=4, side='left', fill='x', expand=True)
        self.browse_tags_list_button.pack(padx=4, side='left', fill='x')

        # settings
        # zatarita, added propagatable check box to top of settings frame. (if enabled)
        if self.propagatable:
            self.do_propegate_settings.pack(padx=4, anchor='w')
        self.recursive_cbtn.pack(padx=4, anchor='w')
        self.overwrite_cbtn.pack(padx=4, anchor='w')
        self.do_printout_cbtn.pack(padx=4, anchor='w')

        # accept/cancel
        self.accept_button.pack(side='right')
        self.cancel_button.pack(side='left')
        if self.tag_index_ref is not None:
            self.show_meta_button.pack(padx=4, pady=4, expand=True, fill='x')

        # make the window not show up on the start bar
        self.transient(self.master)
        self.wait_visibility()
        self.lift()
        self.grab_set()
        self.apply_style()

        try:
            self.update()
            self.app_root.place_window_relative(self)
            # I would use focus_set, but it doesn't seem to always work
            self.accept_button.focus_force()
        except AttributeError:
            pass

    def apply_style(self, seen=None):
        BinillaWidget.apply_style(self, seen)
        self.update()
        w, h = self.winfo_reqwidth(), self.winfo_reqheight()
        self.geometry("%sx%s" % (w, h))
        self.minsize(width=w, height=h)

    def add_to_queue(self, e=None):
        self.accept_settings.set(1)
        self.destroy()

    def rename(self, e=None):
        if not self.renamable:
            return

        new_name = self.rename_string.get()
        new_name = str(PureWindowsPath(new_name)).lower().strip(".")
        if self.tag_index_ref is None and new_name:
            # directory of tags
            new_name += "\\"

        old_class = new_class = None
        try:
            old_class = self.tag_index_ref.class_1.enum_name
        except Exception:
            pass

        try:
            new_class = self.class_scroll_menu.get_option()
        except Exception:
            new_class = ""

        #new_name = os.path.splitext(new_name)[0]
        self.rename_string.set(new_name)
        str_len = len(new_name)
        if str_len > MAX_TAG_NAME_LEN:
            messagebox.showerror(
                "Max name length exceeded",
                ("The max length for a tag is limited to %s characters\n" +
                 "Remove %s characters(excluding extension).") %
                (MAX_TAG_NAME_LEN, str_len - MAX_TAG_NAME_LEN),
                parent=self)
            return
        elif is_protected_tag(new_name):
            messagebox.showerror("Invalid name",
                                 "The entered string is not a valid filename.",
                                 parent=self)
            return
        elif not str_len and self.tag_index_ref is not None:
            messagebox.showerror("Invalid name",
                                 "The entered string cannot be empty.",
                                 parent=self)
            return
        self.accept_rename.set(1)

        # change the type if applicable
        if new_class and new_class != old_class:
            self.newtype_string.set(new_class)

        self.destroy()

    def tags_list_browse(self):
        try:
            init_dir = os.path.dirname(self.tagslist_path.get())
        except Exception:
            init_dir = None
        dirpath = asksaveasfilename(
            initialdir=init_dir,
            parent=self,
            title="Select where to save the tag list log",
            filetypes=(("text log", "*.txt"), ("All", "*")))

        if not dirpath:
            return

        self.tagslist_path.set(str(Path(dirpath)))

    def extract_to_browse(self):
        dirpath = askdirectory(initialdir=self.extract_to_dir.get(),
                               parent=self,
                               title="Select the directory to extract tags to")

        if not dirpath:
            return

        self.extract_to_dir.set(str(Path(dirpath)))

    def show_meta(self):
        index_ref = self.tag_index_ref
        if not index_ref:
            return

        try:
            halo_map = self.settings.get("halo_map")
            if halo_map is None:
                print("Could not get map.")
                return

            disable_safe_mode = getattr(self.app_root, "disable_safe_mode")
            disable_tag_cleaning = getattr(self.app_root,
                                           "disable_tag_cleaning")

            meta = halo_map.get_meta(
                index_ref.id & 0xFFff,
                True,
                allow_corrupt=self.settings["allow_corrupt"].get(),
                disable_safe_mode=disable_safe_mode,
                disable_tag_cleaning=disable_tag_cleaning)
            if meta is None:
                print("Could not get meta.")
                return

            meta_tag = meta_tag_def.build()
            meta_tag.data.tagdata = meta
            tag_path = index_ref.path
            meta_tag.filepath = tag_path
            if index_ref.class_1.enum_name not in BAD_CLASSES:
                ext = ".%s" % index_ref.class_1.enum_name
            else:
                ext = ".INVALID"

            w = MetaWindow(self.app_root,
                           meta_tag,
                           engine=halo_map.engine,
                           tag_path=tag_path + ext)
            self.destroy()
            w.focus_set()
        except Exception:
            print(format_exc())
            return
Beispiel #8
0
    def __init__(self, *args, **kwargs):
        title = kwargs.pop('title', None)
        self.renamable = kwargs.pop('renamable', self.renamable)
        self.settings = settings = kwargs.pop('settings', {})
        self.tag_index_ref = kwargs.pop('tag_index_ref', self.tag_index_ref)
        # Zatarita, added propagatable kwarg for displaying checkbox
        self.propagatable = kwargs.pop('propagatable', self.propagatable)
        BinillaWidget.__init__(self, *args, **kwargs)
        tk.Toplevel.__init__(self, *args, **kwargs)

        try:
            self.iconbitmap(e_c.REFINERY_ICON_PATH)
        except Exception:
            if not e_c.IS_LNX:
                print("Could not load window icon.")

        self.bind('<Escape>', lambda e=None, s=self, *a, **kw: s.destroy())

        if self.app_root is None and hasattr(self.master, 'app_root'):
            self.app_root = self.master.app_root

        self.accept_rename = settings.get('accept_rename', tk.IntVar(self))
        self.accept_settings = settings.get('accept_settings', tk.IntVar(self))
        self.rename_string = settings.get('rename_string', tk.StringVar(self))
        self.newtype_string = settings.get('newtype_string',
                                           tk.StringVar(self))
        self.extract_to_dir = settings.get('out_dir', tk.StringVar(self))
        self.tagslist_path = settings.get('tagslist_path', tk.StringVar(self))
        self.extract_mode = settings.get('extract_mode', tk.StringVar(self))
        self.recursive_rename = tk.IntVar(self)
        self.resizable(1, 0)

        if title is None:
            title = self.rename_string.get()
            if not title:
                title = "Options"
        self.title(title)

        self.original_name = PureWindowsPath(self.rename_string.get())
        if self.tag_index_ref is not None:
            # this ActionsWindow is displaying a single tag. the
            # original name will have an extension. remove it
            self.original_name = self.original_name.with_suffix("")

        self.rename_string.set(str(self.original_name))
        self.newtype_string.set("")

        self.accept_rename.set(0)
        self.accept_settings.set(0)

        # frames
        self.rename_frame = tk.LabelFrame(self, text="Rename to")
        self.rename_frame_inner0 = tk.Frame(self.rename_frame)
        self.rename_frame_inner1 = tk.Frame(self.rename_frame)
        self.tags_list_frame = tk.LabelFrame(
            self, text="Tags list log(erase to disable logging)")
        self.extract_to_frame = tk.LabelFrame(self,
                                              text="Directory to extract to")
        self.settings_frame = tk.LabelFrame(self, text="Extract settings")

        self.button_frame = tk.Frame(self)
        self.accept_frame = tk.Frame(self.button_frame)
        self.cancel_frame = tk.Frame(self.button_frame)

        # rename
        self.rename_entry = tk.Entry(self.rename_frame_inner0,
                                     width=50,
                                     textvariable=self.rename_string)
        self.rename_button = tk.Button(self.rename_frame_inner0,
                                       text="Rename",
                                       command=self.rename,
                                       width=6)
        self.class_scroll_menu = ScrollMenu(self.rename_frame_inner1,
                                            menu_width=35)
        self.recursive_rename_cbtn = tk.Checkbutton(
            self.rename_frame_inner1,
            text="Recursive",
            variable=self.recursive_rename)

        if self.tag_index_ref:
            # populate the class_scroll_menu options
            opts = sorted([n for n in self.tag_index_ref.class_1.NAME_MAP])
            self.class_scroll_menu.set_options(opts)
            try:
                self.class_scroll_menu.sel_index = opts.index(
                    self.tag_index_ref.class_1.enum_name)
            except ValueError:
                pass

        # tags list
        self.tags_list_entry = tk.Entry(self.tags_list_frame,
                                        width=50,
                                        textvariable=self.tagslist_path)
        self.browse_tags_list_button = tk.Button(self.tags_list_frame,
                                                 text="Browse",
                                                 command=self.tags_list_browse)

        # extract to dir
        self.extract_to_entry = tk.Entry(self.extract_to_frame,
                                         width=50,
                                         textvariable=self.extract_to_dir)
        self.browse_extract_to_button = tk.Button(
            self.extract_to_frame,
            text="Browse",
            command=self.extract_to_browse)

        # settings
        self.recursive_cbtn = tk.Checkbutton(self.settings_frame,
                                             text="Recursive extraction",
                                             variable=settings.get(
                                                 "recursive", tk.IntVar(self)))
        self.overwrite_cbtn = tk.Checkbutton(
            self.settings_frame,
            text="Overwrite tags(not recommended)",
            variable=settings.get("overwrite", tk.IntVar(self)))
        self.do_printout_cbtn = tk.Checkbutton(
            self.settings_frame,
            text="Print extracted tag names",
            variable=settings.get("do_printout", tk.IntVar(self)))
        # zatarita added do_propegate_settings to place the check box in frame, and link value to variable
        self.do_propegate_settings = tk.Checkbutton(
            self.settings_frame,
            text="Use these settings for each item",
            variable=settings.get("propagate_settings", tk.IntVar(self)))

        # accept/cancel
        self.accept_button = tk.Button(self.accept_frame,
                                       text="Add to queue",
                                       command=self.add_to_queue,
                                       width=14)
        self.cancel_button = tk.Button(self.cancel_frame,
                                       text="Cancel",
                                       command=self.destroy,
                                       width=14)
        self.show_meta_button = tk.Button(self,
                                          text="Display metadata",
                                          command=self.show_meta)

        # pack everything
        # frames
        if self.renamable:
            self.rename_frame.pack(padx=4, pady=2, expand=True, fill="x")
        self.rename_frame_inner0.pack(expand=True, fill="x")
        self.rename_frame_inner1.pack(expand=True, fill="x")
        self.tags_list_frame.pack(padx=4, pady=2, expand=True, fill="x")
        self.extract_to_frame.pack(padx=4, pady=2, expand=True, fill="x")
        self.settings_frame.pack(padx=4, pady=2, expand=True, fill="x")

        self.button_frame.pack(pady=2, expand=True, fill="x")
        self.accept_frame.pack(padx=4, side='left', fill='x', expand=True)
        self.cancel_frame.pack(padx=4, side='right', fill='x', expand=True)

        # rename
        self.rename_entry.pack(padx=4, side='left', fill='x', expand=True)
        self.rename_button.pack(padx=4, side='left', fill='x')
        if self.tag_index_ref:
            self.class_scroll_menu.pack(padx=4, side='left', fill='x')
        #self.recursive_rename_cbtn.pack(padx=4, side='left', fill='x')

        # extract to
        self.extract_to_entry.pack(padx=4, side='left', fill='x', expand=True)
        self.browse_extract_to_button.pack(padx=4, side='left', fill='x')

        # tags list
        self.tags_list_entry.pack(padx=4, side='left', fill='x', expand=True)
        self.browse_tags_list_button.pack(padx=4, side='left', fill='x')

        # settings
        # zatarita, added propagatable check box to top of settings frame. (if enabled)
        if self.propagatable:
            self.do_propegate_settings.pack(padx=4, anchor='w')
        self.recursive_cbtn.pack(padx=4, anchor='w')
        self.overwrite_cbtn.pack(padx=4, anchor='w')
        self.do_printout_cbtn.pack(padx=4, anchor='w')

        # accept/cancel
        self.accept_button.pack(side='right')
        self.cancel_button.pack(side='left')
        if self.tag_index_ref is not None:
            self.show_meta_button.pack(padx=4, pady=4, expand=True, fill='x')

        # make the window not show up on the start bar
        self.transient(self.master)
        self.wait_visibility()
        self.lift()
        self.grab_set()
        self.apply_style()

        try:
            self.update()
            self.app_root.place_window_relative(self)
            # I would use focus_set, but it doesn't seem to always work
            self.accept_button.focus_force()
        except AttributeError:
            pass
Beispiel #9
0
    def __init__(self, *args, **kwargs):
        kwargs.update(relief='flat',
                      bd=0,
                      highlightthickness=0,
                      bg=self.default_bg_color)
        field_widget.FieldWidget.__init__(self, *args, **kwargs)
        tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs))

        show_frame = bool(
            kwargs.pop('show_frame', not self.blocks_start_hidden))
        if self.is_empty and self.hide_if_blank:
            show_frame = False

        self.show = tk.BooleanVar()
        self.show.set(show_frame)
        self.options_sane = False

        node_len = 0
        try:
            node_len = len(self.node)
        except Exception:
            pass

        self.sel_index = (node_len > 0) - 1

        # make the title, element menu, and all the buttons
        self.controls = tk.Frame(self, relief='raised', bd=self.frame_depth)
        self.title = title = tk.Frame(self.controls, relief='flat', bd=0)
        self.buttons = buttons = tk.Frame(self.controls, relief='flat', bd=0)

        toggle_text = '-' if show_frame else '+'

        self.title_label = tk.Label(
            title,
            text=self.gui_name,
            justify='left',
            anchor='w',
            width=self.title_size,
            font=self.get_font("frame_title"),
            disabledforeground=self.text_disabled_color)
        self.title_label.font_type = "frame_title"

        self.show_btn = ttk.Checkbutton(title,
                                        width=3,
                                        text=toggle_text,
                                        command=self.toggle_visible,
                                        style='ShowButton.TButton')
        self.sel_menu = ScrollMenu(title,
                                   f_widget_parent=self,
                                   sel_index=self.sel_index,
                                   max_index=node_len - 1,
                                   option_getter=self.get_options,
                                   callback=self.select_option)

        self.shift_up_btn = ttk.Button(title,
                                       width=7,
                                       text='Shift ▲',
                                       command=self.shift_entry_up)
        self.shift_down_btn = ttk.Button(buttons,
                                         width=7,
                                         text='Shift ▼',
                                         command=self.shift_entry_down)
        self.add_btn = ttk.Button(buttons,
                                  width=4,
                                  text='Add',
                                  command=self.add_entry)
        self.insert_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Insert',
                                     command=self.insert_entry)
        self.duplicate_btn = ttk.Button(buttons,
                                        width=9,
                                        text='Duplicate',
                                        command=self.duplicate_entry)
        self.delete_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Delete',
                                     command=self.delete_entry)
        self.delete_all_btn = ttk.Button(buttons,
                                         width=10,
                                         text='Delete all',
                                         command=self.delete_all_entries)

        self.import_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Import',
                                     command=self.import_node)
        self.export_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Export',
                                     command=self.export_node)

        # pack the title, menu, and all the buttons
        for w in (self.shift_down_btn, self.export_btn, self.import_btn,
                  self.delete_all_btn, self.delete_btn, self.duplicate_btn,
                  self.insert_btn, self.add_btn):
            w.pack(side="right", padx=(0, 4), pady=(2, 2))

        self.show_btn.pack(side="left")
        if self.gui_name != '':
            self.title_label.pack(side="left", fill="x", expand=True)

        self.sel_menu.pack(side="left", fill="x", expand=True, padx=(0, 4))
        self.shift_up_btn.pack(side="right", padx=(0, 1), pady=(2, 2))

        self.title.pack(fill="x", expand=True, padx=0)
        self.buttons.pack(fill="x", expand=True, padx=0)
        self.controls.pack(fill="x", expand=True, padx=0)

        self.populate()
        self._initialized = True
class ModelCompilerWindow(window_base_class, BinillaWidget):
    app_root = None
    tags_dir = ''

    jms_models = ()
    merged_jms = None
    mod2_tag = None

    shader_paths = ()
    shader_types = ()

    _compiling = False
    _loading = False
    _saving = False
    _editing_shader_path = False

    _jms_tree_iids = ()

    def __init__(self, app_root, *args, **kwargs):
        if window_base_class == tk.Toplevel:
            kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color)
            self.app_root = app_root
        else:
            self.app_root = self

        window_base_class.__init__(self, app_root, *args, **kwargs)
        BinillaWidget.__init__(self, *args, **kwargs)

        self.title("Gbxmodel compiler")
        self.resizable(1, 1)
        self.update()
        try:
            self.iconbitmap(e_c.MOZZ_ICON_PATH)
        except Exception:
            print("Could not load window icon.")

        self.superhigh_lod_cutoff = tk.StringVar(self)
        self.high_lod_cutoff = tk.StringVar(self)
        self.medium_lod_cutoff = tk.StringVar(self)
        self.low_lod_cutoff = tk.StringVar(self)
        self.superlow_lod_cutoff = tk.StringVar(self)
        self.shader_path_string_var = tk.StringVar(self)

        tags_dir = getattr(app_root, "tags_dir", "")

        self.optimize_level = tk.IntVar(self)
        self.tags_dir = tk.StringVar(self, tags_dir if tags_dir else "")
        self.jms_dir = tk.StringVar(self)
        self.gbxmodel_path = tk.StringVar(self)

        # make the frames
        self.main_frame = tk.Frame(self)
        self.jms_info_frame = tk.LabelFrame(self, text="Model info")

        self.dirs_frame = tk.LabelFrame(self.main_frame, text="Directories")
        self.buttons_frame = tk.Frame(self.main_frame)
        self.settings_frame = tk.LabelFrame(self.main_frame,
                                            text="Compilation settings")

        self.jms_dir_frame = tk.LabelFrame(self.dirs_frame,
                                           text="Source models folder")
        self.tags_dir_frame = tk.LabelFrame(self.dirs_frame,
                                            text="Tags directory root folder")
        self.gbxmodel_path_frame = tk.LabelFrame(self.dirs_frame,
                                                 text="Gbxmodel output path")

        self.lods_frame = tk.LabelFrame(self.settings_frame,
                                        text="LOD cutoffs")
        self.shaders_frame = tk.LabelFrame(self.settings_frame, text="Shaders")

        self.optimize_label = tk.Label(
            self.settings_frame,
            justify="right",
            text=("Vertex optimization\n(Set before loading)"))
        self.optimize_menu = ScrollMenu(self.settings_frame,
                                        menu_width=5,
                                        options=("None", "Exact", "Loose"))
        self.optimize_menu.sel_index = 1

        self.jms_info_tree = tk.ttk.Treeview(self.jms_info_frame,
                                             selectmode='browse',
                                             padding=(0, 0),
                                             height=4)
        self.jms_info_vsb = tk.Scrollbar(self.jms_info_frame,
                                         orient='vertical',
                                         command=self.jms_info_tree.yview)
        self.jms_info_hsb = tk.Scrollbar(self.jms_info_frame,
                                         orient='horizontal',
                                         command=self.jms_info_tree.xview)
        self.jms_info_tree.config(yscrollcommand=self.jms_info_vsb.set,
                                  xscrollcommand=self.jms_info_hsb.set)

        self.shader_names_menu = ScrollMenu(
            self.shaders_frame,
            menu_width=10,
            callback=self.select_shader,
            option_getter=self.get_shader_names,
            options_volatile=True)
        self.shader_types_menu = ScrollMenu(self.shaders_frame,
                                            menu_width=20,
                                            options=shader_types,
                                            callback=self.select_shader_type)
        self.shader_path_browse_button = tk.Button(
            self.shaders_frame,
            text="Browse",
            width=6,
            command=self.browse_shader_path)
        self.shader_path_entry = tk.Entry(
            self.shaders_frame, textvariable=self.shader_path_string_var)

        self.write_trace(self.shader_path_string_var, self.shader_path_edited)

        self.superhigh_lod_label = tk.Label(self.lods_frame, text="Superhigh")
        self.high_lod_label = tk.Label(self.lods_frame, text="High")
        self.medium_lod_label = tk.Label(self.lods_frame, text="Medium")
        self.low_lod_label = tk.Label(self.lods_frame, text="Low")
        self.superlow_lod_label = tk.Label(self.lods_frame, text="Superlow")
        self.superhigh_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.superhigh_lod_cutoff,
            width=6,
            justify='right')
        self.high_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.high_lod_cutoff,
            width=6,
            justify='right')
        self.medium_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.medium_lod_cutoff,
            width=6,
            justify='right')
        self.low_lod_cutoff_entry = tk.Entry(self.lods_frame,
                                             textvariable=self.low_lod_cutoff,
                                             width=6,
                                             justify='right')
        self.superlow_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.superlow_lod_cutoff,
            width=6,
            justify='right')

        self.jms_dir_entry = tk.Entry(self.jms_dir_frame,
                                      textvariable=self.jms_dir,
                                      state=tk.DISABLED)
        self.jms_dir_browse_button = tk.Button(self.jms_dir_frame,
                                               text="Browse",
                                               command=self.jms_dir_browse)

        self.tags_dir_entry = tk.Entry(self.tags_dir_frame,
                                       textvariable=self.tags_dir,
                                       state=tk.DISABLED)
        self.tags_dir_browse_button = tk.Button(self.tags_dir_frame,
                                                text="Browse",
                                                command=self.tags_dir_browse)

        self.gbxmodel_path_entry = tk.Entry(self.gbxmodel_path_frame,
                                            textvariable=self.gbxmodel_path,
                                            state=tk.DISABLED)
        self.gbxmodel_path_browse_button = tk.Button(
            self.gbxmodel_path_frame,
            text="Browse",
            command=self.gbxmodel_path_browse)

        self.load_button = tk.Button(self.buttons_frame,
                                     text="Load\nmodels",
                                     command=self.load_models)
        self.save_button = tk.Button(self.buttons_frame,
                                     text="Save as JMS",
                                     command=self.save_models)
        self.compile_button = tk.Button(self.buttons_frame,
                                        text="Compile\ngbxmodel",
                                        command=self.compile_gbxmodel)

        self.populate_model_info_tree()

        # pack everything
        self.main_frame.pack(fill="both", side='left', pady=3, padx=3)
        self.jms_info_frame.pack(fill="both",
                                 side='left',
                                 pady=3,
                                 padx=3,
                                 expand=True)

        self.dirs_frame.pack(fill="x")
        self.buttons_frame.pack(fill="x", pady=3, padx=3)
        self.settings_frame.pack(fill="both", expand=True)

        self.superhigh_lod_label.grid(sticky='e',
                                      row=0,
                                      column=0,
                                      padx=3,
                                      pady=1)
        self.high_lod_label.grid(sticky='e', row=1, column=0, padx=3, pady=1)
        self.medium_lod_label.grid(sticky='e', row=2, column=0, padx=3, pady=1)
        self.low_lod_label.grid(sticky='e', row=3, column=0, padx=3, pady=1)
        self.superlow_lod_label.grid(sticky='e',
                                     row=4,
                                     column=0,
                                     padx=3,
                                     pady=1)
        self.superhigh_lod_cutoff_entry.grid(sticky='ew',
                                             row=0,
                                             column=1,
                                             padx=3,
                                             pady=1)
        self.high_lod_cutoff_entry.grid(sticky='ew',
                                        row=1,
                                        column=1,
                                        padx=3,
                                        pady=1)
        self.medium_lod_cutoff_entry.grid(sticky='ew',
                                          row=2,
                                          column=1,
                                          padx=3,
                                          pady=1)
        self.low_lod_cutoff_entry.grid(sticky='ew',
                                       row=3,
                                       column=1,
                                       padx=3,
                                       pady=1)
        self.superlow_lod_cutoff_entry.grid(sticky='ew',
                                            row=4,
                                            column=1,
                                            padx=3,
                                            pady=1)

        self.jms_dir_frame.pack(expand=True, fill='x')
        self.tags_dir_frame.pack(expand=True, fill='x')
        self.gbxmodel_path_frame.pack(expand=True, fill='x')

        self.jms_dir_entry.pack(side='left', expand=True, fill='x')
        self.jms_dir_browse_button.pack(side='left')

        self.gbxmodel_path_entry.pack(side='left', expand=True, fill='x')
        self.gbxmodel_path_browse_button.pack(side='left')

        self.tags_dir_entry.pack(side='left', expand=True, fill='x')
        self.tags_dir_browse_button.pack(side='left')

        self.optimize_label.grid(sticky='ne', row=3, column=1, padx=3)
        self.optimize_menu.grid(sticky='new',
                                row=3,
                                column=2,
                                padx=3,
                                pady=(3, 0))
        self.lods_frame.grid(sticky='ne', row=0, column=3, rowspan=4)
        self.shaders_frame.grid(sticky='nsew',
                                row=0,
                                column=0,
                                columnspan=3,
                                rowspan=3,
                                pady=(0, 3))

        self.shader_names_menu.grid(sticky='new',
                                    row=0,
                                    column=0,
                                    padx=3,
                                    columnspan=5,
                                    pady=2)
        self.shader_path_browse_button.grid(sticky='ne',
                                            row=1,
                                            column=4,
                                            padx=3,
                                            pady=2)
        self.shader_types_menu.grid(sticky='new',
                                    row=1,
                                    column=1,
                                    padx=3,
                                    columnspan=3,
                                    pady=2)
        self.shader_path_entry.grid(sticky='new',
                                    row=2,
                                    column=0,
                                    padx=3,
                                    columnspan=5,
                                    pady=2)

        self.jms_info_hsb.pack(side="bottom", fill='x')
        self.jms_info_vsb.pack(side="right", fill='y')
        self.jms_info_tree.pack(side='left', fill='both', expand=True)

        self.load_button.pack(side='left', expand=True, fill='both', padx=3)
        self.save_button.pack(side='left', expand=True, fill='both', padx=3)
        self.compile_button.pack(side='right',
                                 expand=True,
                                 fill='both',
                                 padx=3)

        self.apply_style()
        if self.app_root is not self:
            self.transient(self.app_root)

    def populate_model_info_tree(self):
        jms_tree = self.jms_info_tree
        if not jms_tree['columns']:
            jms_tree['columns'] = ('data', )
            jms_tree.heading("#0")
            jms_tree.heading("data")
            jms_tree.column("#0", minwidth=100, width=100)
            jms_tree.column("data", minwidth=50, width=50, stretch=False)

        for iid in self._jms_tree_iids:
            jms_tree.delete(iid)

        self._jms_tree_iids = []

        if not self.jms_models or not self.merged_jms:
            return

        nodes_iid = jms_tree.insert('',
                                    'end',
                                    text="Nodes",
                                    tags=('item', ),
                                    values=(len(self.merged_jms.nodes), ))
        self._jms_tree_iids.append(nodes_iid)
        nodes = self.merged_jms.nodes
        for node in nodes:
            iid = jms_tree.insert(nodes_iid,
                                  'end',
                                  text=node.name,
                                  tags=('item', ))
            parent_name = child_name = sibling_name = "NONE"
            if node.parent_index >= 0:
                parent_name = nodes[node.parent_index].name
            if node.sibling_index >= 0:
                sibling_name = nodes[node.sibling_index].name
            if node.first_child >= 0:
                child_name = nodes[node.first_child].name

            jms_tree.insert(
                iid,
                'end',
                text="Parent",
                values=(parent_name, ),
                tags=('item', ),
            )
            jms_tree.insert(
                iid,
                'end',
                text="First child",
                values=(child_name, ),
                tags=('item', ),
            )
            jms_tree.insert(
                iid,
                'end',
                text="Next sibling",
                values=(sibling_name, ),
                tags=('item', ),
            )

            jms_tree.insert(
                iid,
                'end',
                text="i",
                values=(node.rot_i, ),
                tags=('item', ),
            )
            jms_tree.insert(
                iid,
                'end',
                text="j",
                values=(node.rot_j, ),
                tags=('item', ),
            )
            jms_tree.insert(
                iid,
                'end',
                text="k",
                values=(node.rot_k, ),
                tags=('item', ),
            )
            jms_tree.insert(
                iid,
                'end',
                text="w",
                values=(node.rot_w, ),
                tags=('item', ),
            )

            jms_tree.insert(
                iid,
                'end',
                text="x",
                values=(node.pos_x, ),
                tags=('item', ),
            )
            jms_tree.insert(
                iid,
                'end',
                text="y",
                values=(node.pos_y, ),
                tags=('item', ),
            )
            jms_tree.insert(
                iid,
                'end',
                text="z",
                values=(node.pos_z, ),
                tags=('item', ),
            )

        mats_iid = jms_tree.insert('',
                                   'end',
                                   text="Materials",
                                   tags=('item', ),
                                   values=(len(self.merged_jms.materials), ))
        self._jms_tree_iids.append(mats_iid)
        for mat in self.merged_jms.materials:
            jms_tree.insert(mats_iid,
                            'end',
                            text=mat.name,
                            tags=('item', ),
                            values=(mat.tiff_path, ))

        regions = list(sorted(self.merged_jms.regions))
        regions_iid = jms_tree.insert('',
                                      'end',
                                      text="Regions",
                                      tags=('item', ),
                                      values=(len(regions), ))
        self._jms_tree_iids.append(regions_iid)
        for region in regions:
            jms_tree.insert(
                regions_iid,
                'end',
                text=region,
                tags=('item', ),
            )

        geoms_iid = jms_tree.insert('',
                                    'end',
                                    text="Geometries",
                                    tags=('item', ),
                                    values=(len(self.jms_models), ))
        self._jms_tree_iids.append(geoms_iid)
        for jms_model in self.jms_models:
            iid = jms_tree.insert(geoms_iid,
                                  'end',
                                  tags=('item', ),
                                  text=jms_model.name)
            jms_tree.insert(iid,
                            'end',
                            text="Vertex count",
                            tags=('item', ),
                            values=(len(jms_model.verts), ))
            jms_tree.insert(iid,
                            'end',
                            text="Triangle count",
                            tags=('item', ),
                            values=(len(jms_model.tris), ))

            markers_iid = jms_tree.insert(iid,
                                          'end',
                                          text="Markers",
                                          tags=('item', ),
                                          values=(len(jms_model.markers), ))
            for marker in jms_model.markers:
                iid = jms_tree.insert(markers_iid,
                                      'end',
                                      tags=('item', ),
                                      text=marker.name)
                perm_name = marker.permutation
                region_name = regions[marker.region]
                parent_name = ""
                if marker.parent >= 0:
                    parent_name = nodes[marker.parent].name

                jms_tree.insert(iid,
                                'end',
                                text="Permutation",
                                values=(perm_name, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="Region",
                                values=(region_name, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="Parent",
                                values=(parent_name, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="Radius",
                                values=(marker.radius, ),
                                tags=('item', ))

                jms_tree.insert(iid,
                                'end',
                                text="i",
                                values=(marker.rot_i, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="j",
                                values=(marker.rot_j, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="k",
                                values=(marker.rot_k, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="w",
                                values=(marker.rot_w, ),
                                tags=('item', ))

                jms_tree.insert(iid,
                                'end',
                                text="x",
                                values=(marker.pos_x, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="y",
                                values=(marker.pos_y, ),
                                tags=('item', ))
                jms_tree.insert(iid,
                                'end',
                                text="z",
                                values=(marker.pos_z, ),
                                tags=('item', ))

    def jms_dir_browse(self):
        if self._compiling or self._loading or self._saving:
            return

        tags_dir = self.tags_dir.get()
        data_dir = path_replace(tags_dir, "tags", "data")
        jms_dir = self.jms_dir.get()
        if tags_dir and not jms_dir:
            jms_dir = data_dir

        dirpath = askdirectory(
            initialdir=jms_dir,
            parent=self,
            title="Select the folder of models to compile...")

        if not dirpath:
            return

        dirpath = str(Path(dirpath))
        if tags_dir and data_dir and os.path.basename(
                dirpath).lower() == "models":
            object_dir = os.path.dirname(dirpath)

            if object_dir and is_in_dir(object_dir, data_dir):
                tag_path = os.path.join(object_dir,
                                        os.path.basename(object_dir))
                tag_path = os.path.join(tags_dir,
                                        os.path.relpath(tag_path, data_dir))
                self.gbxmodel_path.set(tag_path + ".gbxmodel")

        self.app_root.last_load_dir = os.path.dirname(dirpath)
        self.jms_dir.set(dirpath)
        if not self.tags_dir.get():
            self.tags_dir.set(
                Path(path_split(self.app_root.last_load_dir, "data"), "tags"))

    def tags_dir_browse(self):
        if self._compiling or self._loading or self._saving:
            return

        old_tags_dir = self.tags_dir.get()
        tags_dir = askdirectory(initialdir=old_tags_dir,
                                parent=self,
                                title="Select the root of the tags directory")

        if not tags_dir:
            return

        tags_dir = path_normalize(tags_dir)

        mod2_path = self.gbxmodel_path.get()
        if old_tags_dir and mod2_path and not is_in_dir(mod2_path, tags_dir):
            # adjust mod2 filepath to be relative to the new tags directory
            mod2_path = os.path.join(tags_dir,
                                     os.path.relpath(mod2_path, old_tags_dir))
            self.gbxmodel_path.set(mod2_path)

        self.app_root.last_load_dir = os.path.dirname(tags_dir)
        self.tags_dir.set(tags_dir)

    def gbxmodel_path_browse(self, force=False):
        if not force and (self._compiling or self._loading or self._saving):
            return

        mod2_dir = os.path.dirname(self.gbxmodel_path.get())
        if self.tags_dir.get() and not mod2_dir:
            mod2_dir = self.tags_dir.get()

        fp = asksaveasfilename(initialdir=mod2_dir,
                               title="Save gbxmodel to...",
                               parent=self,
                               filetypes=(("Gearbox model", "*.gbxmodel"),
                                          ('All', '*')))

        if not fp:
            return

        fp = Path(fp).with_suffix(".gbxmodel")

        self.app_root.last_load_dir = str(fp.parent)
        self.gbxmodel_path.set(str(fp))

        self.tags_dir.set(
            path_split(self.app_root.last_load_dir, "tags", after=True))

    def apply_style(self, seen=None):
        BinillaWidget.apply_style(self, seen)
        self.update()
        w, h = self.winfo_reqwidth(), self.winfo_reqheight()
        self.geometry("%sx%s" % (w, h))
        self.minsize(width=w, height=h)

    def get_material(self, index):
        if not isinstance(self.merged_jms, MergedJmsModel):
            return None
        mats = self.merged_jms.materials
        if index >= len(mats) or index < 0:
            return None

        return mats[index]

    def select_shader(self, shader_index):
        self._editing_shader_path = True
        try:
            self._editing_shader_path = False
            mat = self.get_material(shader_index)
            if mat:
                self.shader_names_menu.sel_index = shader_index
                self.shader_path_string_var.set(mat.shader_path)
                self.shader_types_menu.sel_index = shader_type_map.get(
                    mat.shader_type, -1)
            elif shader_index < 0:
                self.shader_types_menu.sel_index = -1
                self.shader_names_menu.sel_index = -1
                self.shader_path_string_var.set("")
        except Exception:
            self._editing_shader_path = False
            raise

    def select_shader_type(self, shader_type):
        mat = self.get_material(self.shader_names_menu.sel_index)
        if mat and shader_type in range(len(shader_types)):
            mat.shader_type = "shader_" + shader_types[shader_type]

    def shader_path_edited(self, *a, **kw):
        if self._editing_shader_path:
            return

        self._editing_shader_path = True
        try:
            shader_path = self.shader_path_string_var.get().replace('/', '\\')
            mat = self.get_material(self.shader_names_menu.sel_index)
            if mat:
                mat.shader_path = shader_path

            self._editing_shader_path = False
        except Exception:
            self._editing_shader_path = False
            raise

    def browse_shader_path(self):
        if self._compiling or self._loading or self._saving:
            return

        tags_dir = self.tags_dir.get()
        if not tags_dir or not os.path.exists(tags_dir):
            return

        shader_dir = os.path.dirname(
            os.path.join(tags_dir, self.shader_path_string_var.get()))

        shader_exts = tuple((typ, "*.shader_%s" % typ) for typ in shader_types)
        fp = asksaveasfilename(
            initialdir=shader_dir,
            parent=self,
            title="Select the shader to use(or where to make one)",
            filetypes=shader_exts + (('All', '*'), ))

        fp, ext = os.path.splitext(fp)
        if fp:
            if not is_in_dir(fp, tags_dir):
                print("Specified shader is not located in the tags directory.")
                return

            ext = ext.strip(".").lower()
            self.shader_path_string_var.set(os.path.relpath(fp, tags_dir))
            mat = self.get_material(self.shader_names_menu.sel_index)
            if mat and ext in shader_type_map:
                self.shader_types_menu.sel_index = shader_type_map[ext]
                mat.shader_type = ext

    def get_shader_names(self, opt_index=None):
        if opt_index == "<ACTIVE>":
            opt_index = self.shader_names_menu.sel_index

        if isinstance(opt_index, str):
            opt_index = -1

        if opt_index is not None:
            return self.merged_jms.materials[opt_index].\
                   shader_path.split("\\")[-1]

        shader_names = {}
        if isinstance(self.merged_jms, MergedJmsModel):
            i = 0
            for mat in self.merged_jms.materials:
                shader_names[i] = mat.shader_path.split("\\")[-1]
                i += 1

        return shader_names

    def destroy(self):
        try:
            self.app_root.tool_windows.pop(self.window_name, None)
        except AttributeError:
            pass
        window_base_class.destroy(self)

    def load_models(self):
        if not self._compiling and not self._loading and not self._saving:
            self._loading = True
            try:
                self._load_models()
            except Exception:
                print(format_exc())
            try:
                self.populate_model_info_tree()
            except Exception:
                print(format_exc())
            self._loading = False

    def save_models(self):
        if not self._compiling and not self._loading and not self._saving:
            self._saving = True
            try:
                self._save_models()
            except Exception:
                print(format_exc())
            self._saving = False

    def compile_gbxmodel(self):
        if not self._compiling and not self._loading and not self._saving:
            self._compiling = True
            try:
                self._compile_gbxmodel()
            except Exception:
                print(format_exc())
            self._compiling = False

    def _load_models(self):
        models_dir = self.jms_dir.get()
        if not models_dir:
            return

        start = time.time()
        print("Locating jms files...")
        fps = []
        for _, __, files in os.walk(models_dir):
            for fname in files:
                ext = os.path.splitext(fname)[-1].lower()
                #if ext in ".jms.obj.dae":
                if ext in ".jms.obj":
                    fps.append(os.path.join(models_dir, fname))

            break

        if not fps:
            print("    No valid jms files found in the folder.")
            return

        self.mod2_tag = self.merged_jms = None
        optimize_level = max(0, self.optimize_menu.sel_index)

        jms_models = self.jms_models = []
        print("Loading jms files...")
        self.app_root.update()
        for fp in fps:
            try:
                print("    %s" % fp.replace('/', '\\').split("\\")[-1])
                self.app_root.update()

                model_name = os.path.basename(fp).split('.')[0]
                ext = os.path.splitext(fp)[-1].lower()

                jms_model = None
                if ext == ".jms":
                    with open(fp, "r") as f:
                        jms_model = read_jms(f.read(), '', model_name)
                elif ext == ".obj":
                    with open(fp, "r") as f:
                        jms_model = jms_model_from_obj(f.read(), model_name)
                elif ext == ".dae":
                    jms_model = jms_model_from_dae(fp, model_name)

                if not jms_model:
                    continue

                jms_models.append(jms_model)

                if optimize_level:
                    old_vert_ct = len(jms_model.verts)
                    print("        Optimizing...", end='')
                    jms_model.optimize_geometry(optimize_level == 1)
                    print(" Removed %s verts" %
                          (old_vert_ct - len(jms_model.verts)))

                print("        Calculating normals...")
                jms_model.calculate_vertex_normals()
            except Exception:
                print(format_exc())
                print("    Could not parse jms file.")
                self.app_root.update()

        if not jms_models:
            print("    No valid jms files found.")
            return

        first_crc = None
        for jms_model in jms_models:
            if first_crc is None:
                first_crc = jms_model.node_list_checksum
            elif first_crc != jms_model.node_list_checksum:
                print("    Warning, not all node list checksums match.")
                break

        # make sure the highest lod for each permutation is set as superhigh
        # this is necessary, as only superhigh jms markers are used
        jms_models_by_name = {}
        for jms_model in jms_models:
            lod_models = jms_models_by_name.setdefault(jms_model.perm_name,
                                                       [None] * 5)
            lod_index = {
                "high": 1,
                "medium": 2,
                "low": 3,
                "superlow": 4
            }.get(jms_model.lod_level, 0)
            lod_models[lod_index] = jms_model

        for lod_models in jms_models_by_name.values():
            for jms_model in lod_models:
                if jms_model is not None:
                    jms_model.lod_level = "superhigh"
                    break

        print("Merging jms data...")
        self.app_root.update()
        self.merged_jms = merged_jms = MergedJmsModel()
        errors_occurred = False
        for jms_model in jms_models:
            errors = merged_jms.merge_jms_model(jms_model)
            errors_occurred |= bool(errors)
            if errors:
                print("    Errors in '%s'" % jms_model.name)
                for error in errors:
                    print("        ", error, sep='')

            self.app_root.update()

        mod2_path = self.gbxmodel_path.get()
        tags_dir = self.tags_dir.get().replace('/', '\\')
        self.shader_names_menu.max_index = len(merged_jms.materials) - 1

        shaders_dir = ""
        if mod2_path:
            shaders_dir = os.path.join(os.path.dirname(mod2_path), "shaders",
                                       '')
        tags_dir = self.tags_dir.get()
        has_local_shaders = os.path.exists(shaders_dir) and os.path.exists(
            tags_dir)
        if errors_occurred:
            print("    Errors occurred while loading jms files.")
        elif os.path.isfile(mod2_path):
            try:
                self.mod2_tag = mod2_def.build(filepath=mod2_path)

                tagdata = self.mod2_tag.data.tagdata
                self.merged_jms.node_list_checksum = tagdata.node_list_checksum
                self.superhigh_lod_cutoff.set(str(
                    tagdata.superhigh_lod_cutoff))
                self.high_lod_cutoff.set(str(tagdata.high_lod_cutoff))
                self.medium_lod_cutoff.set(str(tagdata.medium_lod_cutoff))
                self.low_lod_cutoff.set(str(tagdata.low_lod_cutoff))
                self.superlow_lod_cutoff.set(str(tagdata.superlow_lod_cutoff))

                # get any shaders in the gbxmodel and set the shader_path
                # and shader_type for any matching materials in the jms
                shdr_refs = {}
                for shdr_ref in tagdata.shaders.STEPTREE:
                    shdr_name = shdr_ref.shader.filepath.split(
                        "\\")[-1].lower()
                    shdr_refs.setdefault(shdr_name, []).append(shdr_ref)

                for mat in merged_jms.materials:
                    shdr_ref = shdr_refs.get(mat.name, [""]).pop(0)
                    if shdr_ref:
                        mat.shader_type = shdr_ref.shader.tag_class.enum_name
                        mat.shader_path = shdr_ref.shader.filepath

                local_shaders = {}
                if has_local_shaders and is_in_dir(shaders_dir, tags_dir):
                    # fill in any missing shader paths with ones found nearby
                    for _, __, files in os.walk(shaders_dir):
                        for filename in files:
                            name, ext = os.path.splitext(filename)
                            ext = ext.lower()
                            if ext.startswith(".shader"):
                                local_shaders.setdefault(
                                    name.split("\\")[-1].lower(), []).append(
                                        os.path.join(shaders_dir, filename))
                        break

                    for mat in merged_jms.materials:
                        shader_path = local_shaders.get(mat.name, [""]).pop(0)
                        if "shader_" in mat.shader_type or not shader_path:
                            continue

                        # shader type isnt set. Try to detect its location and
                        # type if possible, or set it to a default value if not
                        shader_path = shader_path.lower().replace("/", "\\")
                        name, ext = os.path.splitext(shader_path)
                        mat.shader_path = os.path.relpath(name,
                                                          tags_dir).strip("\\")
                        mat.shader_type = ext.strip(".")

            except Exception:
                print(format_exc())
        else:
            self.superhigh_lod_cutoff.set("0.0")
            self.high_lod_cutoff.set("0.0")
            self.medium_lod_cutoff.set("0.0")
            self.low_lod_cutoff.set("0.0")
            self.superlow_lod_cutoff.set("0.0")

        for mat in merged_jms.materials:
            shader_path = mat.shader_path
            if mat.shader_type in ("shader", ""):
                assume_shaders_dir = not shaders_dir

                if not assume_shaders_dir:
                    try:
                        shader_path = os.path.relpath(
                            os.path.join(shaders_dir, shader_path), tags_dir)
                        shader_path = shader_path.strip("\\")
                    except ValueError:
                        assume_shaders_dir = True

                mat.shader_type = "shader_model"
            else:
                assume_shaders_dir = False

            if assume_shaders_dir or shader_path.startswith("..\\"):
                shader_path = "shaders\\" + os.path.basename(shader_path)

            mat.shader_path = shader_path.lstrip("..\\")

        if not self.mod2_tag:
            print(
                "    Existing gbxmodel tag not detected or could not be loaded.\n"
                "        A new gbxmodel tag will be created.")

        print("Finished loading models. Took %.6f seconds.\n" %
              (time.time() - start))
        self.select_shader(0)

    def _save_models(self):
        models_dir = self.jms_dir.get()
        if not models_dir:
            return

        start = time.time()
        print("Saving jms models...")
        for jms_model in self.jms_models:
            if isinstance(jms_model, JmsModel):
                fname = "%s %s.jms" % (jms_model.perm_name,
                                       jms_model.lod_level)
                if not jms_model.is_random_perm:
                    fname = "~" + fname

                write_jms(os.path.join(models_dir, fname), jms_model)

        print("Finished saving models. Took %s seconds.\n" %
              str(time.time() - start).split('.')[0])

    def _compile_gbxmodel(self):
        if not self.merged_jms:
            return

        try:
            superhigh_lod_cutoff = self.superhigh_lod_cutoff.get().strip(" ")
            high_lod_cutoff = self.high_lod_cutoff.get().strip(" ")
            medium_lod_cutoff = self.medium_lod_cutoff.get().strip(" ")
            low_lod_cutoff = self.low_lod_cutoff.get().strip(" ")
            superlow_lod_cutoff = self.superlow_lod_cutoff.get().strip(" ")

            if not superhigh_lod_cutoff: superhigh_lod_cutoff = "0"
            if not high_lod_cutoff: high_lod_cutoff = "0"
            if not medium_lod_cutoff: medium_lod_cutoff = "0"
            if not low_lod_cutoff: low_lod_cutoff = "0"
            if not superlow_lod_cutoff: superlow_lod_cutoff = "0"

            superhigh_lod_cutoff = float(superhigh_lod_cutoff)
            high_lod_cutoff = float(high_lod_cutoff)
            medium_lod_cutoff = float(medium_lod_cutoff)
            low_lod_cutoff = float(low_lod_cutoff)
            superlow_lod_cutoff = float(superlow_lod_cutoff)
        except ValueError:
            print("LOD cutoffs are invalid.")
            return

        updating = self.mod2_tag is not None
        if updating:
            print("Updating existing gbxmodel tag.")
            mod2_tag = self.mod2_tag
        else:
            print("Creating new gbxmodel tag.")
            mod2_tag = mod2_def.build()

            while not self.gbxmodel_path.get():
                self.gbxmodel_path_browse(True)
                if not self.gbxmodel_path.get():
                    if messagebox.askyesno(
                            "Unsaved gbxmodel",
                            "Are you sure you wish to cancel saving?",
                            icon='warning',
                            parent=self):
                        print("    Gbxmodel compilation cancelled.")
                        return

            mod2_tag.filepath = self.gbxmodel_path.get()

        self.app_root.update()

        errors = compile_gbxmodel(mod2_tag, self.merged_jms)
        if errors:
            for error in errors:
                print(error)
            print("Gbxmodel compilation failed.")
            return

        tags_dir = self.tags_dir.get()
        if tags_dir:
            data_dir = os.path.join(os.path.dirname(os.path.dirname(tags_dir)),
                                    "data", "")
            for mat in self.merged_jms.materials:
                try:
                    generate_shader(mat, tags_dir, data_dir)
                except Exception:
                    print(format_exc())
                    print("Failed to generate shader tag.")

        tagdata = mod2_tag.data.tagdata
        tagdata.superhigh_lod_cutoff = superhigh_lod_cutoff
        tagdata.high_lod_cutoff = high_lod_cutoff
        tagdata.medium_lod_cutoff = medium_lod_cutoff
        tagdata.low_lod_cutoff = low_lod_cutoff
        tagdata.superlow_lod_cutoff = superlow_lod_cutoff

        try:
            mod2_tag.calc_internal_data()
            mod2_tag.serialize(temp=False,
                               backup=False,
                               calc_pointers=False,
                               int_test=False)
            print("    Finished")
        except Exception:
            print(format_exc())
            print("    Could not save compiled gbxmodel.")
    def __init__(self, app_root, *args, **kwargs):
        if window_base_class == tk.Toplevel:
            kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color)
            self.app_root = app_root
        else:
            self.app_root = self

        window_base_class.__init__(self, app_root, *args, **kwargs)
        BinillaWidget.__init__(self, *args, **kwargs)

        self.title("Gbxmodel compiler")
        self.resizable(1, 1)
        self.update()
        try:
            self.iconbitmap(e_c.MOZZ_ICON_PATH)
        except Exception:
            print("Could not load window icon.")

        self.superhigh_lod_cutoff = tk.StringVar(self)
        self.high_lod_cutoff = tk.StringVar(self)
        self.medium_lod_cutoff = tk.StringVar(self)
        self.low_lod_cutoff = tk.StringVar(self)
        self.superlow_lod_cutoff = tk.StringVar(self)
        self.shader_path_string_var = tk.StringVar(self)

        tags_dir = getattr(app_root, "tags_dir", "")

        self.optimize_level = tk.IntVar(self)
        self.tags_dir = tk.StringVar(self, tags_dir if tags_dir else "")
        self.jms_dir = tk.StringVar(self)
        self.gbxmodel_path = tk.StringVar(self)

        # make the frames
        self.main_frame = tk.Frame(self)
        self.jms_info_frame = tk.LabelFrame(self, text="Model info")

        self.dirs_frame = tk.LabelFrame(self.main_frame, text="Directories")
        self.buttons_frame = tk.Frame(self.main_frame)
        self.settings_frame = tk.LabelFrame(self.main_frame,
                                            text="Compilation settings")

        self.jms_dir_frame = tk.LabelFrame(self.dirs_frame,
                                           text="Source models folder")
        self.tags_dir_frame = tk.LabelFrame(self.dirs_frame,
                                            text="Tags directory root folder")
        self.gbxmodel_path_frame = tk.LabelFrame(self.dirs_frame,
                                                 text="Gbxmodel output path")

        self.lods_frame = tk.LabelFrame(self.settings_frame,
                                        text="LOD cutoffs")
        self.shaders_frame = tk.LabelFrame(self.settings_frame, text="Shaders")

        self.optimize_label = tk.Label(
            self.settings_frame,
            justify="right",
            text=("Vertex optimization\n(Set before loading)"))
        self.optimize_menu = ScrollMenu(self.settings_frame,
                                        menu_width=5,
                                        options=("None", "Exact", "Loose"))
        self.optimize_menu.sel_index = 1

        self.jms_info_tree = tk.ttk.Treeview(self.jms_info_frame,
                                             selectmode='browse',
                                             padding=(0, 0),
                                             height=4)
        self.jms_info_vsb = tk.Scrollbar(self.jms_info_frame,
                                         orient='vertical',
                                         command=self.jms_info_tree.yview)
        self.jms_info_hsb = tk.Scrollbar(self.jms_info_frame,
                                         orient='horizontal',
                                         command=self.jms_info_tree.xview)
        self.jms_info_tree.config(yscrollcommand=self.jms_info_vsb.set,
                                  xscrollcommand=self.jms_info_hsb.set)

        self.shader_names_menu = ScrollMenu(
            self.shaders_frame,
            menu_width=10,
            callback=self.select_shader,
            option_getter=self.get_shader_names,
            options_volatile=True)
        self.shader_types_menu = ScrollMenu(self.shaders_frame,
                                            menu_width=20,
                                            options=shader_types,
                                            callback=self.select_shader_type)
        self.shader_path_browse_button = tk.Button(
            self.shaders_frame,
            text="Browse",
            width=6,
            command=self.browse_shader_path)
        self.shader_path_entry = tk.Entry(
            self.shaders_frame, textvariable=self.shader_path_string_var)

        self.write_trace(self.shader_path_string_var, self.shader_path_edited)

        self.superhigh_lod_label = tk.Label(self.lods_frame, text="Superhigh")
        self.high_lod_label = tk.Label(self.lods_frame, text="High")
        self.medium_lod_label = tk.Label(self.lods_frame, text="Medium")
        self.low_lod_label = tk.Label(self.lods_frame, text="Low")
        self.superlow_lod_label = tk.Label(self.lods_frame, text="Superlow")
        self.superhigh_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.superhigh_lod_cutoff,
            width=6,
            justify='right')
        self.high_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.high_lod_cutoff,
            width=6,
            justify='right')
        self.medium_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.medium_lod_cutoff,
            width=6,
            justify='right')
        self.low_lod_cutoff_entry = tk.Entry(self.lods_frame,
                                             textvariable=self.low_lod_cutoff,
                                             width=6,
                                             justify='right')
        self.superlow_lod_cutoff_entry = tk.Entry(
            self.lods_frame,
            textvariable=self.superlow_lod_cutoff,
            width=6,
            justify='right')

        self.jms_dir_entry = tk.Entry(self.jms_dir_frame,
                                      textvariable=self.jms_dir,
                                      state=tk.DISABLED)
        self.jms_dir_browse_button = tk.Button(self.jms_dir_frame,
                                               text="Browse",
                                               command=self.jms_dir_browse)

        self.tags_dir_entry = tk.Entry(self.tags_dir_frame,
                                       textvariable=self.tags_dir,
                                       state=tk.DISABLED)
        self.tags_dir_browse_button = tk.Button(self.tags_dir_frame,
                                                text="Browse",
                                                command=self.tags_dir_browse)

        self.gbxmodel_path_entry = tk.Entry(self.gbxmodel_path_frame,
                                            textvariable=self.gbxmodel_path,
                                            state=tk.DISABLED)
        self.gbxmodel_path_browse_button = tk.Button(
            self.gbxmodel_path_frame,
            text="Browse",
            command=self.gbxmodel_path_browse)

        self.load_button = tk.Button(self.buttons_frame,
                                     text="Load\nmodels",
                                     command=self.load_models)
        self.save_button = tk.Button(self.buttons_frame,
                                     text="Save as JMS",
                                     command=self.save_models)
        self.compile_button = tk.Button(self.buttons_frame,
                                        text="Compile\ngbxmodel",
                                        command=self.compile_gbxmodel)

        self.populate_model_info_tree()

        # pack everything
        self.main_frame.pack(fill="both", side='left', pady=3, padx=3)
        self.jms_info_frame.pack(fill="both",
                                 side='left',
                                 pady=3,
                                 padx=3,
                                 expand=True)

        self.dirs_frame.pack(fill="x")
        self.buttons_frame.pack(fill="x", pady=3, padx=3)
        self.settings_frame.pack(fill="both", expand=True)

        self.superhigh_lod_label.grid(sticky='e',
                                      row=0,
                                      column=0,
                                      padx=3,
                                      pady=1)
        self.high_lod_label.grid(sticky='e', row=1, column=0, padx=3, pady=1)
        self.medium_lod_label.grid(sticky='e', row=2, column=0, padx=3, pady=1)
        self.low_lod_label.grid(sticky='e', row=3, column=0, padx=3, pady=1)
        self.superlow_lod_label.grid(sticky='e',
                                     row=4,
                                     column=0,
                                     padx=3,
                                     pady=1)
        self.superhigh_lod_cutoff_entry.grid(sticky='ew',
                                             row=0,
                                             column=1,
                                             padx=3,
                                             pady=1)
        self.high_lod_cutoff_entry.grid(sticky='ew',
                                        row=1,
                                        column=1,
                                        padx=3,
                                        pady=1)
        self.medium_lod_cutoff_entry.grid(sticky='ew',
                                          row=2,
                                          column=1,
                                          padx=3,
                                          pady=1)
        self.low_lod_cutoff_entry.grid(sticky='ew',
                                       row=3,
                                       column=1,
                                       padx=3,
                                       pady=1)
        self.superlow_lod_cutoff_entry.grid(sticky='ew',
                                            row=4,
                                            column=1,
                                            padx=3,
                                            pady=1)

        self.jms_dir_frame.pack(expand=True, fill='x')
        self.tags_dir_frame.pack(expand=True, fill='x')
        self.gbxmodel_path_frame.pack(expand=True, fill='x')

        self.jms_dir_entry.pack(side='left', expand=True, fill='x')
        self.jms_dir_browse_button.pack(side='left')

        self.gbxmodel_path_entry.pack(side='left', expand=True, fill='x')
        self.gbxmodel_path_browse_button.pack(side='left')

        self.tags_dir_entry.pack(side='left', expand=True, fill='x')
        self.tags_dir_browse_button.pack(side='left')

        self.optimize_label.grid(sticky='ne', row=3, column=1, padx=3)
        self.optimize_menu.grid(sticky='new',
                                row=3,
                                column=2,
                                padx=3,
                                pady=(3, 0))
        self.lods_frame.grid(sticky='ne', row=0, column=3, rowspan=4)
        self.shaders_frame.grid(sticky='nsew',
                                row=0,
                                column=0,
                                columnspan=3,
                                rowspan=3,
                                pady=(0, 3))

        self.shader_names_menu.grid(sticky='new',
                                    row=0,
                                    column=0,
                                    padx=3,
                                    columnspan=5,
                                    pady=2)
        self.shader_path_browse_button.grid(sticky='ne',
                                            row=1,
                                            column=4,
                                            padx=3,
                                            pady=2)
        self.shader_types_menu.grid(sticky='new',
                                    row=1,
                                    column=1,
                                    padx=3,
                                    columnspan=3,
                                    pady=2)
        self.shader_path_entry.grid(sticky='new',
                                    row=2,
                                    column=0,
                                    padx=3,
                                    columnspan=5,
                                    pady=2)

        self.jms_info_hsb.pack(side="bottom", fill='x')
        self.jms_info_vsb.pack(side="right", fill='y')
        self.jms_info_tree.pack(side='left', fill='both', expand=True)

        self.load_button.pack(side='left', expand=True, fill='both', padx=3)
        self.save_button.pack(side='left', expand=True, fill='both', padx=3)
        self.compile_button.pack(side='right',
                                 expand=True,
                                 fill='both',
                                 padx=3)

        self.apply_style()
        if self.app_root is not self:
            self.transient(self.app_root)
Beispiel #12
0
class ArrayFrame(container_frame.ContainerFrame):
    '''Used for array nodes. Displays a single element in
    the ArrayBlock represented by it, and contains a combobox
    for selecting which array element is displayed.'''

    sel_index = -1
    sel_menu = None
    populated = False
    option_cache = None
    options_sane = False

    def __init__(self, *args, **kwargs):
        kwargs.update(relief='flat',
                      bd=0,
                      highlightthickness=0,
                      bg=self.default_bg_color)
        field_widget.FieldWidget.__init__(self, *args, **kwargs)
        tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs))

        show_frame = bool(
            kwargs.pop('show_frame', not self.blocks_start_hidden))
        if self.is_empty and self.hide_if_blank:
            show_frame = False

        self.show = tk.BooleanVar()
        self.show.set(show_frame)
        self.options_sane = False

        node_len = 0
        try:
            node_len = len(self.node)
        except Exception:
            pass

        self.sel_index = (node_len > 0) - 1

        # make the title, element menu, and all the buttons
        self.controls = tk.Frame(self, relief='raised', bd=self.frame_depth)
        self.title = title = tk.Frame(self.controls, relief='flat', bd=0)
        self.buttons = buttons = tk.Frame(self.controls, relief='flat', bd=0)

        toggle_text = '-' if show_frame else '+'

        self.title_label = tk.Label(
            title,
            text=self.gui_name,
            justify='left',
            anchor='w',
            width=self.title_size,
            font=self.get_font("frame_title"),
            disabledforeground=self.text_disabled_color)
        self.title_label.font_type = "frame_title"

        self.show_btn = ttk.Checkbutton(title,
                                        width=3,
                                        text=toggle_text,
                                        command=self.toggle_visible,
                                        style='ShowButton.TButton')
        self.sel_menu = ScrollMenu(title,
                                   f_widget_parent=self,
                                   sel_index=self.sel_index,
                                   max_index=node_len - 1,
                                   option_getter=self.get_options,
                                   callback=self.select_option)

        self.shift_up_btn = ttk.Button(title,
                                       width=7,
                                       text='Shift ▲',
                                       command=self.shift_entry_up)
        self.shift_down_btn = ttk.Button(buttons,
                                         width=7,
                                         text='Shift ▼',
                                         command=self.shift_entry_down)
        self.add_btn = ttk.Button(buttons,
                                  width=4,
                                  text='Add',
                                  command=self.add_entry)
        self.insert_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Insert',
                                     command=self.insert_entry)
        self.duplicate_btn = ttk.Button(buttons,
                                        width=9,
                                        text='Duplicate',
                                        command=self.duplicate_entry)
        self.delete_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Delete',
                                     command=self.delete_entry)
        self.delete_all_btn = ttk.Button(buttons,
                                         width=10,
                                         text='Delete all',
                                         command=self.delete_all_entries)

        self.import_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Import',
                                     command=self.import_node)
        self.export_btn = ttk.Button(buttons,
                                     width=6,
                                     text='Export',
                                     command=self.export_node)

        # pack the title, menu, and all the buttons
        for w in (self.shift_down_btn, self.export_btn, self.import_btn,
                  self.delete_all_btn, self.delete_btn, self.duplicate_btn,
                  self.insert_btn, self.add_btn):
            w.pack(side="right", padx=(0, 4), pady=(2, 2))

        self.show_btn.pack(side="left")
        if self.gui_name != '':
            self.title_label.pack(side="left", fill="x", expand=True)

        self.sel_menu.pack(side="left", fill="x", expand=True, padx=(0, 4))
        self.shift_up_btn.pack(side="right", padx=(0, 1), pady=(2, 2))

        self.title.pack(fill="x", expand=True, padx=0)
        self.buttons.pack(fill="x", expand=True, padx=0)
        self.controls.pack(fill="x", expand=True, padx=0)

        self.populate()
        self._initialized = True

    @property
    def is_empty(self):
        if getattr(self, "node", None) is None:
            return True
        return len(self.node) == 0

    def load_node_data(self, parent, node, attr_index, desc=None):
        field_widget.FieldWidget.load_node_data(self, parent, node, attr_index,
                                                desc)
        sub_node = attr_index = None
        if self.node:
            attr_index = self.sel_index
            if attr_index in range(len(self.node)):
                sub_node = self.node[attr_index]
            else:
                attr_index = len(self.node) - 1
                if attr_index < 0:
                    attr_index = None

        if self.sel_menu is not None:
            self.options_sane = self.sel_menu.options_menu_sane = False

        for wid in self.f_widgets:
            # there must be only one entry in self.f_widgets
            w = self.f_widgets[wid]
            if w.load_node_data(self.node, sub_node, attr_index):
                return True

        return False

    def unload_node_data(self):
        self.sel_menu.update_label(" ")
        container_frame.ContainerFrame.unload_node_data(self)

    def set_disabled(self, disable=True):
        disable = disable or not self.editable
        if self.node is None and not disable:
            return

        if getattr(self, "sel_menu", None):
            self.sel_menu.set_disabled(disable)

        if bool(disable) == self.disabled:
            pass
        elif not disable:
            self.set_all_buttons_disabled(False)
            self.disable_unusable_buttons()
        else:
            new_state = tk.DISABLED if disable else tk.NORMAL
            for w in (self.shift_up_btn, self.shift_down_btn, self.add_btn,
                      self.insert_btn, self.duplicate_btn, self.delete_btn,
                      self.delete_all_btn):
                if w:
                    w.config(state=new_state)

        container_frame.ContainerFrame.set_disabled(self, disable)

    def apply_style(self, seen=None):
        container_frame.ContainerFrame.apply_style(self, seen)
        self.controls.config(bd=self.frame_depth, bg=self.frame_bg_color)
        self.title.config(bg=self.frame_bg_color)
        self.title_label.config(bg=self.frame_bg_color)
        self.buttons.config(bg=self.frame_bg_color)
        #if self.show.get():
        #    self.pose_fields()

    def destroy(self):
        # These will linger and take up RAM, even if the widget is destroyed.
        # Need to remove the references manually
        self.option_cache = None
        container_frame.ContainerFrame.destroy(self)

    def export_node(self):
        try:
            # pass call to the export_node method of the array entry's widget
            w = self.f_widgets[self.f_widget_ids[0]]
        except Exception:
            return
        w.export_node()

    def import_node(self):
        try:
            # pass call to the import_node method of the array entry's widget
            w = self.f_widgets[self.f_widget_ids[0]]
        except Exception:
            return
        w.import_node()

    def get_options(self, opt_index=None):
        '''
        Returns a list of the option strings sorted by option index.
        '''
        if (self.option_cache is None or not self.options_sane
                or opt_index is not None):
            result = self.generate_options(opt_index)
            if opt_index is not None:
                return result

        if opt_index is None:
            return self.option_cache
        elif opt_index == e_c.ACTIVE_ENUM_NAME:
            opt_index = self.sel_index

        if opt_index < 0: opt_index = -1

        return self.option_cache.get(opt_index)

    def generate_options(self, opt_index=None):
        # sort the options by value(values are integers)
        options = {i: n for n, i in self.desc.get('NAME_MAP', {}).items()}

        if self.node:
            node, desc = self.node, self.desc
            sub_desc = desc['SUB_STRUCT']
            def_struct_name = sub_desc['NAME']
            if self.use_gui_names and 'GUI_NAME' in sub_desc:
                def_struct_name = sub_desc['GUI_NAME']

            options_to_generate = range(len(node))
            if opt_index is not None:
                options_to_generate = ((opt_index, ) if opt_index
                                       in options_to_generate else ())

            for i in options_to_generate:
                if i in options:
                    continue
                sub_node = node[i]
                if not hasattr(sub_node, 'desc'):
                    continue
                sub_desc = sub_node.desc
                sub_struct_name = sub_desc.get('GUI_NAME', sub_desc['NAME'])
                if sub_struct_name == def_struct_name:
                    continue

                options[i] = sub_struct_name

        if opt_index is None:
            self.options_sane = True
            self.option_cache = options
            if self.sel_menu is not None:
                self.sel_menu.options_menu_sane = False
                self.sel_menu.max_index = len(node) - 1
            return options
        return options.get(opt_index, None)

    def set_shift_up_disabled(self, disable=True):
        '''
        Disables the move up button if disable is True. Enables it if not.
        '''
        if disable: self.shift_up_btn.config(state="disabled")
        else: self.shift_up_btn.config(state="normal")

    def set_shift_down_disabled(self, disable=True):
        '''
        Disables the move down button if disable is True. Enables it if not.
        '''
        if disable: self.shift_down_btn.config(state="disabled")
        else: self.shift_down_btn.config(state="normal")

    def set_add_disabled(self, disable=True):
        '''Disables the add button if disable is True. Enables it if not.'''
        if disable: self.add_btn.config(state="disabled")
        else: self.add_btn.config(state="normal")

    def set_insert_disabled(self, disable=True):
        '''Disables the insert button if disable is True. Enables it if not.'''
        if disable: self.insert_btn.config(state="disabled")
        else: self.insert_btn.config(state="normal")

    def set_duplicate_disabled(self, disable=True):
        '''
        Disables the duplicate button if disable is True. Enables it if not.
        '''
        if disable: self.duplicate_btn.config(state="disabled")
        else: self.duplicate_btn.config(state="normal")

    def set_delete_disabled(self, disable=True):
        '''Disables the delete button if disable is True. Enables it if not.'''
        if disable: self.delete_btn.config(state="disabled")
        else: self.delete_btn.config(state="normal")

    def set_delete_all_disabled(self, disable=True):
        '''
        Disables the delete_all button if disable is True. Enables it if not.
        '''
        if disable: self.delete_all_btn.config(state="disabled")
        else: self.delete_all_btn.config(state="normal")

    def edit_apply(self=None, *, edit_state, undo=True):
        state = edit_state

        edit_type = state.edit_type
        i = state.attr_index
        undo_node = state.undo_node
        redo_node = state.redo_node

        edit_info = state.edit_info
        sel_index = edit_info.get('sel_index', 0)

        w, node = field_widget.FieldWidget.get_widget_and_node(
            nodepath=state.nodepath, tag_window=state.tag_window)

        if edit_type == 'shift_up':
            node[i], node[i - 1] = node[i - 1], node[i]
        elif edit_type == 'shift_down':
            node[i], node[i + 1] = node[i + 1], node[i]
        elif edit_type in ('add', 'insert', 'duplicate'):
            if undo:
                sel_index = None
                node.pop(i)
            else:
                node.insert(i, redo_node)
        elif edit_type == 'delete':
            if undo:
                node.insert(i, undo_node)
            else:
                sel_index = None
                node.pop(i)
        elif edit_type == 'delete_all':
            if undo:
                node[:] = undo_node
            else:
                del node[:]
                sel_index = None
        else:
            raise TypeError('Unknown edit_state type')

        if w is not None:
            try:
                if w.desc is not state.desc:
                    return

                if sel_index is None:
                    pass
                elif edit_type in ('add', 'insert', 'duplicate', 'delete'):
                    w.sel_index = sel_index
                elif edit_type in ('shift_up', 'shift_down'):
                    w.sel_index = sel_index
                    if undo:
                        pass
                    elif 'down' in edit_type:
                        w.sel_index += 1
                    else:
                        w.sel_index -= 1

                max_index = len(node) - 1
                w.sel_menu.max_index = max_index
                w.options_sane = w.sel_menu.options_menu_sane = False
                if w.sel_index < 0:
                    w.select_option(0, force=True)
                elif w.sel_index > max_index:
                    w.select_option(max_index, force=True)
                else:
                    w.select_option(w.sel_index, force=True)

                w.needs_flushing = False
                w.set_edited()
            except Exception:
                print(format_exc())

    def edit_create(self, **kwargs):
        # add own stuff
        kwargs.setdefault("sel_index", self.sel_index)
        field_widget.FieldWidget.edit_create(self, **kwargs)

    def shift_entry_up(self):
        if not hasattr(self.node, '__len__') or len(self.node) < 2:
            return

        node = self.node
        index = self.sel_index
        if index <= 0:
            return

        self.set_edited()  # do this first so the TagWindow detects that
        #                   the title needs to be updated with an asterisk
        self.edit_create(edit_type='shift_up', attr_index=index)
        node[index], node[index - 1] = node[index - 1], node[index]

        self.sel_index = self.sel_menu.sel_index = index - 1
        self.options_sane = self.sel_menu.options_menu_sane = False
        self.sel_menu.update_label()

    def shift_entry_down(self):
        if not hasattr(self.node, '__len__') or len(self.node) < 2:
            return

        node = self.node
        index = self.sel_index
        if index >= len(node) - 1:
            return

        self.set_edited()  # do this first so the TagWindow detects that
        #                   the title needs to be updated with an asterisk
        self.edit_create(edit_type='shift_down', attr_index=index)
        node[index], node[index + 1] = node[index + 1], node[index]

        self.sel_index = self.sel_menu.sel_index = index + 1
        self.options_sane = self.sel_menu.options_menu_sane = False
        self.sel_menu.update_label()

    def add_entry(self):
        if not hasattr(self.node, '__len__'):
            return

        field_max = self.field_max
        if field_max is not None and len(self.node) >= field_max:
            if self.enforce_max:
                return

        attr_index = len(self.node)
        self.set_edited()  # do this first so the TagWindow detects that
        #                   the title needs to be updated with an asterisk
        self.node.append()

        self.edit_create(edit_type='add',
                         attr_index=attr_index,
                         redo_node=self.node[attr_index],
                         sel_index=attr_index)

        self.options_sane = self.sel_menu.options_menu_sane = False
        self.set_all_buttons_disabled(self.disabled)
        self.disable_unusable_buttons()
        self.select_option(len(self.node) - 1, True)

    def insert_entry(self):
        if not hasattr(self.node, '__len__'):
            return

        field_max = self.field_max
        if field_max is not None and len(self.node) >= field_max:
            if self.enforce_max:
                return

        attr_index = self.sel_index = max(self.sel_index, 0)
        self.set_edited()  # do this first so the TagWindow detects that
        #                   the title needs to be updated with an asterisk
        self.node.insert(attr_index)

        self.edit_create(edit_type='insert',
                         attr_index=attr_index,
                         redo_node=self.node[attr_index],
                         sel_index=attr_index)

        self.options_sane = self.sel_menu.options_menu_sane = False
        self.set_all_buttons_disabled(self.disabled)
        self.disable_unusable_buttons()
        self.select_option(attr_index, True)  # select the new entry

    def duplicate_entry(self):
        if not hasattr(self.node, '__len__') or len(self.node) < 1:
            return

        field_max = self.field_max
        if field_max is not None and len(self.node) >= field_max:
            if self.enforce_max:
                return

        self.sel_index = self.sel_menu.sel_index = max(self.sel_index, 0)
        self.set_edited()  # do this first so the TagWindow detects that
        #                   the title needs to be updated with an asterisk
        new_subnode = deepcopy(self.node[self.sel_index])
        attr_index = len(self.node)

        self.edit_create(edit_type='duplicate',
                         attr_index=attr_index,
                         redo_node=new_subnode,
                         sel_index=attr_index)

        self.node.append(new_subnode)

        self.options_sane = self.sel_menu.options_menu_sane = False
        self.set_all_buttons_disabled(self.disabled)
        self.disable_unusable_buttons()
        self.select_option(attr_index, True)

    def delete_entry(self):
        if not hasattr(self.node, '__len__') or len(self.node) == 0:
            return

        field_min = self.field_min
        if field_min is None:
            field_min = 0

        if len(self.node) <= field_min:
            if self.enforce_min:
                return

        if not len(self.node):
            self.sel_menu.disable()
            return

        attr_index = max(self.sel_index, 0)

        self.set_edited()  # do this first so the TagWindow detects that
        #                   the title needs to be updated with an asterisk
        self.edit_create(edit_type='delete',
                         undo_node=self.node[attr_index],
                         attr_index=attr_index,
                         sel_index=attr_index)

        del self.node[attr_index]
        attr_index = max(-1, min(len(self.node) - 1, attr_index))

        self.options_sane = self.sel_menu.options_menu_sane = False
        self.select_option(attr_index, True)
        self.set_all_buttons_disabled(self.disabled)
        self.disable_unusable_buttons()

    def delete_all_entries(self):
        if not hasattr(self.node, '__len__') or len(self.node) == 0:
            return

        field_min = self.field_min
        if field_min is None:
            field_min = 0

        if len(self.node) <= field_min:
            if self.enforce_min:
                return

        if not len(self.node):
            self.sel_menu.disable()
            return

        self.set_edited()  # do this first so the TagWindow detects that
        #                   the title needs to be updated with an asterisk
        self.edit_create(edit_type='delete_all', undo_node=tuple(self.node[:]))

        del self.node[:]

        self.options_sane = self.sel_menu.options_menu_sane = False
        self.set_all_buttons_disabled(self.disabled)
        self.disable_unusable_buttons()
        self.select_option(self.sel_index, True)

    def set_all_buttons_disabled(self, disable=False):
        for btn in (self.add_btn, self.insert_btn, self.duplicate_btn,
                    self.delete_btn, self.delete_all_btn, self.import_btn,
                    self.export_btn, self.shift_up_btn, self.shift_down_btn):
            if disable:
                btn.config(state=tk.DISABLED)
            else:
                btn.config(state=tk.NORMAL)

    def disable_unusable_buttons(self):
        no_node = not hasattr(self.node, '__len__')
        if no_node or len(self.node) < 2:
            self.set_shift_up_disabled()
            self.set_shift_down_disabled()

        if no_node or (isinstance(self.desc.get('SIZE'), int)
                       and self.enforce_min):
            self.set_add_disabled()
            self.set_insert_disabled()
            self.set_duplicate_disabled()
            self.set_delete_disabled()
            self.set_delete_all_disabled()
            return

        field_max = self.field_max
        field_min = self.field_min
        if field_min is None or field_min < 0: field_min = 0
        enforce_min = self.enforce_min or field_min == 0
        enforce_max = self.enforce_max

        if field_max is not None and len(
                self.node) >= field_max and enforce_max:
            self.set_add_disabled()
            self.set_insert_disabled()
            self.set_duplicate_disabled()

        if len(self.node) <= field_min and (enforce_min or not self.node):
            self.set_delete_disabled()
            self.set_delete_all_disabled()

        if not self.node:
            self.set_export_disabled()
            self.set_import_disabled()
            self.set_duplicate_disabled()

    def populate(self):
        node = self.node
        desc = self.desc
        sub_node = None
        sub_desc = desc['SUB_STRUCT']

        if node and self.sel_index in range(len(node)):
            sub_node = node[self.sel_index]
            sub_desc = desc['SUB_STRUCT']
            if hasattr(sub_node, 'desc'):
                sub_desc = sub_node.desc

        if self.content in (None, self):
            self.content = tk.Frame(self,
                                    relief="sunken",
                                    bd=self.frame_depth,
                                    bg=self.default_bg_color)

        self.sel_menu.default_text = sub_desc.get('GUI_NAME',
                                                  sub_desc.get('NAME', ""))
        self.sel_menu.update_label()
        self.disable_unusable_buttons()

        rebuild = not bool(self.f_widgets)
        if hasattr(node, '__len__') and len(node) == 0:
            # disabling existing widgets
            self.sel_index = -1
            self.sel_menu.max_index = -1
            if self.f_widgets:
                self.unload_child_node_data()
        else:
            for w in self.f_widgets:
                if getattr(w, "desc", None) != sub_desc:
                    rebuild = True
                    break

        if rebuild:
            # destroy existing widgets and make new ones
            self.populated = False
            self.f_widget_ids = []
            self.f_widget_ids_map = {}
            self.f_widget_ids_map_inv = {}

            # destroy any child widgets of the content
            for c in list(self.f_widgets.values()):
                c.destroy()

            for w in (self, self.content, self.title, self.title_label,
                      self.controls, self.buttons):
                w.tooltip_string = self.desc.get('TOOLTIP')

            self.display_comment(self.content)

            widget_cls = self.widget_picker.get_widget(sub_desc)
            try:
                widget = widget_cls(self.content,
                                    node=sub_node,
                                    parent=node,
                                    show_title=False,
                                    dont_padx_fields=True,
                                    attr_index=self.sel_index,
                                    tag_window=self.tag_window,
                                    f_widget_parent=self,
                                    disabled=self.disabled)
            except Exception:
                print(format_exc())
                widget = data_frame.NullFrame(self.content,
                                              node=sub_node,
                                              parent=node,
                                              show_title=False,
                                              dont_padx_fields=True,
                                              attr_index=self.sel_index,
                                              tag_window=self.tag_window,
                                              f_widget_parent=self,
                                              disabled=self.disabled)

            wid = id(widget)
            self.f_widget_ids.append(wid)
            self.f_widget_ids_map[self.sel_index] = wid
            self.f_widget_ids_map_inv[wid] = self.sel_index

            self.populated = True
            self.build_f_widget_cache()

            # now that the field widgets are created, position them
            if self.show.get():
                self.pose_fields()

        if self.node is None:
            self.set_disabled(True)
        else:
            self.set_children_disabled(not self.node)

    def reload(self):
        '''Resupplies the nodes to the widgets which display them.'''
        try:
            node = self.node if self.node else ()
            desc = self.desc

            is_empty = len(node) == 0
            field_max = self.field_max
            field_min = self.field_min
            if field_min is None: field_min = 0

            self.set_all_buttons_disabled(self.disabled)
            self.disable_unusable_buttons()

            if is_empty:
                self.sel_menu.sel_index = -1
                self.sel_menu.max_index = -1
                # if there is no index to select, destroy the content
                if self.sel_index != -1:
                    self.sel_index = -1

                self.unload_child_node_data()
            else:
                self.f_widget_ids_map = {}
                self.f_widget_ids_map_inv = {}

            self.sel_menu.sel_index = self.sel_index
            self.sel_menu.max_index = len(node) - 1

            sub_node = None
            sub_desc = desc['SUB_STRUCT']
            if node and self.sel_index in range(len(node)):
                sub_node = node[self.sel_index]
                sub_desc = desc['SUB_STRUCT']
                if hasattr(sub_node, 'desc'):
                    sub_desc = sub_node.desc

            for wid in self.f_widget_ids:
                w = self.f_widgets[wid]
                wid = id(w)

                if node and self.sel_index not in range(len(node)):
                    # current selection index is invalid. call select_option
                    # to reset it to some valid option. Don't reload though,
                    # as we will be either reloading or repopulating below.
                    self.select_option(force=True, reload=False)

                self.f_widget_ids_map[self.sel_index] = wid
                self.f_widget_ids_map_inv[wid] = self.sel_index

                # if the descriptors are different, gotta repopulate!
                if w.load_node_data(node, sub_node, self.sel_index, sub_desc):
                    self.populate()
                    self.apply_style()
                    return

                w.reload()
                if w.desc.get("PORTABLE", True) and self.node:
                    self.set_import_disabled(False)
                    self.set_export_disabled(False)
                else:
                    self.set_import_disabled()
                    self.set_export_disabled()

            self.sel_menu.update_label()
            if self.node is None:
                self.set_disabled(True)
            else:
                self.set_children_disabled(not self.node)
        except Exception:
            print(format_exc())

    def pose_fields(self):
        # there should only be one wid in here, but for
        # the sake of consistancy we'll loop over them.
        for wid in self.f_widget_ids:
            w = self.f_widgets[wid]

            # by adding a fixed amount of padding, we fix a problem
            # with difficult to predict padding based on nesting
            w.pack(fill='x',
                   side='top',
                   expand=True,
                   padx=self.vertical_padx,
                   pady=self.vertical_pady)

        # if there are no children in the content, we need to
        # pack in SOMETHING, update the idletasks, and then
        # destroy that something to resize the content frame
        if not self.f_widgets:
            f = tk.Frame(self.content, width=0, height=0, bd=0)
            f.pack()
            self.content.update_idletasks()
            f.destroy()

        self.content.pack(fill='both', side='top', anchor='nw', expand=True)

    def select_option(self, opt_index=None, force=False, reload=True):
        node = self.node if self.node else ()
        curr_index = self.sel_index
        if opt_index is None:
            opt_index = curr_index

        if opt_index < 0:
            opt_index = 0

        if opt_index == curr_index and not force:
            return
        elif not node:
            opt_index = -1
        elif opt_index not in range(len(node)):
            opt_index = len(node) - 1

        # flush any lingering changes
        self.flush()
        self.sel_index = opt_index
        self.sel_menu.sel_index = opt_index
        self.sel_menu.max_index = len(node) - 1
        if reload:
            self.reload()

        self.sel_menu.update_label()

    @property
    def visible_field_count(self):
        # array frames only display one item at a time
        return 1
Beispiel #13
0
class HaloBitmapDisplayFrame(BitmapDisplayFrame):
    # these mappings have the 2nd and 3rd faces swapped on pc for some reason
    cubemap_cross_mapping = (
        (-1,  1),
        ( 2,  4,  0,  5),
        (-1,  3),
        )

    def __init__(self, master, bitmap_tag=None, *args, **kwargs):
        self.bitmap_tag = bitmap_tag
        textures = kwargs.get('textures', ())
        BitmapDisplayFrame.__init__(self, master, *args, **kwargs)
        self.sequence_index = tk.IntVar(self)
        self.sprite_index   = tk.IntVar(self)

        labels = []
        labels.append(tk.Label(self.controls_frame0, text="Sequence index"))
        labels.append(tk.Label(self.controls_frame1, text="Sprite index"))
        for lbl in labels:
            lbl.config(width=15, anchor='w')

        self.sequence_menu = ScrollMenu(self.controls_frame0, menu_width=7,
                                        variable=self.sequence_index)
        self.sprite_menu = ScrollMenu(self.controls_frame1, menu_width=7,
                                      variable=self.sprite_index)

        padx = self.horizontal_padx
        pady = self.horizontal_pady
        for lbl in labels:
            lbl.pack(side='left', padx=(15, 0), pady=pady)
        self.sequence_menu.pack(side='left', padx=padx, pady=pady)
        self.sprite_menu.pack(side='left', padx=padx, pady=pady)
        self.write_trace(self.sequence_index, self.sequence_changed)
        self.write_trace(self.sprite_index,   self.sprite_changed)

        self.change_textures(textures)
        self.apply_style()

    def sequence_changed(self, *args):
        tag = self.bitmap_tag
        if tag is None:
            self.sprite_menu.set_options(())
            self.sequence_menu.set_options(("None", ))
            self.sprite_menu.sel_index = -1
            self.sequence_menu.sel_index = 0
            return

        sequence_i = self.sequence_index.get() - 1
        options = ()
        sequences = tag.data.tagdata.sequences.STEPTREE
        if sequence_i in range(len(sequences)):
            sequence = sequences[sequence_i]
            options = range(len(sequence.sprites.STEPTREE))
            if not options:
                options = range(sequence.bitmap_count)

        self.sprite_menu.set_options(options)
        self.sprite_menu.sel_index = (self.sprite_menu.max_index >= 0) - 1

    def sprite_changed(self, *args):
        self.image_canvas.delete(SPRITE_RECTANGLE_TAG)
        self.image_canvas.delete(SPRITE_CENTER_TAG)
        tag = self.bitmap_tag
        if tag is None:
            self.sprite_menu.set_options(())
            self.sequence_menu.set_options(("None", ))
            self.sprite_menu.sel_index = -1
            self.sequence_menu.sel_index = 0
            return

        data = tag.data.tagdata
        sequences = data.sequences.STEPTREE
        bitmaps   = data.bitmaps.STEPTREE

        sequence_i = self.sequence_index.get() - 1
        if sequence_i not in range(len(sequences)):
            return

        sequence = sequences[sequence_i]
        sprite_i = self.sprite_index.get()
        sprites = sequence.sprites.STEPTREE
        bitmap_index = sequence.first_bitmap_index + sprite_i
        x0, y0, x1, y1 = 0, 0, 1, 1
        rx, ry = 0.5, 0.5
        if sprite_i in range(len(sprites)):
            sprite = sprites[sprite_i]
            if sprite.bitmap_index not in range(len(bitmaps)):
                return

            bitmap_index = sprite.bitmap_index
            x0, y0, x1, y1 = sprite.left_side,  sprite.top_side,\
                             sprite.right_side, sprite.bottom_side
            rx = x0 + sprite.registration_point_x
            ry = y0 + sprite.registration_point_y
        elif bitmap_index not in range(len(bitmaps)):
            return

        if bitmap_index != self.bitmap_index.get():
            self.bitmap_menu.sel_index = bitmap_index

        bitmap = bitmaps[bitmap_index]
        mip = self.mipmap_index.get()
        w, h = max(bitmap.width>>mip, 1), max(bitmap.height>>mip, 1)
        x0, y0 = int(round(x0 * w)), int(round(y0 * h))
        x1, y1 = int(round(x1 * w)), int(round(y1 * h))
        rx, ry = int(round(rx * w)), int(round(ry * h))
        rx0, rx1 = rx - 1, rx + 1
        ry0, ry1 = ry - 1, ry + 1
        if x1 < x0: x0, x1 = x1, x0
        if y1 < y0: y0, y1 = y1, y0
        if ry1 < ry0: ry0, ry1 = ry1, ry0
        x0 -= 1; y0 -= 1
        rx0 -= 1; ry0 -= 1

        self.image_canvas.create_rectangle(
            (x0, y0, x1, y1), fill=None, dash=(2, 1),
            tags=SPRITE_RECTANGLE_TAG,
            outline=self.bitmap_canvas_outline_color)

        self.image_canvas.create_rectangle(
            (rx0, ry0, rx1, ry1), fill=None,
            tags=SPRITE_CENTER_TAG,
            outline=self.bitmap_canvas_bg_color)

    def change_textures(self, textures):
        BitmapDisplayFrame.change_textures(self, textures)
        tag = self.bitmap_tag
        if tag is None: return

        data = tag.data.tagdata
        options = {i+1: str(i) for i in range(len(data.sequences.STEPTREE))}
        options[0] = "None"

        if not (hasattr(self, "sprite_menu") and
                hasattr(self, "sequence_menu")):
            return
        self.sequence_menu.set_options(options)
        self.sequence_menu.sel_index = (self.sequence_menu.max_index >= 0) - 1
Beispiel #14
0
    def __init__(self, *args, **kwargs):
        field_widget.FieldWidget.__init__(self, *args, **kwargs)
        self.u_node_widgets_by_u_index = {}

        if self.f_widget_parent is None:
            self.pack_padx = self.pack_pady = 0

        kwargs.update(relief='flat',
                      bd=0,
                      highlightthickness=0,
                      bg=self.default_bg_color)

        show_frame = bool(
            kwargs.pop('show_frame', not self.blocks_start_hidden))
        if self.is_empty and self.hide_if_blank:
            show_frame = False

        tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs))

        self.show = tk.BooleanVar(self)
        self.show.set(show_frame)

        max_u_index = len(self.desc['CASE_MAP'])
        u_index = getattr(self.node, "u_index", None)
        if u_index is None:
            u_index = max_u_index

        toggle_text = '-' if show_frame else '+'

        self.title = tk.Frame(self, relief='raised')
        self.show_btn = ttk.Checkbutton(self.title,
                                        width=3,
                                        text=toggle_text,
                                        command=self.toggle_visible,
                                        style='ShowButton.TButton')
        self.title_label = tk.Label(self.title,
                                    text=self.gui_name,
                                    anchor='w',
                                    width=self.title_size,
                                    justify='left')
        self.title_label.font_type = "frame_title"
        self.sel_menu = ScrollMenu(self.title,
                                   f_widget_parent=self,
                                   sel_index=u_index,
                                   max_index=max_u_index,
                                   disabled=self.disabled,
                                   callback=self.select_option,
                                   option_getter=self.get_options)

        self.show_btn.pack(side="left")
        self.title_label.pack(side="left", fill="x", expand=True)
        self.sel_menu.pack(side="left", fill="x")
        self.title.pack(fill="x", expand=True)

        self.content = tk.Frame(self, relief="sunken")

        # make the default raw bytes union frame
        self.raw_frame = tk.Frame(self.content, relief="flat", bd=0)
        self.raw_label = tk.Label(self.raw_frame,
                                  text='DataUnion',
                                  width=self.title_size,
                                  anchor='w',
                                  disabledforeground=self.text_disabled_color)
        self.import_btn = ttk.Button(self.raw_frame,
                                     text='Import',
                                     command=self.import_node,
                                     width=7)
        self.export_btn = ttk.Button(self.raw_frame,
                                     text='Export',
                                     command=self.export_node,
                                     width=7)

        self.raw_label.pack(side="left", expand=True, fill='x')
        for w in (self.export_btn, self.import_btn):
            w.pack(side="left", padx=(0, 4), pady=2)

        self.populate()
        self._initialized = True
Beispiel #15
0
class DynamicEnumFrame(EnumFrame):
    options_sane = False

    def __init__(self, *args, **kwargs):
        kwargs.update(relief='flat', bd=0, highlightthickness=0)
        data_frame.DataFrame.__init__(self, *args, **kwargs)

        sel_index = -1 if self.node is None else self.node + 1

        # make the widgets
        self.content = tk.Frame(self, relief='flat', bd=0)
        self.title_label = tk.Label(
            self.content,
            text=self.gui_name,
            justify='left',
            anchor='w',
            width=self.title_size,
            disabledforeground=self.text_disabled_color)
        self.sel_menu = ScrollMenu(self.content,
                                   f_widget_parent=self,
                                   menu_width=self.widget_width,
                                   sel_index=sel_index,
                                   max_index=0,
                                   disabled=self.disabled,
                                   default_text="<INVALID>",
                                   option_getter=self.get_options,
                                   callback=self.select_option)

        self.sel_menu.bind('<FocusIn>', self.flag_sanity_change)
        self.sel_menu.arrow_button.bind('<FocusIn>', self.flag_sanity_change)
        self.sel_menu.bind('<Enter>', self.flag_sanity_change)
        self.sel_menu.arrow_button.bind('<Enter>', self.flag_sanity_change)
        self.sel_menu.options_volatile = 'DYN_NAME_PATH' in self.desc

        if self.gui_name != '':
            self.title_label.pack(side="left", fill="x")
        self.content.pack(fill="x", expand=True)
        self.sel_menu.pack(side="left", fill="x")
        self.populate()
        self._initialized = True

    def edit_apply(self=None, *, edit_state, undo=True):
        state = edit_state
        attr_index = state.attr_index

        w_parent, parent = field_widget.FieldWidget.get_widget_and_node(
            nodepath=state.nodepath, tag_window=state.tag_window)

        if undo:
            parent[attr_index] = state.undo_node
        else:
            parent[attr_index] = state.redo_node

        if w_parent is not None:
            try:
                w = w_parent.f_widgets[w_parent.f_widget_ids_map[attr_index]]
                if w.desc is not state.desc:
                    return

                w.sel_menu.sel_index = parent[attr_index] + 1
                w.needs_flushing = False
                w.sel_menu.update_label()
                w.set_edited()
            except Exception:
                print(format_exc())

    def generate_options(self, opt_index=None):
        desc = self.desc
        options = {0: "-1. NONE"}

        dyn_name_path = desc.get('DYN_NAME_PATH')
        if self.node is None:
            if opt_index is None:
                return options
            return None
        elif not dyn_name_path:
            print("Missing DYN_NAME_PATH path in dynamic enumerator.")
            print(self.parent.get_root().def_id, self.name)
            if opt_index is None:
                return options
            return None

        try:
            p_out, p_in = dyn_name_path.split('[DYN_I]')

            # We are ALWAYS going to go to the parent, so we need to slice
            if p_out.startswith('..'): p_out = p_out.split('.', 1)[-1]
            array = self.parent.get_neighbor(p_out)

            options_to_generate = range(len(array))
            if opt_index is not None:
                options_to_generate = ((opt_index - 1, ) if opt_index -
                                       1 in options_to_generate else ())

            for i in options_to_generate:
                name = array[i].get_neighbor(p_in)
                if isinstance(name, list):
                    name = repr(name).strip("[").strip("]")
                else:
                    name = str(name)

                options[i + 1] = '%s. %s' % (i, name.split('\n')[0])
            option_count = len(array) + 1
        except Exception:
            print(format_exc())
            option_count = 1

        if opt_index is None:
            self.option_cache = options
            self.options_sane = True
            if self.sel_menu is not None:
                self.sel_menu.options_menu_sane = False
                self.sel_menu.max_index = option_count - 1
            return options
        return options.get(opt_index, None)

    def reload(self):
        try:
            self.options_sane = False
            if self.disabled == self.sel_menu.disabled:
                pass
            elif self.disabled:
                self.sel_menu.disable()
            else:
                self.sel_menu.enable()

            self.generate_options()
            if self.node is not None:
                self.sel_menu.sel_index = self.node + 1
            self.sel_menu.update_label()
        except Exception:
            print(format_exc())

    populate = reload

    def select_option(self, opt_index=None):
        if None in (self.parent, self.node):
            return

        # make an edit state
        if self.node != opt_index - 1:
            self.set_edited()
            self.edit_create(undo_node=self.node, redo_node=opt_index - 1)

        self.sel_menu.sel_index = opt_index

        # since the node value is actually signed and can be -1, we'll
        # set entry 0 to be a node value of -1 and all other values
        # are one less than the entry index they are located in.
        self.node = self.parent[self.attr_index] = opt_index - 1
        self.sel_menu.update_label()

    def flag_sanity_change(self, e=None):
        self.options_sane = self.sel_menu.options_menu_sane = (
            not self.sel_menu.options_volatile)

        if not self.options_sane:
            self.option_cache = None
Beispiel #16
0
class BitmapDisplayFrame(BinillaWidget, tk.Frame):
    app_root = None
    root_frame_id = None

    bitmap_index = None  # index of the bitmap being displayed
    mipmap_index = None  # the mip level to display of that bitmap
    channel_index = None  # how to display the bitmap:
    #                       0=RGB or L, 1=A, 2=ARGB or AL, 3=R, 4=G, 5=B
    depth_index = None  # since 3d bitmaps must be viewed in 2d slices,
    #                       this is the depth of the slice to display.
    cube_display_index = None  # mode to display the cubemap in:
    #                            0 == horizontal, 1 == vertical,
    #                            2 == linear strip

    prev_bitmap_index = None
    prev_mipmap_index = None
    prev_channel_index = None
    prev_depth_index = None
    prev_cube_display_index = None
    changing_settings = False

    curr_depth = 0
    depth_canvas = None
    depth_canvas_id = None
    depth_canvas_image_id = None

    default_bitmap_mapping = ((0, ), )

    cubemap_cross_mapping = (
        (-1, 2),
        (1, 4, 0, 5),
        (-1, 3),
    )

    cubemap_strip_mapping = ((0, 1, 2, 3, 4, 5), )

    _image_handlers = None
    image_canvas_ids = ()  # does NOT include the depth_canvas_id
    textures = ()  # List of textures ready to be loaded into arbytmap.
    # Structure is as follows:  [ (tex_block0, tex_info0),
    #                             (tex_block1, tex_info1),
    #                             (tex_block2, tex_info2), ... ]

    temp_root = os.path.join(tempfile.gettempdir(), "arbytmaps")
    temp_dir = ''

    def __init__(self, master, *args, **kwargs):
        BinillaWidget.__init__(self)
        self.temp_root = kwargs.pop('temp_root', self.temp_root)
        textures = kwargs.pop('textures', ())
        app_root = kwargs.pop('app_root', ())

        self.image_canvas_ids = []
        self.textures = []
        self._image_handlers = {}

        temp_name = str(int(random.random() * (1 << 32)))
        self.temp_dir = os.path.join(self.temp_root, temp_name)

        kwargs.update(relief='flat',
                      bd=self.frame_depth,
                      bg=self.default_bg_color)
        tk.Frame.__init__(self, master, *args, **kwargs)

        self.bitmap_index = tk.IntVar(self)
        self.mipmap_index = tk.IntVar(self)
        self.depth_index = tk.IntVar(self)
        self.channel_index = tk.IntVar(self)
        self.cube_display_index = tk.IntVar(self)
        self.root_canvas = tk.Canvas(self, highlightthickness=0)
        self.root_frame = tk.Frame(self.root_canvas, highlightthickness=0)

        # create the root_canvas and the root_frame within the canvas
        self.controls_frame0 = tk.Frame(self.root_frame, highlightthickness=0)
        self.controls_frame1 = tk.Frame(self.root_frame, highlightthickness=0)
        self.controls_frame2 = tk.Frame(self.root_frame, highlightthickness=0)
        self.image_root_frame = tk.Frame(self.root_frame, highlightthickness=0)
        self.image_canvas = tk.Canvas(self.image_root_frame,
                                      highlightthickness=0,
                                      bg=self.bitmap_canvas_bg_color)
        self.depth_canvas = tk.Canvas(self.image_canvas,
                                      highlightthickness=0,
                                      bg=self.bitmap_canvas_bg_color)

        self.bitmap_menu = ScrollMenu(self.controls_frame0,
                                      menu_width=7,
                                      variable=self.bitmap_index,
                                      can_scroll=True)
        self.mipmap_menu = ScrollMenu(self.controls_frame1,
                                      menu_width=7,
                                      variable=self.mipmap_index,
                                      can_scroll=True)
        self.depth_menu = ScrollMenu(self.controls_frame2,
                                     menu_width=7,
                                     variable=self.depth_index,
                                     can_scroll=True)
        self.channel_menu = ScrollMenu(self.controls_frame0,
                                       menu_width=9,
                                       variable=self.channel_index,
                                       can_scroll=True)
        self.cube_display_menu = ScrollMenu(self.controls_frame1,
                                            menu_width=9,
                                            variable=self.cube_display_index,
                                            options=("cross", "linear"),
                                            can_scroll=True)

        self.save_button = ttk.Button(self.controls_frame2,
                                      width=11,
                                      text="Browse",
                                      command=self.save_as)
        self.depth_menu.default_text = self.mipmap_menu.default_text =\
                                       self.bitmap_menu.default_text =\
                                       self.channel_menu.default_text =\
                                       self.cube_display_menu.default_text = ""

        labels = []
        labels.append(tk.Label(self.controls_frame0, text="Bitmap index"))
        labels.append(tk.Label(self.controls_frame1, text="Mipmap level"))
        labels.append(tk.Label(self.controls_frame2, text="Depth level"))
        labels.append(tk.Label(self.controls_frame0, text="Channels"))
        labels.append(tk.Label(self.controls_frame1, text="Cubemap display"))
        labels.append(tk.Label(self.controls_frame2, text="Save to file"))
        for lbl in labels:
            lbl.config(width=15,
                       anchor='w',
                       bg=self.default_bg_color,
                       fg=self.text_normal_color,
                       disabledforeground=self.text_disabled_color)

        self.hsb = tk.Scrollbar(self,
                                orient="horizontal",
                                command=self.root_canvas.xview)
        self.vsb = tk.Scrollbar(self,
                                orient="vertical",
                                command=self.root_canvas.yview)
        self.root_canvas.config(xscrollcommand=self.hsb.set,
                                xscrollincrement=1,
                                yscrollcommand=self.vsb.set,
                                yscrollincrement=1)
        for w in [
                self.root_frame, self.root_canvas, self.image_canvas,
                self.controls_frame0, self.controls_frame1,
                self.controls_frame2
        ] + labels:
            if e_c.IS_LNX:
                w.bind('<Shift-4>', self.mousewheel_scroll_x)
                w.bind('<Shift-5>', self.mousewheel_scroll_x)
                w.bind('<4>', self.mousewheel_scroll_y)
                w.bind('<5>', self.mousewheel_scroll_y)
            else:
                w.bind('<Shift-MouseWheel>', self.mousewheel_scroll_x)
                w.bind('<MouseWheel>', self.mousewheel_scroll_y)

        # pack everything
        # pack in this order so scrollbars aren't shrunk
        self.root_frame_id = self.root_canvas.create_window(
            (0, 0), anchor="nw", window=self.root_frame)
        self.hsb.pack(side='bottom', fill='x', anchor='nw')
        self.vsb.pack(side='right', fill='y', anchor='nw')
        self.root_canvas.pack(fill='both', anchor='nw', expand=True)
        self.controls_frame0.pack(side='top', fill='x', anchor='nw')
        self.controls_frame1.pack(side='top', fill='x', anchor='nw')
        self.controls_frame2.pack(side='top', fill='x', anchor='nw')
        self.image_root_frame.pack(fill='both', anchor='nw', expand=True)
        self.image_canvas.pack(fill='both',
                               side='right',
                               anchor='nw',
                               expand=True)

        padx = self.horizontal_padx
        pady = self.horizontal_pady
        for lbl in labels[:3]:
            lbl.pack(side='left', padx=(25, 0), pady=pady)
        self.bitmap_menu.pack(side='left', padx=padx, pady=pady)
        self.mipmap_menu.pack(side='left', padx=padx, pady=pady)
        self.depth_menu.pack(side='left', padx=padx, pady=pady)
        for lbl in labels[3:]:
            lbl.pack(side='left', padx=(15, 0), pady=pady)
        self.save_button.pack(side='left', padx=padx, pady=pady)
        self.channel_menu.pack(side='left', padx=padx, pady=pady)
        self.cube_display_menu.pack(side='left', padx=padx, pady=pady)

        self.change_textures(textures)

        self.write_trace(self.bitmap_index, self.settings_changed)
        self.write_trace(self.mipmap_index, self.settings_changed)
        self.write_trace(self.depth_index, self.settings_changed)
        self.write_trace(self.cube_display_index, self.settings_changed)
        self.write_trace(self.channel_index, self.settings_changed)

        self.apply_style()

    def destroy(self):
        try:
            self.clear_canvas()
            self.clear_depth_canvas()
        except Exception:
            pass
        try:
            del self.textures[:]
        except Exception:
            pass
        try:
            del self._image_handlers[:]
        except Exception:
            pass
        self.image_canvas_ids = self._image_handlers = None
        self.textures = None
        tk.Frame.destroy(self)
        self.delete_all_traces()
        self.delete_all_widget_refs()
        gc.collect()

    @property
    def active_image_handler(self):
        b = self.bitmap_index.get()
        if b not in range(len(self.textures)) or not import_arbytmap():
            return None
        elif b not in self._image_handlers:
            # make a new PhotoImageHandler if one doesnt exist already
            self._image_handlers[b] = PhotoImageHandler(
                self.textures[b][0], self.textures[b][1], self.temp_dir)

        return self._image_handlers[b]

    @property
    def should_update(self):
        return (self.prev_bitmap_index != self.bitmap_index.get()
                or self.prev_mipmap_index != self.mipmap_index.get()
                or self.prev_channel_index != self.channel_index.get())

    def mousewheel_scroll_x(self, e):
        # prevent scrolling if the root_canvas.bbox width >= canvas width
        bbox = self.root_canvas.bbox(tk.ALL)
        if not bbox or (self.root_canvas.winfo_width() >= bbox[2] - bbox[0]):
            return

        delta = getattr(self.app_root, "scroll_increment_x", 20)
        self.root_canvas.xview_scroll(int(get_mouse_delta(e) * delta), "units")

    def mousewheel_scroll_y(self, e):
        # prevent scrolling if the root_canvas.bbox height >= canvas height
        bbox = self.root_canvas.bbox(tk.ALL)
        if not bbox or (self.root_canvas.winfo_height() >= bbox[3] - bbox[1]):
            return

        delta = getattr(self.app_root, "scroll_increment_y", 20)
        self.root_canvas.yview_scroll(int(get_mouse_delta(e) * delta), "units")

    def update_scroll_regions(self):
        if not self.image_canvas_ids and not self.depth_canvas_id:
            return
        rf = self.root_frame
        region = self.image_canvas.bbox(tk.ALL)
        # bbox region isn't actually x, y, w, h, but if the
        # origin is at (0,0) we can treat it like that
        if region is None:
            x, y, w, h = (0, 0, 0, 0)
        else:
            x, y, w, h = region

        self.image_canvas.config(scrollregion=(x, y, w, h))
        rf.update_idletasks()
        max_w = w
        total_h = h
        for widget in self.root_frame.children.values():
            if widget is not self.image_root_frame:
                max_w = max(widget.winfo_reqwidth(), max_w)
                total_h += widget.winfo_reqheight()

        self.root_canvas.itemconfigure(self.root_frame_id,
                                       width=max_w,
                                       height=total_h)
        self.root_canvas.config(scrollregion=(0, 0, max_w, total_h))

    def save_as(self, e=None, initial_dir=None):
        handler = self.active_image_handler
        if handler is None:
            return None
        fp = asksaveasfilename(initialdir=initial_dir,
                               defaultextension='.dds',
                               title="Save bitmap as...",
                               parent=self,
                               filetypes=(("DirectDraw surface", "*.dds"),
                                          ('Portable network graphics',
                                           '*.png'),
                                          ('Truevision graphics adapter',
                                           '*.tga'), ('Raw pixel data',
                                                      '*.bin')))

        fp, ext = os.path.splitext(fp)
        if not fp:
            return
        elif not ext:
            ext = ".dds"

        mip_levels = "all" if ext.lower() == ".dds" else self.mipmap_index.get(
        )

        handler.arby.save_to_file(output_path=fp,
                                  ext=ext,
                                  overwrite=True,
                                  mip_levels=mip_levels,
                                  bitmap_indexes="all",
                                  keep_alpha=handler.channels.get("A"),
                                  swizzle_mode=False,
                                  channel_mapping=handler.channel_mapping,
                                  target_big_endian=False,
                                  tile_mode=False)

    def clear_depth_canvas(self):
        self.depth_canvas.delete(tk.ALL)
        self.prev_depth_index = None
        self.prev_cube_display_index = None
        self.depth_canvas_image_id = None

    def clear_canvas(self):
        self.image_canvas.delete(tk.ALL)
        self.clear_depth_canvas()
        self.depth_canvas_id = None
        self.image_canvas_ids = []

    def hide_depth_canvas(self):
        if self.depth_canvas_id is not None:
            self.image_canvas.delete(self.depth_canvas_id)
            self.depth_canvas_id = None

    def show_depth_canvas(self):
        self.depth_canvas_id = self.image_canvas.create_window(
            (0, 0), anchor="nw", window=self.depth_canvas)

    def change_textures(self, textures):
        assert hasattr(textures, '__iter__')

        for tex in textures:
            assert len(tex) == 2
            assert isinstance(tex[1], dict)

        if self._image_handlers: del self._image_handlers
        if self.textures: del self.textures[:]

        self.textures = list(textures)

        self._image_handlers = {}
        self.bitmap_index.set(0)
        self.mipmap_index.set(0)
        self.channel_index.set(0)
        self.depth_index.set(0)
        self.cube_display_index.set(0)

        self.bitmap_menu.set_options(range(len(textures)))

        self.prev_bitmap_index = None
        self.prev_mipmap_index = None
        self.prev_channel_index = None
        self.prev_depth_index = None
        self.prev_cube_display_index = None
        self.update_bitmap(force=True)

    def get_images(self):
        image_handler = self.active_image_handler
        if not image_handler: return
        images = image_handler.get_images(mip_levels=self.mipmap_index.get())
        return tuple(images[i] for i in sorted(images.keys()))

    def settings_changed(self, *args, force=False):
        handler = self.active_image_handler
        force = False
        if not handler:
            return
        elif self.changing_settings:
            return
        elif self.prev_bitmap_index != self.bitmap_index.get():
            force = True
        elif self.prev_mipmap_index != self.mipmap_index.get():
            force = True
        elif self.prev_channel_index != self.channel_index.get():
            force = True
        elif self.prev_depth_index != self.depth_index.get():
            pass
        elif self.prev_cube_display_index != self.cube_display_index.get():
            force = True
        else:
            return
        self.changing_settings = True

        new_mip_options = range(handler.max_mipmap + 1)
        if self.mipmap_index.get() not in new_mip_options:
            self.mipmap_index.set(0)

        max_depth = handler.mip_depth(self.mipmap_index.get())
        self.mipmap_menu.set_options(new_mip_options)
        self.depth_menu.set_options(range(max_depth))
        if self.depth_menu.sel_index > max_depth - 1:
            self.depth_menu.sel_index = max_depth - 1

        channel_count = handler.channel_count
        if channel_count <= 2:
            opts = ("Luminance", "Alpha", "AL")
        else:
            opts = ("RGB", "Alpha", "ARGB", "Red", "Green", "Blue")
        self.channel_menu.set_options(opts)

        try:
            handler.set_channel_mode(self.channel_index.get())
            self.update_bitmap(force=force)
            self.changing_settings = False
        except Exception:
            self.changing_settings = False
            raise

    def update_bitmap(self, *args, force=False):
        handler = self.active_image_handler
        if handler is None:
            return None

        tex_type = handler.tex_type
        if tex_type == "2D": self._display_2d_bitmap(force)
        elif tex_type == "3D": self._display_3d_bitmap(force)
        elif tex_type == "CUBE": self._display_cubemap(force)

        self.prev_bitmap_index = self.bitmap_index.get()
        self.prev_mipmap_index = self.mipmap_index.get()
        self.prev_channel_index = self.channel_index.get()
        self.prev_depth_index = self.depth_index.get()
        self.prev_cube_display_index = self.cube_display_index.get()

    def _display_cubemap(self, force=False, bitmap_mapping=None):
        mapping_type = self.cube_display_index.get()
        if bitmap_mapping is None:
            if mapping_type == 0:
                bitmap_mapping = self.cubemap_cross_mapping
            else:
                bitmap_mapping = self.cubemap_strip_mapping

        self._display_2d_bitmap(force, bitmap_mapping)

    def _display_2d_bitmap(self, force=False, bitmap_mapping=None):
        images = self.get_images()
        if not images or not (self.should_update or force): return
        w = max(image.width() for image in images)
        h = max(image.height() for image in images)
        if bitmap_mapping is None:
            bitmap_mapping = self.default_bitmap_mapping

        self.clear_canvas()
        # place the bitmaps on the canvas
        y = 0
        for line in bitmap_mapping:
            max_column_ct = max(0, len(line))
            x = 0
            for image_index in line:
                if image_index in range(len(images)):
                    # place the cube face on the canvas
                    self.image_canvas_ids.append(
                        self.image_canvas.create_image(
                            (x, y),
                            anchor="nw",
                            image=images[image_index],
                            tags=("BITMAP", "2D_BITMAP")))
                x += w
            y += h
        self.update_scroll_regions()

    def _display_3d_bitmap(self, force=False):
        if self.should_update or self.depth_canvas_image_id is None or force:
            self.clear_canvas()
            self.show_depth_canvas()
            handler = self.active_image_handler
            images = self.get_images()
            if not (images and handler): return
            m = self.mipmap_index.get()
            w, h = images[0].width(), handler.mip_height(m)
            self.curr_depth = handler.mip_depth(m)

            # place the bitmap on the canvas
            self.depth_canvas_image_id = self.depth_canvas.create_image(
                (0, 0),
                anchor="nw",
                image=images[0],
                tags=("BITMAP", "3D_BITMAP"))
            self.image_canvas.itemconfig(self.depth_canvas_id,
                                         width=w,
                                         height=h)
            self.depth_canvas.config(scrollregion="0 0 %s %s" % (w, h))
            self.update_scroll_regions()

        self.depth_canvas.coords(
            self.depth_canvas_image_id,
            (0, -self.depth_index.get() * self.curr_depth))
Beispiel #17
0
    def __init__(self, master, *args, **kwargs):
        BinillaWidget.__init__(self)
        self.temp_root = kwargs.pop('temp_root', self.temp_root)
        textures = kwargs.pop('textures', ())
        app_root = kwargs.pop('app_root', ())

        self.image_canvas_ids = []
        self.textures = []
        self._image_handlers = {}

        temp_name = str(int(random.random() * (1 << 32)))
        self.temp_dir = os.path.join(self.temp_root, temp_name)

        kwargs.update(relief='flat',
                      bd=self.frame_depth,
                      bg=self.default_bg_color)
        tk.Frame.__init__(self, master, *args, **kwargs)

        self.bitmap_index = tk.IntVar(self)
        self.mipmap_index = tk.IntVar(self)
        self.depth_index = tk.IntVar(self)
        self.channel_index = tk.IntVar(self)
        self.cube_display_index = tk.IntVar(self)
        self.root_canvas = tk.Canvas(self, highlightthickness=0)
        self.root_frame = tk.Frame(self.root_canvas, highlightthickness=0)

        # create the root_canvas and the root_frame within the canvas
        self.controls_frame0 = tk.Frame(self.root_frame, highlightthickness=0)
        self.controls_frame1 = tk.Frame(self.root_frame, highlightthickness=0)
        self.controls_frame2 = tk.Frame(self.root_frame, highlightthickness=0)
        self.image_root_frame = tk.Frame(self.root_frame, highlightthickness=0)
        self.image_canvas = tk.Canvas(self.image_root_frame,
                                      highlightthickness=0,
                                      bg=self.bitmap_canvas_bg_color)
        self.depth_canvas = tk.Canvas(self.image_canvas,
                                      highlightthickness=0,
                                      bg=self.bitmap_canvas_bg_color)

        self.bitmap_menu = ScrollMenu(self.controls_frame0,
                                      menu_width=7,
                                      variable=self.bitmap_index,
                                      can_scroll=True)
        self.mipmap_menu = ScrollMenu(self.controls_frame1,
                                      menu_width=7,
                                      variable=self.mipmap_index,
                                      can_scroll=True)
        self.depth_menu = ScrollMenu(self.controls_frame2,
                                     menu_width=7,
                                     variable=self.depth_index,
                                     can_scroll=True)
        self.channel_menu = ScrollMenu(self.controls_frame0,
                                       menu_width=9,
                                       variable=self.channel_index,
                                       can_scroll=True)
        self.cube_display_menu = ScrollMenu(self.controls_frame1,
                                            menu_width=9,
                                            variable=self.cube_display_index,
                                            options=("cross", "linear"),
                                            can_scroll=True)

        self.save_button = ttk.Button(self.controls_frame2,
                                      width=11,
                                      text="Browse",
                                      command=self.save_as)
        self.depth_menu.default_text = self.mipmap_menu.default_text =\
                                       self.bitmap_menu.default_text =\
                                       self.channel_menu.default_text =\
                                       self.cube_display_menu.default_text = ""

        labels = []
        labels.append(tk.Label(self.controls_frame0, text="Bitmap index"))
        labels.append(tk.Label(self.controls_frame1, text="Mipmap level"))
        labels.append(tk.Label(self.controls_frame2, text="Depth level"))
        labels.append(tk.Label(self.controls_frame0, text="Channels"))
        labels.append(tk.Label(self.controls_frame1, text="Cubemap display"))
        labels.append(tk.Label(self.controls_frame2, text="Save to file"))
        for lbl in labels:
            lbl.config(width=15,
                       anchor='w',
                       bg=self.default_bg_color,
                       fg=self.text_normal_color,
                       disabledforeground=self.text_disabled_color)

        self.hsb = tk.Scrollbar(self,
                                orient="horizontal",
                                command=self.root_canvas.xview)
        self.vsb = tk.Scrollbar(self,
                                orient="vertical",
                                command=self.root_canvas.yview)
        self.root_canvas.config(xscrollcommand=self.hsb.set,
                                xscrollincrement=1,
                                yscrollcommand=self.vsb.set,
                                yscrollincrement=1)
        for w in [
                self.root_frame, self.root_canvas, self.image_canvas,
                self.controls_frame0, self.controls_frame1,
                self.controls_frame2
        ] + labels:
            if e_c.IS_LNX:
                w.bind('<Shift-4>', self.mousewheel_scroll_x)
                w.bind('<Shift-5>', self.mousewheel_scroll_x)
                w.bind('<4>', self.mousewheel_scroll_y)
                w.bind('<5>', self.mousewheel_scroll_y)
            else:
                w.bind('<Shift-MouseWheel>', self.mousewheel_scroll_x)
                w.bind('<MouseWheel>', self.mousewheel_scroll_y)

        # pack everything
        # pack in this order so scrollbars aren't shrunk
        self.root_frame_id = self.root_canvas.create_window(
            (0, 0), anchor="nw", window=self.root_frame)
        self.hsb.pack(side='bottom', fill='x', anchor='nw')
        self.vsb.pack(side='right', fill='y', anchor='nw')
        self.root_canvas.pack(fill='both', anchor='nw', expand=True)
        self.controls_frame0.pack(side='top', fill='x', anchor='nw')
        self.controls_frame1.pack(side='top', fill='x', anchor='nw')
        self.controls_frame2.pack(side='top', fill='x', anchor='nw')
        self.image_root_frame.pack(fill='both', anchor='nw', expand=True)
        self.image_canvas.pack(fill='both',
                               side='right',
                               anchor='nw',
                               expand=True)

        padx = self.horizontal_padx
        pady = self.horizontal_pady
        for lbl in labels[:3]:
            lbl.pack(side='left', padx=(25, 0), pady=pady)
        self.bitmap_menu.pack(side='left', padx=padx, pady=pady)
        self.mipmap_menu.pack(side='left', padx=padx, pady=pady)
        self.depth_menu.pack(side='left', padx=padx, pady=pady)
        for lbl in labels[3:]:
            lbl.pack(side='left', padx=(15, 0), pady=pady)
        self.save_button.pack(side='left', padx=padx, pady=pady)
        self.channel_menu.pack(side='left', padx=padx, pady=pady)
        self.cube_display_menu.pack(side='left', padx=padx, pady=pady)

        self.change_textures(textures)

        self.write_trace(self.bitmap_index, self.settings_changed)
        self.write_trace(self.mipmap_index, self.settings_changed)
        self.write_trace(self.depth_index, self.settings_changed)
        self.write_trace(self.cube_display_index, self.settings_changed)
        self.write_trace(self.channel_index, self.settings_changed)

        self.apply_style()
Beispiel #18
0
    def __init__(self, *args, **kwargs):
        self.settings = settings = kwargs.pop('settings', {})
        BinillaWidget.__init__(self, *args, **kwargs)
        tk.Toplevel.__init__(self, *args, **kwargs)
        try:
            self.iconbitmap(e_c.REFINERY_ICON_PATH)
        except Exception:
            if not e_c.IS_LNX:
                print("Could not load window icon.")

        self.geometry("550x350")
        self.minsize(width=450, height=350)
        self.resizable(1, 1)
        self.title("Settings")

        self.tabs = ttk.Notebook(self)
        self.dirs_frame         = tk.Frame(self.tabs)
        self.extract_frame      = tk.Frame(self.tabs)
        self.data_extract_frame = tk.Frame(self.tabs)
        self.tag_fixup_frame    = tk.Frame(self.tabs)
        self.deprotect_frame    = tk.Frame(self.tabs)
        self.heuristics_frame   = tk.Frame(self.tabs)
        self.fonts_frame        = tk.Frame(self.tabs)
        self.other_frame        = tk.Frame(self.tabs)

        self.tabs.add(self.dirs_frame, text="Directories")
        self.tabs.add(self.extract_frame, text="Extraction")
        self.tabs.add(self.data_extract_frame, text="Data extraction")
        self.tabs.add(self.tag_fixup_frame, text="Tag fixup")
        self.tabs.add(self.deprotect_frame, text="Deprotection")
        self.tabs.add(self.heuristics_frame, text="Heuristics")
        self.tabs.add(self.fonts_frame, text="Fonts")
        self.tabs.add(self.other_frame, text="Other")

        font_families = tuple(sorted(tkinter.font.families()))

        self.tags_dir_frame  = tk.LabelFrame(
            self.dirs_frame, text="Default tags extraction folder")
        self.data_dir_frame  = tk.LabelFrame(
            self.dirs_frame, text="Default data extraction folder")
        self.tags_list_frame = tk.LabelFrame(
            self.dirs_frame, text="Tags list log (erase to disable logging)")

        for attr in ("overwrite", "recursive",
                     "rename_scnr_dups", "decode_adpcm",
                     "bitmap_extract_keep_alpha",
                     "generate_comp_verts", "generate_uncomp_verts",
                     "force_lower_case_paths", "fix_tag_classes",
                     "autoload_resources", "extract_yelo_cheape",
                     "use_minimum_priorities", "use_heuristics",
                     "rename_cached_tags", "show_all_fields",
                     "show_structure_meta",
                     "edit_all_fields", "allow_corrupt",
                     "valid_tag_paths_are_accurate", "limit_tag_path_lengths",
                     "scrape_tag_paths_from_scripts", "shallow_ui_widget_nesting",
                     "fix_tag_index_offset", "use_tag_index_for_script_names",
                     "do_printout", "print_heuristic_name_changes",
                     "use_scenario_names_for_script_names",
                     "skip_seen_tags_during_queue_processing",
                     "disable_safe_mode", "disable_tag_cleaning",):
            object.__setattr__(self, attr, settings.get(attr, tk.IntVar(self)))

        for attr in ("bitmap_extract_format", "globals_overwrite_mode",
                     "tags_dir", "data_dir", "tagslist_path"):
            object.__setattr__(self, attr, settings.get(attr, tk.StringVar(self)))


        # tags directory
        self.tags_dir_entry = tk.Entry(
            self.tags_dir_frame, state='disabled',
            textvariable=self.tags_dir)
        self.tags_dir_browse_button = tk.Button(
            self.tags_dir_frame, text="Browse",
            command=self.tags_dir_browse, width=6)

        # data directory
        self.data_dir_entry = tk.Entry(
            self.data_dir_frame, state='disabled',
            textvariable=self.data_dir)
        self.data_dir_browse_button = tk.Button(
            self.data_dir_frame, text="Browse",
            command=self.data_dir_browse, width=6)

        # tags list
        self.tags_list_entry = tk.Entry(
            self.tags_list_frame, textvariable=self.tagslist_path)
        self.browse_tags_list_button = tk.Button(
            self.tags_list_frame, text="Browse",
            command=self.tags_list_browse, width=6)


        self.rename_scnr_dups_cbtn = tk.Checkbutton(
            self.tag_fixup_frame, text=(
                "Rename duplicate camera points, cutscene\n"+
                "flags, and recorded animations in scenario"),
            variable=self.rename_scnr_dups, justify="left")
        self.generate_comp_verts_cbtn = tk.Checkbutton(
            self.tag_fixup_frame, text="Generate compressed lightmap vertices",
            variable=self.generate_comp_verts)
        self.generate_uncomp_verts_cbtn = tk.Checkbutton(
            self.tag_fixup_frame, text="Generate uncompressed lightmap vertices",
            variable=self.generate_uncomp_verts)

        self.dont_touch_frame = tk.LabelFrame(
            self.tag_fixup_frame,
            text="ONLY CHECK THESE IF YOU ARE NOT DEALING WITH PROTECTED MAPS")
        self.disable_safe_mode_cbtn = tk.Checkbutton(
            self.dont_touch_frame, variable=self.disable_safe_mode, justify="left",
            text="Disable safe-mode")
        self.disable_tag_cleaning_cbtn = tk.Checkbutton(
            self.dont_touch_frame, variable=self.disable_tag_cleaning, justify="left",
            text="Disable cleaning errors from tags when reading them.")


        self.overwrite_cbtn = tk.Checkbutton(
            self.extract_frame, text="Overwrite files(not recommended)",
            variable=self.overwrite)
        self.recursive_cbtn = tk.Checkbutton(
            self.extract_frame, text="Recursive extraction",
            variable=self.recursive)
        self.do_printout_cbtn = tk.Checkbutton(
            self.extract_frame, text="Print extracted file names",
            variable=self.do_printout)
        self.force_lower_case_paths_cbtn = tk.Checkbutton(
            self.extract_frame, text="Force all tag paths to lowercase",
            variable=self.force_lower_case_paths)
        self.skip_seen_tags_during_queue_processing_cbtn = tk.Checkbutton(
            self.extract_frame, text="During processing, skip any tags that were already extracted",
            variable=self.skip_seen_tags_during_queue_processing)
        self.globals_overwrite_mode_frame = tk.LabelFrame(
            self.extract_frame, relief="flat",
            text="When to overwrite an existing globals.globals")

        sel_index = self.globals_overwrite_mode.get()
        if sel_index not in range(len(globals_overwrite_gui_names)):
            sel_index = 0

        self.globals_overwrite_mode_menu = ScrollMenu(
            self.globals_overwrite_mode_frame, sel_index=sel_index,
            variable=self.globals_overwrite_mode, menu_width=50,
            options=globals_overwrite_gui_names)


        self.decode_adpcm_cbtn = tk.Checkbutton(
            self.data_extract_frame, variable=self.decode_adpcm,
            text="Decode Xbox audio")
        self.bitmap_extract_frame = tk.LabelFrame(
            self.data_extract_frame, relief="flat",
            text="Bitmap extraction format")
        self.bitmap_extract_keep_alpha_cbtn = tk.Checkbutton(
            self.bitmap_extract_frame, variable=self.bitmap_extract_keep_alpha,
            text="Preserve alpha when extracting to PNG")
        self.use_tag_index_for_script_names_cbtn = tk.Checkbutton(
            self.data_extract_frame, variable=self.use_tag_index_for_script_names,
            text=("When extracting scripts, redirect tag references to\n"
                  "what the tag is currently named(guarantees scripts\n"
                  "point to a valid tag, even if you rename them)"),
            justify="left")
        self.use_scenario_names_for_script_names_cbtn = tk.Checkbutton(
            self.data_extract_frame, variable=self.use_scenario_names_for_script_names,
            text=("When extracting scripts, extract names for encounters,\n"
                  "command lists, scripts, cutscene titles/camera points/flags,\n"
                  "trigger volumes, recorded animations, ai conversations,\n"
                  "object names, device groups, and player starting profiles\n"
                  "from the scenarios reflexives, rather than script strings."),
            justify="left")

        try:
            sel_index = bitmap_file_formats.index(self.bitmap_extract_format.get())
        except Exception:
            sel_index = 0

        self.bitmap_extract_format_menu = ScrollMenu(
            self.bitmap_extract_frame, str_variable=self.bitmap_extract_format,
            menu_width=10, options=bitmap_file_formats, sel_index=sel_index)


        self.fix_tag_classes_cbtn = tk.Checkbutton(
            self.deprotect_frame, text="Fix tag classes",
            variable=self.fix_tag_classes)
        self.use_heuristics_cbtn = tk.Checkbutton(
            self.deprotect_frame, text="Use heuristic deprotection methods",
            variable=self.use_heuristics)
        self.scrape_tag_paths_from_scripts_cbtn = tk.Checkbutton(
            self.deprotect_frame, text="Scrape tag paths from scenario scripts",
            variable=self.scrape_tag_paths_from_scripts)
        self.rename_cached_tags_cbtn = tk.Checkbutton(
            self.deprotect_frame, text=("Rename cached tags using tag paths in\n"
                                        "bitmaps/loc/sounds resource maps"),
            variable=self.rename_cached_tags, justify="left")
        self.limit_tag_path_lengths_cbtn = tk.Checkbutton(
            self.deprotect_frame, text="Limit tag paths to 254 characters (tool.exe limitation)",
            variable=self.limit_tag_path_lengths)
        self.fix_tag_index_offset_cbtn = tk.Checkbutton(
            self.deprotect_frame, text=("Fix tag index offset when saving\n"
                                        "WARNING: Can corrupt certain maps"),
            variable=self.fix_tag_index_offset, justify='left')


        self.valid_tag_paths_are_accurate_cbtn = tk.Checkbutton(
            self.heuristics_frame, text="Do not rename non-protected tag paths",
            variable=self.valid_tag_paths_are_accurate)
        self.shallow_ui_widget_nesting_cbtn = tk.Checkbutton(
            self.heuristics_frame, text="Use shallow ui_widget_definition nesting",
            variable=self.shallow_ui_widget_nesting)
        self.use_fast_heuristics_cbtn = tk.Checkbutton(
            self.heuristics_frame, text="Use fast heuristics",
            variable=self.use_minimum_priorities)
        self.print_heuristic_progress_cbtn = tk.Checkbutton(
            self.heuristics_frame, text=("Print heuristic tag path changes"),
            variable=self.print_heuristic_name_changes, justify='left')


        font_frame_widgets = {}
        for font_type in sorted(self.font_settings):
            if font_type not in ("default", "treeview", "console", "heading",
                                 "heading_small", "frame_title",):
                continue

            if font_type == "console":
                font_type_name_text = "Map info"
            elif font_type == "treeview":
                font_type_name_text = "Map contents / Extraction queue"
            elif font_type == "heading_small":
                font_type_name_text = "Map contents columns / Settings tabs"
            else:
                font_type_name_text = font_type.replace("_", " ").capitalize()

            font_frame = tk.LabelFrame(
                self.fonts_frame, text=font_type_name_text)

            font_info = self.font_settings[font_type]
            try:
                sel_index = font_families.index(font_info.family)
            except Exception:
                sel_index = -1

            family_var = tk.StringVar(self)
            size_var = tk.StringVar(self, str(font_info.size))
            weight_var = tk.IntVar(self, int(font_info.weight == "bold"))
            self.write_trace(family_var, lambda *a, v=family_var, t=font_type:
                             self.update_font_family(v, t))
            self.write_trace(size_var, lambda *a, v=size_var, t=font_type:
                             self.update_font_size(v, t))
            self.write_trace(weight_var, lambda *a, v=weight_var, t=font_type:
                             self.update_font_weight(v, t))

            family_label = tk.Label(font_frame, text="Family ")
            family_menu = ScrollMenu(
                font_frame, str_variable=family_var, menu_width=20,
                options=font_families, sel_index=sel_index)
            weight_btn = tk.Checkbutton(font_frame, text="bold", variable=weight_var)
            size_label = tk.Label(font_frame, text="Size ")
            size_menu = tk.Spinbox(
                font_frame, width=3, state="readonly",
                textvariable=size_var, from_=0, to=200)

            font_frame_widgets[font_type_name_text] = (
                font_frame, (family_label, family_menu, weight_btn,
                             size_label, size_menu))

        self.apply_fonts_btn = tk.Button(
            self.fonts_frame, text="Apply changes", command=self.apply_fonts)


        self.autoload_resources_cbtn = tk.Checkbutton(
            self.other_frame, text=("Load resource maps automatically\n" +
                                    "when loading a non-resource map"),
            variable=self.autoload_resources, justify="left")
        self.extract_yelo_cheape_cbtn = tk.Checkbutton(
            self.other_frame, variable=self.extract_yelo_cheape,
            text="Extract cheape.map when extracting from yelo maps")
        self.show_all_fields_cbtn = tk.Checkbutton(
            self.other_frame, variable=self.show_all_fields,
            text="Show hidden fields when viewing metadata")
        self.show_structure_meta_cbtn = tk.Checkbutton(
            self.other_frame, variable=self.show_structure_meta,
            text="Show hidden meta structure fields when viewing metadata")
        self.edit_all_fields_cbtn = tk.Checkbutton(
            self.other_frame, variable=self.edit_all_fields,
            text="Allow editing all fields when viewing metadata")
        self.allow_corrupt_cbtn = tk.Checkbutton(
            self.other_frame, variable=self.allow_corrupt,
            text="Allow previewing corrupt tags")


        # pack everything
        self.tabs.pack(fill="both", expand=True)
        for w in (self.tags_dir_frame, self.data_dir_frame,
                  self.tags_list_frame):
            w.pack(padx=4, pady=2, fill="x")

        for w in (self.overwrite_cbtn, self.recursive_cbtn,
                  self.do_printout_cbtn, self.force_lower_case_paths_cbtn,
                  self.skip_seen_tags_during_queue_processing_cbtn,
                  self.globals_overwrite_mode_frame):
            w.pack(padx=4, anchor='w')

        for w in (self.bitmap_extract_keep_alpha_cbtn,
                  self.bitmap_extract_format_menu, self.globals_overwrite_mode_menu):
            w.pack(padx=16, anchor='w')

        for w in (self.decode_adpcm_cbtn, self.bitmap_extract_frame,
                  self.use_tag_index_for_script_names_cbtn,
                  self.use_scenario_names_for_script_names_cbtn):
            w.pack(padx=4, anchor='w')

        for w in (self.rename_scnr_dups_cbtn,
                  self.generate_uncomp_verts_cbtn, self.generate_comp_verts_cbtn):
            w.pack(padx=4, anchor='w')

        self.dont_touch_frame.pack(padx=4, anchor='w', expand=True, fill="both")
        for w in (self.disable_safe_mode_cbtn, self.disable_tag_cleaning_cbtn):
            w.pack(padx=4, anchor='w')

        for w in (self.fix_tag_classes_cbtn, self.use_heuristics_cbtn,
                  self.fix_tag_index_offset_cbtn, self.rename_cached_tags_cbtn,
                  self.limit_tag_path_lengths_cbtn,
                  self.scrape_tag_paths_from_scripts_cbtn,
                  ):
            w.pack(padx=4, anchor='w')

        for w in (self.print_heuristic_progress_cbtn,
                  self.valid_tag_paths_are_accurate_cbtn,
                  self.shallow_ui_widget_nesting_cbtn,
                  self.use_fast_heuristics_cbtn,
                  ):
            w.pack(padx=4, anchor='w')

        for w in (self.autoload_resources_cbtn, self.extract_yelo_cheape_cbtn,
                  self.show_all_fields_cbtn, self.show_structure_meta_cbtn,
                  self.edit_all_fields_cbtn, self.allow_corrupt_cbtn,
                  ):
            w.pack(padx=4, anchor='w')

        for k in sorted(font_frame_widgets):
            font_frame, font_widgets = font_frame_widgets[k]
            font_frame.pack(padx=4, anchor='w', expand=True, fill="x")
            for w in font_widgets:
                w.pack(padx=(0, 4), pady=2, side='left', fill='both',
                       expand=isinstance(w, ScrollMenu))

        self.apply_fonts_btn.pack(padx=4, anchor='w', expand=True, fill="both")

        for w1, w2 in ((self.tags_dir_entry, self.tags_dir_browse_button),
                       (self.data_dir_entry, self.data_dir_browse_button),
                       (self.tags_list_entry, self.browse_tags_list_button)):
            w1.pack(padx=(4, 0), pady=2, side='left', expand=True, fill='x')
            w2.pack(padx=(0, 4), pady=2, side='left')

        # make the window not show up on the start bar
        self.transient(self.master)
        self.apply_style()
Beispiel #19
0
class EnumFrame(data_frame.DataFrame):
    '''Used for enumerator nodes. When clicked, creates
    a dropdown box of all available enumerator options.'''

    option_cache = None
    sel_menu = None

    def __init__(self, *args, **kwargs):
        kwargs.update(relief='flat', bd=0, highlightthickness=0)
        data_frame.DataFrame.__init__(self, *args, **kwargs)

        try:
            sel_index = self.node.get_index()
        except Exception:
            sel_index = -1

        # make the widgets
        self.content = tk.Frame(self, relief='flat', bd=0)

        self.display_comment()

        self.title_label = tk.Label(
            self.content,
            text=self.gui_name,
            justify='left',
            anchor='w',
            width=self.title_size,
            disabledforeground=self.text_disabled_color)
        self.sel_menu = ScrollMenu(self.content,
                                   f_widget_parent=self,
                                   menu_width=self.widget_width,
                                   sel_index=sel_index,
                                   max_index=self.desc.get('ENTRIES', 0) - 1,
                                   disabled=self.disabled,
                                   default_text="<INVALID>",
                                   option_getter=self.get_options,
                                   callback=self.select_option)

        if self.gui_name != '':
            self.title_label.pack(side="left", fill="x")
        self.content.pack(fill="x", expand=True)
        self.sel_menu.pack(side="left", fill="x")
        self.populate()
        self.pose_fields()
        self._initialized = True

    @property
    def widget_width(self):
        desc = self.desc
        width = desc.get('WIDGET_WIDTH', self.enum_menu_width)
        if width <= 0:
            for s in self.get_options().values():
                width = max(width, len(s))

        return width

    def apply_style(self, seen=None):
        self.sel_menu.menu_width = self.widget_width
        data_frame.DataFrame.apply_style(self, seen)

    def unload_node_data(self):
        field_widget.FieldWidget.unload_node_data(self)
        self.sel_menu.update_label(" ")

    def set_disabled(self, disable=True):
        disable = disable or not self.editable
        if self.node is None and not disable:
            return

        if bool(disable) != self.disabled and getattr(self, "sel_menu", None):
            self.sel_menu.set_disabled(disable)

        data_frame.DataFrame.set_disabled(self, disable)

    def flush(self):
        pass

    def edit_apply(self=None, *, edit_state, undo=True):
        state = edit_state
        w, node = field_widget.FieldWidget.get_widget_and_node(
            nodepath=state.nodepath, tag_window=state.tag_window)

        if undo:
            node.data = state.undo_node
        else:
            node.data = state.redo_node

        if w is not None:
            try:
                if w.desc is not state.desc:
                    return
                try:
                    w.sel_menu.sel_index = node.get_index()
                except Exception:
                    # option doesnt exist, so make the displayed one blank
                    w.sel_menu.sel_index = -1

                w.needs_flushing = False
                w.sel_menu.update_label()
                w.set_edited()
            except Exception:
                print(format_exc())

    def edit_create(self, **kwargs):
        # add own stuff
        kwargs.update(sel_index=self.sel_menu.sel_index,
                      max_index=self.sel_menu.max_index)
        field_widget.FieldWidget.edit_create(self, **kwargs)

    def get_options(self, opt_index=None):
        '''
        Returns a list of the option strings sorted by option index.
        '''
        if self.option_cache is None or opt_index is not None:
            return self.generate_options(opt_index)

        if opt_index is None:
            return self.option_cache
        elif opt_index == e_c.ACTIVE_ENUM_NAME:
            opt_index = self.sel_menu.sel_index

        return self.option_cache.get(opt_index, None)

    def generate_options(self, opt_index=None):
        desc = self.desc
        options = {}
        option_count = desc.get('ENTRIES', 0)
        options_to_generate = range(option_count)
        if opt_index is not None:
            options_to_generate = ((opt_index, )
                                   if opt_index in options_to_generate else ())

        # sort the options by value(values are integers)
        use_gui_names = self.use_gui_names
        for i in options_to_generate:
            opt = desc[i]
            if use_gui_names and 'GUI_NAME' in opt:
                options[i] = opt['GUI_NAME']
            else:
                options[i] = opt.get('NAME', '<UNNAMED %s>' % i)\
                             .replace('_', ' ')

        if opt_index is None:
            self.options_sane = True
            self.option_cache = options
            if self.sel_menu is not None:
                self.sel_menu.options_menu_sane = False
                self.sel_menu.max_index = option_count - 1
            return options
        return options.get(opt_index, None)

    def reload(self):
        try:
            if self.disabled == self.sel_menu.disabled:
                pass
            elif self.disabled:
                self.sel_menu.disable()
            else:
                self.sel_menu.enable()

            for w in (self, self.content, self.sel_menu, self.title_label):
                w.tooltip_string = self.desc.get('TOOLTIP')

            options = self.get_options()
            option_count = len(options)
            if not option_count:
                self.sel_menu.sel_index = -1
                self.sel_menu.max_index = -1
                return

            try:
                curr_index = self.node.get_index()
            except Exception:
                curr_index = -1

            self.sel_menu.sel_index = curr_index
            self.sel_menu.max_index = option_count - 1
            self.sel_menu.update_label()
        except Exception:
            print(format_exc())

    populate = reload

    def pose_fields(self):
        pass

    def select_option(self, opt_index=None):
        if None in (self.parent, self.node):
            return

        node = self.node
        curr_index = self.sel_menu.sel_index

        if (opt_index is None or opt_index < 0
                or opt_index > self.sel_menu.max_index):
            print("Invalid option index '%s'" % opt_index)
            return

        self.sel_menu.sel_index = opt_index

        undo_node = self.node.data
        self.node.set_to(opt_index)

        # make an edit state
        if undo_node != self.node.data:
            self.set_edited()
            self.edit_create(undo_node=undo_node, redo_node=self.node.data)

        self.sel_menu.update_label()
Beispiel #20
0
    def __init__(self, app_root, *args, **kwargs):
        if window_base_class == tk.Toplevel:
            kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color)
            self.app_root = app_root
        else:
            self.app_root = self

        window_base_class.__init__(self, app_root, *args, **kwargs)
        BinillaWidget.__init__(self, *args, **kwargs)

        self.title("Sound compiler")
        self.resizable(1, 1)
        self.update()
        try:
            self.iconbitmap(e_c.MOZZ_ICON_PATH)
        except Exception:
            print("Could not load window icon.")

        self.wav_dir = tk.StringVar(self)
        self.sound_path = tk.StringVar(self)

        self.generate_mouth_data = tk.IntVar(self, 0)
        self.split_into_smaller_chunks = tk.IntVar(self, 1)

        self.compression = tk.IntVar(self, constants.COMPRESSION_PCM_16_LE)
        self.sample_rate = tk.IntVar(self, constants.SAMPLE_RATE_22K)
        self.encoding = tk.IntVar(self, constants.ENCODING_MONO)
        self.update_mode = tk.IntVar(self, constants.SOUND_COMPILE_MODE_PRESERVE)

        self.adpcm_lookahead = tk.IntVar(self, 3)
        self.adpcm_noise_shaping = tk.IntVar(self, adpcm.NOISE_SHAPING_OFF)

        self.chunk_size_string = tk.StringVar(self, str(self.chunk_size))
        self.chunk_size_string.trace(
            "w", lambda *a, s=self: s.set_chunk_size())

        # make the frames
        self.main_frame = tk.Frame(self)
        self.wav_info_frame = tk.LabelFrame(
            self, text="Import info")

        self.dirs_frame = tk.LabelFrame(
            self.main_frame, text="Directories")
        self.buttons_frame = tk.Frame(self.main_frame)
        self.settings_frame = tk.Frame(
            self.main_frame)

        self.wav_dir_frame = tk.LabelFrame(
            self.dirs_frame, text="Wav files folder")
        self.sound_path_frame = tk.LabelFrame(
            self.dirs_frame, text="Sound output path")

        self.update_mode_frame = tk.LabelFrame(
            self.main_frame, text="What to do with existing sound tag")
        self.processing_frame = tk.LabelFrame(
            self.settings_frame, text="Format settings")
        self.adpcm_frame = tk.LabelFrame(
            self.settings_frame, text="ADPCM settings")
        self.misc_frame = tk.LabelFrame(
            self.settings_frame, text="Misc settings")


        self.compile_mode_replace_rbtn = tk.Radiobutton(
            self.update_mode_frame, anchor="w",
            variable=self.update_mode, value=constants.SOUND_COMPILE_MODE_NEW,
            text="Erase everything(create from scratch)")
        self.compile_mode_preserve_rbtn = tk.Radiobutton(
            self.update_mode_frame, anchor="w",
            variable=self.update_mode, value=constants.SOUND_COMPILE_MODE_PRESERVE,
            text="Preserve tag values(skip fractions and such)")
        self.compile_mode_additive_rbtn = tk.Radiobutton(
            self.update_mode_frame, anchor="w",
            variable=self.update_mode, value=constants.SOUND_COMPILE_MODE_ADDITIVE,
            text="Erase nothing(only add/update permutations)")


        self.compression_menu = ScrollMenu(
            self.processing_frame,  variable=self.compression, menu_width=10,
            options=tuple(
                compression_names[const] for const in compression_menu_values
                )
            )
        self.sample_rate_menu = ScrollMenu(
            self.processing_frame, variable=self.sample_rate, menu_width=5,
            options=tuple(
                sample_rate_names[const] for const in sample_rate_menu_values
                )
            )
        self.encoding_menu = ScrollMenu(
            self.processing_frame, variable=self.encoding, menu_width=5,
            options=tuple(
                encoding_names[const] for const in encoding_menu_values
                )
            )

        self.adpcm_lookahead_label = tk.Label(
            self.adpcm_frame, text="Lookahead prediction �")
        self.adpcm_lookahead_menu = ScrollMenu(
            self.adpcm_frame, variable=self.adpcm_lookahead,
            menu_width=17, options=adpcm_lookahead_names
            )
        self.adpcm_noise_shaping_label = tk.Label(
            self.adpcm_frame, text="Noise shaping �")
        self.adpcm_noise_shaping_menu = ScrollMenu(
            self.adpcm_frame, variable=self.adpcm_noise_shaping,
            menu_width=17, options=adpcm_noise_shaping_names
            )
        self.adpcm_lookahead_label.tooltip_string = self.adpcm_lookahead_menu.tooltip_string = (
            "Number of samples to look ahead when determining minimum\n"
            "ADPCM error. Higher numbers are typically better."
            )
        self.adpcm_noise_shaping_label.tooltip_string = self.adpcm_noise_shaping_menu.tooltip_string = (
            "The type of noise shaping algorithm to apply to the sound.\n"
            "Noise shaping is a form of dithering, but for audio.\n"
            "Results will vary, so always test after turning this on."
            )

        self.compression_menu.sel_index = 0
        self.sample_rate_menu.sel_index = 0
        self.encoding_menu.sel_index = 0

        self.generate_mouth_data_cbtn = tk.Checkbutton(
            self.misc_frame, variable=self.generate_mouth_data,
            anchor="w", text="Generate mouth data �")
        self.split_into_smaller_chunks_cbtn = tk.Checkbutton(
            self.misc_frame, variable=self.split_into_smaller_chunks,
            anchor="w", text="Split into chunks �")
        self.chunk_size_label = tk.Label(
            self.misc_frame, text="Chunk size �")
        self.chunk_size_spinbox = tk.Spinbox(
            self.misc_frame, from_=self.min_chunk_size,
            to=self.max_chunk_size, increment=1024*4,
            textvariable=self.chunk_size_string, justify="right")

        self.generate_mouth_data_cbtn.tooltip_string = (
            "Whether or not to generate mouth data for this sound.\n"
            "Mouth data animates a characters mouth to the sound."
            )
        self.split_into_smaller_chunks_cbtn.tooltip_string = (
            "Whether or not to split long sounds into pieces.\n"
            "Long sounds may not play properly ingame, and this\n"
            "setting is recommended if the sound is over a few seconds."
            )
        self.chunk_size_label.tooltip_string = self.chunk_size_spinbox.tooltip_string = (
            'The number of samples per chunk to split long sounds into.\n'
            'NOTE 1:\tThis is for mono. A value of 2 equals 1 stereo sample.\n'
            'NOTE 2:\tThis will be rounded down to a multiple of 64 for ADPCM.'
            )


        self._pr_info_tree = tk.ttk.Treeview(
            self.wav_info_frame, selectmode='browse', padding=(0, 0), height=4)
        self.wav_info_vsb = tk.Scrollbar(
            self.wav_info_frame, orient='vertical',
            command=self._pr_info_tree.yview)
        self.wav_info_hsb = tk.Scrollbar(
            self.wav_info_frame, orient='horizontal',
            command=self._pr_info_tree.xview)
        self._pr_info_tree.config(yscrollcommand=self.wav_info_vsb.set,
                                  xscrollcommand=self.wav_info_hsb.set)

        self.wav_dir_entry = tk.Entry(
            self.wav_dir_frame, textvariable=self.wav_dir, state=tk.DISABLED)
        self.wav_dir_browse_button = tk.Button(
            self.wav_dir_frame, text="Browse", command=self.wav_dir_browse)


        self.sound_path_entry = tk.Entry(
            self.sound_path_frame,
            textvariable=self.sound_path,
            state=tk.DISABLED)
        self.sound_path_browse_button = tk.Button(
            self.sound_path_frame, text="Browse",
            command=self.sound_path_browse)


        self.load_button = tk.Button(
            self.buttons_frame, text="Load wav files",
            command=self.load_wav_files)
        self.compile_button = tk.Button(
            self.buttons_frame, text="Compile sound",
            command=self.compile_sound)

        self.populate_wav_info_tree()

        # pack everything
        self.main_frame.pack(fill="both", side='left', pady=3, padx=3)
        self.wav_info_frame.pack(fill="both", side='left', pady=3, padx=3,
                                 expand=True)

        self.dirs_frame.pack(fill="x")
        self.buttons_frame.pack(fill="x", pady=3, padx=3)
        self.update_mode_frame.pack(fill='both')
        self.settings_frame.pack(fill="both")

        self.wav_dir_frame.pack(fill='x')
        self.sound_path_frame.pack(fill='x')

        self.wav_dir_entry.pack(side='left', fill='x', expand=True)
        self.wav_dir_browse_button.pack(side='left')

        self.sound_path_entry.pack(side='left', fill='x', expand=True)
        self.sound_path_browse_button.pack(side='left')

        self.wav_info_hsb.pack(side="bottom", fill='x')
        self.wav_info_vsb.pack(side="right",  fill='y')
        self._pr_info_tree.pack(side='left', fill='both', expand=True)

        self.load_button.pack(side='left', fill='both', padx=3, expand=True)
        self.compile_button.pack(side='right', fill='both', padx=3, expand=True)

        for w in (self.processing_frame, self.adpcm_frame, self.misc_frame):
            w.pack(expand=True, fill='both')

        for w in (self.compile_mode_replace_rbtn,
                  self.compile_mode_preserve_rbtn,
                  self.compile_mode_additive_rbtn,):
            w.pack(expand=True, fill='both')

        for w in (self.compression_menu,
                  self.sample_rate_menu,
                  self.encoding_menu):
            w.pack(expand=True, side='left', fill='both')

        i = 0
        for w, lbl in (
                (self.adpcm_lookahead_menu, self.adpcm_lookahead_label),
                (self.adpcm_noise_shaping_menu, self.adpcm_noise_shaping_label)
                ):
            lbl.grid(row=i, column=0, sticky="w", padx=(17, 0))
            w.grid(row=i, column=2, sticky="news", padx=(10, 0))
            i += 1


        self.generate_mouth_data_cbtn.grid(
            row=0, column=0, sticky="news", padx=(17, 0))
        self.split_into_smaller_chunks_cbtn.grid(
            row=0, column=1, sticky="news", padx=(17, 0))
        self.chunk_size_label.grid(
            row=1, column=0, sticky="w", padx=(17, 0))
        self.chunk_size_spinbox.grid(
            row=1, column=1, sticky="news", padx=(10, 10))

        self.apply_style()
        if self.app_root is not self:
            self.transient(self.app_root)
Beispiel #21
0
class ObjectConverter(ConverterBase, window_base_class):
    object_types = (
        "vehicle",
        "biped",
        "weapon",
        "equipment",
        "garbage",
        "device_machine",
        "device_control",
        "device_light_fixture",
        "projectile",
        "sound_scenery",
        "scenery",
    )

    def __init__(self, app_root, *args, **kwargs):
        if isinstance(self, tk.Toplevel):
            kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color)

        window_base_class.__init__(self, app_root, *args, **kwargs)
        ConverterBase.__init__(self, app_root, *args, **kwargs)
        kwargs["title"] = "Object to object converter"
        self.setup_window(*args, **kwargs)

    @property
    def src_ext(self):
        try:
            return self.object_types[self.src_menu.sel_index]
        except Exception:
            return ""

    @property
    def dst_ext(self):
        try:
            return self.object_types[self.dst_menu.sel_index]
        except Exception:
            return ""

    @property
    def src_exts(self):
        return self.object_types

    def setup_window(self, *args, **kwargs):
        ConverterBase.setup_window(self, *args, **kwargs)

        self.settings_frame = tk.LabelFrame(self, text="Conversion settings")
        self.from_label = tk.Label(self.settings_frame, text="from")
        self.src_menu = ScrollMenu(
            self.settings_frame,
            options=self.object_types,
            menu_width=20,
            sel_index=0,
        )
        self.to_label = tk.Label(self.settings_frame, text="to")
        self.dst_menu = ScrollMenu(
            self.settings_frame,
            options=self.object_types,
            menu_width=20,
            sel_index=10,
        )

        self.pack_widgets()
        self.apply_style()

    def pack_widgets(self):
        ConverterBase.pack_widgets(self)

        # pack everything
        self.settings_frame.pack(fill='both', anchor='nw')
        self.from_label.pack(padx=(10, 0), pady=10, anchor='w', side='left')
        self.src_menu.pack(padx=(10, 0),
                           pady=10,
                           fill='x',
                           anchor='w',
                           side='left')
        self.to_label.pack(padx=(10, 0), pady=10, anchor='w', side='left')
        self.dst_menu.pack(padx=(10, 0),
                           pady=10,
                           fill='x',
                           anchor='w',
                           side='left')

    def destroy(self):
        ConverterBase.destroy(self)
        window_base_class.destroy(self)

    def tag_path_browse(self):
        curr_tag_path = self.tag_path.get()
        ConverterBase.tag_path_browse(self)
        if curr_tag_path == self.tag_path.get(): return

        try:
            ext = Path(curr_tag_path).suffix[1:].lower()
            self.src_menu.sel_index = self.object_types.index(ext)
        except Exception:
            pass

    def convert(self, tag_path):
        return obje_to_obje(tag_path, self.src_ext, self.dst_ext)