from renderable import LAYER_VIS_FULL, LAYER_VIS_DIM, LAYER_VIS_NONE

from ui_tool import PencilTool, EraseTool, RotateTool, GrabTool, TextTool, SelectTool, PasteTool, FillTool

#
# specific pulldown menu items, eg File > Save, Edit > Copy
#

class PulldownMenuItem:
    # label that displays for this item
    label = 'Test Menu Item'
    # bindable command we look up from InputLord to get binding text from
    command = 'test_command'
    # if not None, passed to button's cb_arg
    cb_arg = None
    # if True, pulldown button creation process won't auto-pad
    no_pad = False
    # if True, item will never be dimmed
    always_active = False
    # if True, pulldown will close when this item is selected
    close_on_select = False
    # item is allowed in Art Mode
    art_mode_allowed = True
    # item is allowed in Game Mode
    game_mode_allowed = True
    def should_dim(app):
        "returns True if this item should be dimmed based on current application state"
        # so many commands are inapplicable with no active art, default to dimming an
        # item if this is the case
        return app.ui.active_art is None
    def get_label(app):
        "returns custom generated label based on app state"
        return None

class SeparatorItem(PulldownMenuItem):
    "menu separator, non-interactive and handled specially by menu drawing"
    pass

class ArtModePulldownMenuItem(PulldownMenuItem):
    # unless overridden, art mode items not allowed in game mode
    game_mode_allowed = False

#
# file menu
#
class FileNewItem(ArtModePulldownMenuItem):
    label = 'New…'
    command = 'new_art'
    always_active = True

class FileOpenItem(ArtModePulldownMenuItem):
    label = 'Open…'
    command = 'open_art'
    always_active = True

class FileSaveItem(ArtModePulldownMenuItem):
    label = 'Save'
    command = 'save_current'
    def should_dim(app):
        return not app.ui.active_art or not app.ui.active_art.unsaved_changes

class FileSaveAsItem(ArtModePulldownMenuItem):
    label = 'Save As…'
    command = 'save_art_as'
    def should_dim(app):
        return app.ui.active_art is None

class FileCloseItem(ArtModePulldownMenuItem):
    label = 'Close'
    command = 'close_art'
    def should_dim(app):
        return app.ui.active_art is None

class FileRevertItem(ArtModePulldownMenuItem):
    label = 'Revert'
    command = 'revert_art'
    def should_dim(app):
        return app.ui.active_art is None or not app.ui.active_art.unsaved_changes

class FileImportItem(ArtModePulldownMenuItem):
    label = 'Import…'
    command = 'import_file'
    always_active = True

class FileExportItem(ArtModePulldownMenuItem):
    label = 'Export…'
    command = 'export_file'
    def should_dim(app):
        return app.ui.active_art is None

class FileExportLastItem(ArtModePulldownMenuItem):
    label = 'Export last'
    command = 'export_file_last'
    def should_dim(app):
        return app.ui.active_art is None

class FileConvertImageItem(ArtModePulldownMenuItem):
    label = 'Convert Image…'
    command = 'convert_image'
    def should_dim(app):
        return app.ui.active_art is None

class FileQuitItem(ArtModePulldownMenuItem):
    label = 'Quit'
    command = 'quit'
    always_active = True
    game_mode_allowed = True

#
# edit menu
#
class EditUndoItem(ArtModePulldownMenuItem):
    label = 'Undo'
    command = 'undo'
    def should_dim(app):
        return not app.ui.active_art or len(app.ui.active_art.command_stack.undo_commands) == 0

class EditRedoItem(ArtModePulldownMenuItem):
    label = 'Redo'
    command = 'redo'
    def should_dim(app):
        return not app.ui.active_art or len(app.ui.active_art.command_stack.redo_commands) == 0

class EditCutItem(ArtModePulldownMenuItem):
    label = 'Cut'
    command = 'cut_selection'
    def should_dim(app):
        return len(app.ui.select_tool.selected_tiles) == 0

class EditCopyItem(ArtModePulldownMenuItem):
    label = 'Copy'
    command = 'copy_selection'
    def should_dim(app):
        return len(app.ui.select_tool.selected_tiles) == 0

class EditPasteItem(ArtModePulldownMenuItem):
    label = 'Paste'
    command = 'select_paste_tool'
    def should_dim(app):
        return len(app.ui.clipboard) == 0

class EditDeleteItem(ArtModePulldownMenuItem):
    label = 'Clear'
    command = 'erase_selection_or_art'

class EditSelectAllItem(ArtModePulldownMenuItem):
    label = 'Select All'
    command = 'select_all'

class EditSelectNoneItem(ArtModePulldownMenuItem):
    label = 'Select None'
    command = 'select_none'

class EditSelectInvertItem(ArtModePulldownMenuItem):
    label = 'Invert Selection'
    command = 'select_invert'

class EditPreferences(ArtModePulldownMenuItem):
    label = 'Preferences…'
    command = 'edit_cfg'

#
# tool menu
#

class ToolTogglePickerItem(ArtModePulldownMenuItem):
    # two spaces in front of each label to leave room for mark
    label = 'Show char/color picker'
    command = 'toggle_picker'

class ToolTogglePickerHoldItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'toggle_picker_hold'
    def get_label(app):
        return 'Picker toggle key: %s' % ['press', 'hold'][app.ui.popup_hold_to_show]

class ToolSwapSelectedColors(ArtModePulldownMenuItem):
    label = 'Swap selected fg/bg colors'
    command = 'swap_fg_bg_colors'

class ToolToggleArtToolbar(ArtModePulldownMenuItem):
    label = '  Show toolbar'
    command = 'toggle_art_toolbar'
    def should_mark(ui):
        return ui.art_toolbar.visible

class ToolPaintItem(ArtModePulldownMenuItem):
    # two spaces in front of each label to leave room for mark
    label = '  %s' % PencilTool.button_caption
    command = 'select_pencil_tool'

class ToolEraseItem(ArtModePulldownMenuItem):
    label = '  %s' % EraseTool.button_caption
    command = 'select_erase_tool'

class ToolRotateItem(ArtModePulldownMenuItem):
    label = '  %s' % RotateTool.button_caption
    command = 'select_rotate_tool'

class ToolGrabItem(ArtModePulldownMenuItem):
    label = '  %s' % GrabTool.button_caption
    command = 'select_grab_tool'

class ToolTextItem(ArtModePulldownMenuItem):
    label = '  %s' % TextTool.button_caption
    command = 'select_text_tool'

class ToolSelectItem(ArtModePulldownMenuItem):
    label = '  %s' % SelectTool.button_caption
    command = 'select_select_tool'

class ToolPasteItem(ArtModePulldownMenuItem):
    label = '  %s' % PasteTool.button_caption
    command = 'select_paste_tool'

class ToolFillItem(ArtModePulldownMenuItem):
    label = '  %s' % FillTool.button_caption
    command = 'select_fill_tool'

class ToolIncreaseBrushSizeItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'increase_brush_size'
    def should_dim(app):
        # dim this item for tools where brush size doesn't apply
        if not app.ui.active_art or not app.ui.selected_tool.brush_size:
            return True
    def get_label(app):
        if not app.ui.selected_tool.brush_size:
            return 'Increase brush size'
        size = app.ui.selected_tool.brush_size + 1
        return 'Increase brush size to %s' % size

class ToolDecreaseBrushSizeItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'decrease_brush_size'
    def should_dim(app):
        if not app.ui.active_art or not app.ui.selected_tool.brush_size:
            return True
        return app.ui.selected_tool.brush_size <= 1
    def get_label(app):
        if not app.ui.selected_tool.brush_size:
            return 'Decrease brush size'
        size = app.ui.selected_tool.brush_size - 1
        return 'Decrease brush size to %s' % size

class ToolSettingsItem(ArtModePulldownMenuItem):
    # base class for tool settings toggle items
    def should_dim(app):
        # blacklist specific tools
        return not app.ui.active_art or type(app.ui.selected_tool) in [SelectTool]

class ToolToggleAffectsCharItem(ToolSettingsItem):
    label = '  Affects: character'
    command = 'toggle_affects_char'
    def should_mark(ui):
        return ui.selected_tool.affects_char

class ToolToggleAffectsFGItem(ToolSettingsItem):
    label = '  Affects: foreground color'
    command = 'toggle_affects_fg'
    def should_mark(ui):
        return ui.selected_tool.affects_fg_color

class ToolToggleAffectsBGItem(ToolSettingsItem):
    label = '  Affects: background color'
    command = 'toggle_affects_bg'
    def should_mark(ui):
        return ui.selected_tool.affects_bg_color

class ToolToggleAffectsXformItem(ToolSettingsItem):
    label = '  Affects: character xform'
    command = 'toggle_affects_xform'
    def should_mark(ui):
        return ui.selected_tool.affects_xform

# cycle between the 3 different fill boundary types
class ToolSetFillBoundariesItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'cycle_fill_boundary_mode'
    def should_dim(app):
        return not type(app.ui.selected_tool) is FillTool
    def get_label(app):
        return 'Fill tool bounded by: %s' % app.ui.fill_tool.boundary_mode_names[app.ui.fill_tool.boundary_mode]


#
# view  menu
#
class ViewToggleCRTItem(ArtModePulldownMenuItem):
    label = '  CRT filter'
    command = 'toggle_crt'
    game_mode_allowed = True
    def should_dim(app):
        return app.fb.disable_crt
    def should_mark(ui):
        return ui.app.fb.crt

class ViewToggleGridItem(ArtModePulldownMenuItem):
    label = '  Grid'
    command = 'toggle_grid_visibility'
    game_mode_allowed = True
    def should_mark(ui):
        return ui.app.grid.visible

class ViewBGTextureItem(ArtModePulldownMenuItem):
    label = '  Textured background'
    command = 'toggle_bg_texture'
    always_active = True
    def should_mark(ui):
        return ui.app.show_bg_texture

class ViewToggleZoomExtentsItem(ArtModePulldownMenuItem):
    label = '  Zoom to Art extents'
    command = 'toggle_zoom_extents'
    def should_mark(ui):
        return ui.active_art and ui.active_art.camera_zoomed_extents

class ViewZoomInItem(ArtModePulldownMenuItem):
    label = 'Zoom in'
    command = 'camera_zoom_in_proportional'

class ViewZoomOutItem(ArtModePulldownMenuItem):
    label = 'Zoom out'
    command = 'camera_zoom_out_proportional'

class ViewSetZoomItem(ArtModePulldownMenuItem):
    label = 'Set camera zoom…'
    command = 'set_camera_zoom'

class ViewToggleCameraTiltItem(ArtModePulldownMenuItem):
    label = '  Camera tilt'
    command = 'toggle_camera_tilt'
    always_active = True
    game_mode_allowed = True
    def should_mark(ui):
        return ui.app.camera.y_tilt != 0

class ViewSetOverlayImageItem(ArtModePulldownMenuItem):
    label = 'Set overlay image…'
    command = 'select_overlay_image'

class ViewToggleOverlayImageItem(ArtModePulldownMenuItem):
    label = '  Toggle overlay image'
    command = 'toggle_overlay_image'
    def should_mark(ui):
        return ui.app.draw_overlay
    def should_dim(app):
        return app.overlay_renderable is None

class ViewSetOverlayImageOpacityItem(ArtModePulldownMenuItem):
    label = 'Set overlay image opacity…'
    command = 'set_overlay_image_opacity'
    def should_dim(app):
        return app.overlay_renderable is None or not app.draw_overlay

class ViewSetOverlayImageScalingItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'set_overlay_image_scaling'
    def get_label(app):
        return 'Overlay image scaling: %s' % ['width', 'height', 'fill'][app.overlay_scale_type]
    def should_dim(app):
        return app.overlay_renderable is None or not app.draw_overlay

#
# art menu
#
class ArtOpenAllGameAssetsItem(ArtModePulldownMenuItem):
    label = 'Open all Game Mode assets'
    command = 'open_all_game_assets'
    def should_dim(app):
        return len(app.gw.objects) == 0

class ArtPreviousItem(ArtModePulldownMenuItem):
    label = 'Previous Art'
    command = 'previous_art'
    def should_dim(app):
        return len(app.art_loaded_for_edit) < 2

class ArtNextItem(ArtModePulldownMenuItem):
    label = 'Next Art'
    command = 'next_art'
    def should_dim(app):
        return len(app.art_loaded_for_edit) < 2

class ArtCropToSelectionItem(ArtModePulldownMenuItem):
    label = 'Crop to selection'
    command = 'crop_to_selection'
    def should_dim(app):
        return len(app.ui.select_tool.selected_tiles) == 0

class ArtResizeItem(ArtModePulldownMenuItem):
    label = 'Resize…'
    command = 'resize_art'

class ArtFlipHorizontal(ArtModePulldownMenuItem):
    label = 'Flip horizontally'
    command = 'art_flip_horizontal'

class ArtFlipVertical(ArtModePulldownMenuItem):
    label = 'Flip vertically'
    command = 'art_flip_vertical'

class ArtToggleFlipAffectsXforms(ArtModePulldownMenuItem):
    label = '  Flip affects xforms'
    command = 'art_toggle_flip_affects_xforms'
    def should_mark(ui):
        return ui.flip_affects_xforms

class ArtRunScriptItem(ArtModePulldownMenuItem):
    label = 'Run Artscript…'
    command = 'run_art_script'
    
class ArtRunLastScriptItem(ArtModePulldownMenuItem):
    label = 'Run last Artscript'
    command = 'run_art_script_last'
    def should_dim(app):
        return app.last_art_script is None

#
# frame menu
#
class FramePreviousItem(ArtModePulldownMenuItem):
    label = 'Previous frame'
    command = 'previous_frame'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.frames < 2

class FrameNextItem(ArtModePulldownMenuItem):
    label = 'Next frame'
    command = 'next_frame'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.frames < 2

class FrameTogglePlaybackItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'toggle_anim_playback'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.frames < 2
    def get_label(app):
        if not app.ui.active_art:
            return 'Start animation playback'
        animating = app.ui.active_art.renderables[0].animating
        return ['Start', 'Stop'][animating] + ' animation playback'

class FrameToggleOnionItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'toggle_onion_visibility'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.frames < 2
    def get_label(app):
        l = '%s onion skin frames' % ['Show', 'Hide'][app.onion_frames_visible]
        return l

class FrameCycleOnionFramesItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'cycle_onion_frames'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.frames < 2
    def get_label(app):
        return 'Number of onion frames: %s' % app.onion_show_frames

class FrameCycleOnionDisplayItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'cycle_onion_ahead_behind'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.frames < 2
    def get_label(app):
        if app.onion_show_frames_behind and app.onion_show_frames_ahead:
            display = 'Next & Previous'
        elif app.onion_show_frames_behind:
            display = 'Previous'
        else:
            display = 'Next'
        return 'Onion frames show: %s' % display

class FrameAddFrameItem(ArtModePulldownMenuItem):
    label = 'Add frame…'
    command = 'add_frame'

class FrameDuplicateFrameItem(ArtModePulldownMenuItem):
    label = 'Duplicate this frame…'
    command = 'duplicate_frame'

class FrameChangeDelayItem(ArtModePulldownMenuItem):
    label = "Change this frame's hold time…"
    command = 'change_frame_delay'

class FrameChangeDelayAllItem(ArtModePulldownMenuItem):
    label = "Change all frames' hold times…"
    command = 'change_frame_delay_all'

class FrameChangeIndexItem(ArtModePulldownMenuItem):
    label = "Change this frame's index…"
    command = 'change_frame_index'

class FrameDeleteFrameItem(ArtModePulldownMenuItem):
    label = 'Delete this frame'
    command = 'delete_frame'
    def should_dim(app):
        # don't delete last frame
        return not app.ui.active_art or app.ui.active_art.frames < 2

#
# layer menu
#
class LayerAddItem(ArtModePulldownMenuItem):
    label = "Add layer…"
    command = 'add_layer'

class LayerDuplicateItem(ArtModePulldownMenuItem):
    label = "Duplicate this layer…"
    command = 'duplicate_layer'

class LayerSetNameItem(ArtModePulldownMenuItem):
    label = "Change this layer's name…"
    command = 'change_layer_name'

class LayerSetZItem(ArtModePulldownMenuItem):
    label = "Change this layer's Z-depth…"
    command = 'change_layer_z'

class LayerToggleVisibleItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'toggle_layer_visibility'
    def get_label(app):
        if not app.ui.active_art:
            return 'Show this layer (Game Mode)'
        visible = app.ui.active_art.layers_visibility[app.ui.active_art.active_layer]
        return ['Show', 'Hide'][visible] + ' this layer (Game Mode)'

class LayerDeleteItem(ArtModePulldownMenuItem):
    label = "Delete this layer"
    command = 'delete_layer'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.layers < 2

class LayerSetInactiveVizItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'cycle_inactive_layer_visibility'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.layers < 2
    def get_label(app):
        l = 'Inactive layers: '
        if app.inactive_layer_visibility == LAYER_VIS_FULL:
            return l + 'Visible'
        elif app.inactive_layer_visibility == LAYER_VIS_DIM:
            return l + 'Dim'
        elif app.inactive_layer_visibility == LAYER_VIS_NONE:
            return l + 'Invisible'

class LayerShowHiddenItem(ArtModePulldownMenuItem):
    label = 'blah'
    command = 'toggle_hidden_layers_visible'
    def get_label(app):
        l = 'Art Mode-only layers: '
        l += ['Hidden', 'Visible'][app.show_hidden_layers]
        return l

class LayerPreviousItem(ArtModePulldownMenuItem):
    label = 'Previous layer'
    command = 'previous_layer'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.layers < 2

class LayerNextItem(ArtModePulldownMenuItem):
    label = 'Next layer'
    command = 'next_layer'
    def should_dim(app):
        return not app.ui.active_art or app.ui.active_art.layers < 2

#
# char/color menu
#
class ChooseCharSetItem(ArtModePulldownMenuItem):
    label = 'Choose character set…'
    command = 'choose_charset'

class ChoosePaletteItem(ArtModePulldownMenuItem):
    label = 'Choose palette…'
    command = 'choose_palette'

class PaletteFromImageItem(ArtModePulldownMenuItem):
    label = 'Palette from image…'
    command = 'palette_from_file'
    always_active = True

#
# help menu
#
class HelpDocsItem(ArtModePulldownMenuItem):
    label = 'Help (in browser)'
    command = 'open_help_docs'
    always_active = True
    close_on_select = True
    game_mode_allowed = True

class HelpGenerateDocsItem(ArtModePulldownMenuItem):
    label = 'Generate documentation'
    command = 'generate_docs'
    close_on_select = True
    def should_dim(app):
        return not app.pdoc_available

class HelpWebsiteItem(ArtModePulldownMenuItem):
    label = 'Playscii website'
    command = 'open_website'
    always_active = True
    close_on_select = True

#
# menu data
#
class PulldownMenuData:
    "data for pulldown menus, eg File, Edit, etc; mainly a list of menu items"
    items = []
    def should_mark_item(item, ui):
        "returns True if this item should be marked, subclasses have custom logic here"
        return False
    def get_items(app):
        """
        returns a list of items generated from app state, used for
        dynamically-generated items
        """
        return []

class FileMenuData(PulldownMenuData):
    items = [FileNewItem, FileOpenItem, FileSaveItem, FileSaveAsItem,
             FileCloseItem, FileRevertItem, SeparatorItem, FileImportItem,
             FileExportItem, FileExportLastItem, SeparatorItem, FileQuitItem]

class EditMenuData(PulldownMenuData):
    items = [EditUndoItem, EditRedoItem, SeparatorItem,
             EditCutItem, EditCopyItem, EditPasteItem, EditDeleteItem,
             SeparatorItem, EditSelectAllItem,
             EditSelectNoneItem, EditSelectInvertItem, SeparatorItem,
             EditPreferences]

class ToolMenuData(PulldownMenuData):
    items = [ToolTogglePickerItem, ToolTogglePickerHoldItem,
             ToolSwapSelectedColors, ToolToggleArtToolbar, SeparatorItem,
             ToolPaintItem, ToolEraseItem, ToolRotateItem, ToolGrabItem,
             ToolTextItem, ToolSelectItem, ToolPasteItem, ToolFillItem, SeparatorItem,
             ToolIncreaseBrushSizeItem, ToolDecreaseBrushSizeItem,
             ToolToggleAffectsCharItem, ToolToggleAffectsFGItem,
             ToolToggleAffectsBGItem, ToolToggleAffectsXformItem, SeparatorItem,
             ToolSetFillBoundariesItem
             ]
             # TODO: cycle char/color/xform items?
    # TODO: generate list from UI.tools instead of manually specified MenuItems
    def should_mark_item(item, ui):
        # if it's a tool setting toggle, use its own mark check function
        if item.__bases__[0] is ToolSettingsItem:
            return item.should_mark(ui)
        elif hasattr(item, 'should_mark'): # toolbar toggle, etc
            return item.should_mark(ui)
        return item.label == '  %s' % ui.selected_tool.button_caption

class ViewMenuData(PulldownMenuData):
    items = [ViewToggleCRTItem, ViewToggleGridItem, ViewBGTextureItem,
             SeparatorItem,
             ViewToggleZoomExtentsItem, ViewZoomInItem, ViewZoomOutItem,
             ViewSetZoomItem, ViewToggleCameraTiltItem, SeparatorItem,
             ViewSetOverlayImageItem, ViewToggleOverlayImageItem,
             ViewSetOverlayImageOpacityItem, ViewSetOverlayImageScalingItem
            ]
    
    def should_mark_item(item, ui):
        if hasattr(item, 'should_mark'):
            return item.should_mark(ui)
        return False

class ArtMenuData(PulldownMenuData):
    items = [ArtResizeItem, ArtCropToSelectionItem,
             ArtFlipHorizontal, ArtFlipVertical, ArtToggleFlipAffectsXforms,
             SeparatorItem,
             ArtRunScriptItem, ArtRunLastScriptItem, SeparatorItem,
             ArtOpenAllGameAssetsItem, SeparatorItem,
             ArtPreviousItem, ArtNextItem, SeparatorItem]
    
    def should_mark_item(item, ui):
        "show checkmark for active art"
        if hasattr(item, 'should_mark'):
            return item.should_mark(ui)
        return ui.active_art and ui.active_art.filename == item.cb_arg
    
    def get_items(app):
        "turn each loaded art into a menu item"
        items = []
        for art in app.art_loaded_for_edit:
            # class just being used to store data, no need to spawn it
            class TempMenuItemClass(ArtModePulldownMenuItem): pass
            item = TempMenuItemClass
            # leave spaces for mark
            item.label = '  %s' % art.filename
            item.command = 'art_switch_to'
            item.cb_arg = art.filename
            # order list by art's time loaded
            item.time_loaded = art.time_loaded
            items.append(item)
        items.sort(key=lambda item: item.time_loaded)
        return items


class FrameMenuData(PulldownMenuData):
    items = [FrameAddFrameItem, FrameDuplicateFrameItem,
             FrameChangeDelayItem, FrameChangeDelayAllItem,
             FrameChangeIndexItem, FrameDeleteFrameItem, SeparatorItem,
             FrameTogglePlaybackItem, FramePreviousItem, FrameNextItem,
             SeparatorItem,
             FrameToggleOnionItem, FrameCycleOnionFramesItem,
             FrameCycleOnionDisplayItem]


class LayerMenuData(PulldownMenuData):
    
    items = [LayerAddItem, LayerDuplicateItem, LayerSetNameItem, LayerSetZItem,
             LayerDeleteItem, SeparatorItem,
             LayerSetInactiveVizItem, LayerPreviousItem,LayerNextItem,
             SeparatorItem, LayerToggleVisibleItem, LayerShowHiddenItem,
             SeparatorItem]
    
    def should_mark_item(item, ui):
        "show checkmark for active art"
        if not ui.active_art:
            return False
        return ui.active_art.active_layer == item.cb_arg
    
    def get_items(app):
        "turn each layer into a menu item"
        items = []
        if not app.ui.active_art:
            return items
        # first determine longest line to set width of items
        longest_line = 0
        for layer_name in app.ui.active_art.layer_names:
            if len(layer_name) > longest_line:
                longest_line = len(layer_name)
        # check non-generated menu items too
        for item in LayerMenuData.items:
            if len(item.label) + 1 > longest_line:
                longest_line = len(item.label) + 1
        # cap at max allowed line length
        longest_line = min(longest_line, 50)
        for i,layer_name in enumerate(app.ui.active_art.layer_names):
            class TempMenuItemClass(ArtModePulldownMenuItem): pass
            item = TempMenuItemClass
            # leave spaces for mark
            item.label = '  %s' % layer_name
            if not app.ui.active_art.layers_visibility[i]:
                item.label += ' (hidden)'
            # pad, put Z depth on far right
            item.label = item.label.ljust(longest_line)
            # trim to keep below a max length
            item.label = item.label[:longest_line]
            # spaces between layer name and z depth
            item.label += 'z:%.2f' % app.ui.active_art.layers_z[i]
            # tell PulldownMenu's button creation process not to auto-pad
            item.no_pad = True
            item.command = 'layer_switch_to'
            item.cb_arg = i
            items.append(item)
        return items

class CharColorMenuData(PulldownMenuData):
    items = [ChooseCharSetItem, ChoosePaletteItem, SeparatorItem,
             PaletteFromImageItem]

class HelpMenuData(PulldownMenuData):
    items = [HelpDocsItem, HelpGenerateDocsItem, HelpWebsiteItem]
