Mercurial > traipse_dev
view orpg/mapper/miniatures_handler.py @ 184:dcae32e219f1 beta
Traipse Beta 'OpenRPG' {100117-00}
Traipse is a distribution of OpenRPG that is designed to be easy to
setup and go. Traipse also makes it easy for developers to work on code
without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy'
and adds fixes to the code. 'Ornery-Orc's main goal is to offer more
advanced features and enhance the productivity of the user.
Update Summary (Beta)
New Features:
Added Bookmarks
Added 'boot' command to remote admin
Added confirmation window for sent nodes
Minor changes to allow for portability to an OpenSUSE linux OS
Miniatures Layer pop up box allows users to turn off Mini labels, from
FlexiRPG
Zoom Mouse plugin added
Images added to Plugin UI
Switching to Element Tree
Map efficiency, from FlexiRPG
Added Status Bar to Update Manager
New TrueDebug Class in orpg_log (See documentation for usage)
Portable Mercurial
Tip of the Day added, from Core and community
New Reference Syntax added for custom PC sheets
New Child Reference for gametree
New Parent Reference for gametree
New Gametree Recursion method, mapping, context sensitivity, and
effeciency..
New Features node with bonus nodes and Node Referencing help added
Dieroller structure from Core
New DieRoller portability for odd Dice
Added 7th Sea die roller; ie [7k3] = [7d10.takeHighest(3).open(10)]
New 'Mythos' System die roller added
Added new vs. die roller method for WoD; ie [3v3] = [3d10.vs(3)].
Included for Mythos roller also
New Warhammer FRPG Die Roller (Special thanks to Puu-san for the
support)
New EZ_Tree Reference system. Push a button, Traipse the tree, get a
reference (Beta!)
Fixes:
Fix to Text based Server
Fix to Remote Admin Commands
Fix to Pretty Print, from Core
Fix to Splitter Nodes not being created
Fix to massive amounts of images loading, from Core
Fix to Map from gametree not showing to all clients
Fix to gametree about menus
Fix to Password Manager check on startup
Fix to PC Sheets from tool nodes. They now use the tabber_panel
Fix to Whiteboard ID to prevent random line or text deleting.
Fixes to Server, Remote Server, and Server GUI
Fix to Update Manager; cleaner clode for saved repositories
Fixes made to Settings Panel and now reactive settings when Ok is
pressed
Fixes to Alternity roller's attack roll. Uses a simple Tuple instead of
a Splice
Fix to Use panel of Forms and Tabbers. Now longer enters design mode
Fix made Image Fetching. New fetching image and new failed image
Modified ID's to prevent non updated clients from ruining the fix.
default_manifest.xml renamed to default_upmana.xml
author | sirebral |
---|---|
date | Sun, 17 Jan 2010 21:37:34 -0600 |
parents | 0d9b746b5751 |
children |
line wrap: on
line source
# Copyright (C) 2000-2001 The OpenRPG Project # # openrpg-dev@lists.sourceforge.net # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # -- # # File: mapper/whiteboard_hander.py # Author: OpenRPG Team # Maintainer: # Version: # $Id: miniatures_handler.py,v Traipse 'Ornery-Orc' prof.ebral Exp $ # # Description: Miniature layer handler # __version__ = "$Id: miniatures_handler.py,v Traipse 'Ornery-Orc' prof.ebral Exp $" from base_handler import * from min_dialogs import * import thread, time, mimetypes, urllib, wx, os from grid import GRID_RECTANGLE from grid import GRID_HEXAGON from grid import GRID_ISOMETRIC from orpg.tools.orpg_settings import settings from xml.etree.ElementTree import ElementTree, Element from xml.etree.ElementTree import fromstring, tostring SHOW_LABELS_TOOL = wx.NewId() AUTO_LABEL_TOOL = wx.NewId() LAYER_TOOL = wx.NewId() MIN_LIST_TOOL = wx.NewId() MIN_TOOL = wx.NewId() MIN_URL = wx.NewId() SERIAL_TOOL = wx.NewId() MIN_MOVE = wx.NewId() MIN_REMOVE = wx.NewId() MIN_PROP_DLG = wx.NewId() MIN_FACING_NONE = wx.NewId() MIN_FACING_MATCH = wx.NewId() MIN_FACING_EAST = wx.NewId() MIN_FACING_WEST = wx.NewId() MIN_FACING_NORTH = wx.NewId() MIN_FACING_SOUTH = wx.NewId() MIN_FACING_NORTHEAST = wx.NewId() MIN_FACING_SOUTHEAST = wx.NewId() MIN_FACING_SOUTHWEST = wx.NewId() MIN_FACING_NORTHWEST = wx.NewId() MIN_HEADING_NONE = wx.NewId() MIN_HEADING_MATCH = wx.NewId() MIN_HEADING_EAST = wx.NewId() MIN_HEADING_WEST = wx.NewId() MIN_HEADING_NORTH = wx.NewId() MIN_HEADING_SOUTH = wx.NewId() MIN_HEADING_NORTHEAST = wx.NewId() MIN_HEADING_SOUTHEAST = wx.NewId() MIN_HEADING_SOUTHWEST = wx.NewId() MIN_HEADING_NORTHWEST = wx.NewId() MIN_HEADING_SUBMENU = wx.NewId() MIN_FACING_SUBMENU = wx.NewId() MIN_ALIGN_SUBMENU = wx.NewId() MIN_ALIGN_GRID_CENTER = wx.NewId() MIN_ALIGN_GRID_TL = wx.NewId() MIN_TITLE_HACK = wx.NewId() MIN_TO_GAMETREE = wx.NewId() MIN_BACK_ONE = wx.NewId() MIN_FORWARD_ONE = wx.NewId() MIN_TO_BACK = wx.NewId() MIN_TO_FRONT = wx.NewId() MIN_LOCK_BACK = wx.NewId() MIN_LOCK_FRONT = wx.NewId() MIN_FRONTBACK_UNLOCK = wx.NewId() MIN_ZORDER_SUBMENU = wx.NewId() MIN_SHOW_HIDE = wx.NewId() MIN_LOCK_UNLOCK = wx.NewId() MAP_REFRESH_MINI_URLS = wx.NewId() class myFileDropTarget(wx.FileDropTarget): def __init__(self, handler): wx.FileDropTarget.__init__(self) self.m_handler = handler def OnDropFiles(self, x, y, filenames): self.m_handler.on_drop_files(x, y, filenames) class miniatures_handler(base_layer_handler): def __init__(self, parent, id, canvas): self.sel_min = None self.auto_label = 1 self.use_serial = 1 self.canvas = canvas self.settings = settings self.mini_rclick_menu_extra_items = {} self.background_rclick_menu_extra_items = {} base_layer_handler.__init__(self, parent, id, canvas) # id is the index of the last good menu choice or 'None' # if the last menu was left without making a choice # should be -1 at other times to prevent events overlapping self.lastMenuChoice = None self.drag_mini = None self.tooltip_delay_miliseconds = 500 self.tooltip_timer = wx.CallLater(self.tooltip_delay_miliseconds, self.on_tooltip_timer) self.tooltip_timer.Stop() dt = myFileDropTarget(self) self.canvas.SetDropTarget(dt) self.tooltip = wx.ToolTip('') def build_ctrls(self): base_layer_handler.build_ctrls(self) # add controls in reverse order! (unless you want them after the default tools) self.min_url = wx.ComboBox(self, wx.ID_ANY, "http://", style=wx.CB_DROPDOWN | wx.CB_SORT) self.localBrowse = wx.Button(self, wx.ID_ANY, 'Browse', style=wx.BU_EXACTFIT) minilist = createMaskedButton( self, dir_struct["icon"]+'questionhead.gif', 'Edit miniature properties', wx.ID_ANY) miniadd = wx.Button(self, wx.ID_OK, "Add Miniature", style=wx.BU_EXACTFIT) self.sizer.Add(self.min_url, 1, wx.ALIGN_CENTER) self.sizer.Add((6, 0)) self.sizer.Add(miniadd, 0, wx.ALIGN_CENTER) self.sizer.Add((6, 0)) self.sizer.Add(self.localBrowse, 0, wx.ALIGN_CENTER) self.sizer.Add((6, 0)) self.sizer.Add(minilist, 0, wx.ALIGN_CENTER) self.Bind(wx.EVT_BUTTON, self.on_min_list, minilist) self.Bind(wx.EVT_BUTTON, self.on_miniature, miniadd) self.Bind(wx.EVT_BUTTON, self.on_browse, self.localBrowse) def on_browse(self, evt): if not self.role_is_gm_or_player(): return dlg = wx.FileDialog(None, "Select a Miniature to load", dir_struct["user"]+'webfiles/', wildcard="Image files (*.bmp, *.gif, *.jpg, *.png)|*.bmp;*.gif;*.jpg;*.png", style=wx.OPEN) if not dlg.ShowModal() == wx.ID_OK: dlg.Destroy() return file = open(dlg.GetPath(), "rb") imgdata = file.read() file.close() filename = dlg.GetFilename() (imgtype,j) = mimetypes.guess_type(filename) postdata = urllib.urlencode({'filename':filename, 'imgdata':imgdata, 'imgtype':imgtype}) if self.settings.get_setting('LocalorRemote') == 'Remote': # make the new mini appear in top left of current viewable map dc = wx.ClientDC(self.canvas) self.canvas.PrepareDC(dc) dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale) x = dc.DeviceToLogicalX(0) y = dc.DeviceToLogicalY(0) thread.start_new_thread(self.canvas.layers['miniatures'].upload, (postdata, dlg.GetPath()), {'pos':cmpPoint(x,y)}) else: self.CherryPit(dlg.GetDirectory(), filename) def CherryPit(self, path, filename): try: min_url = component.get("cherrypy") + filename except: return #chat.InfoPost('CherryPy is not started!') min_url = path.replace(orpg.dirpath.dir_struct["user"]+'webfiles', component.get("cherrypy")).replace(os.sep, '/') + '/' + filename # build url if min_url == "" or min_url == "http://": return if min_url[:7] != "http://": min_url = "http://" + min_url # make label if self.auto_label and min_url[-4:-3] == '.': start = min_url.rfind("/") + 1 min_label = min_url[start:len(min_url)-4] if self.use_serial: min_label = '%s %d' % ( min_label, self.canvas.layers['miniatures'].next_serial() ) else: min_label = "" if self.min_url.FindString(min_url) == -1: self.min_url.Append(min_url) try: id = 'mini-' + self.canvas.frame.session.get_next_id() # make the new mini appear in top left of current viewable map dc = wx.ClientDC(self.canvas) self.canvas.PrepareDC(dc) dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale) x = dc.DeviceToLogicalX(0) y = dc.DeviceToLogicalY(0) self.canvas.layers['miniatures'].add_miniature(id, min_url, pos=cmpPoint(x,y), label=min_label) except: # When there is an exception here, we should be decrementing the serial_number for reuse!! unablemsg= "Unable to load/resolve URL: " + min_url + " on resource \"" + min_label + "\"!!!\n\n" dlg = wx.MessageDialog(self,unablemsg, 'Url not found',wx.ICON_EXCLAMATION) dlg.ShowModal() dlg.Destroy() self.canvas.layers['miniatures'].rollback_serial() self.canvas.send_map_data() self.canvas.Refresh(False) def build_menu(self,label = "Miniature"): base_layer_handler.build_menu(self,label) self.main_menu.AppendSeparator() self.main_menu.Append(SHOW_LABELS_TOOL, "&Show Labels", '', 1) self.main_menu.Check(SHOW_LABELS_TOOL, self.canvas.layers['miniatures'].show_labels) self.main_menu.Append(AUTO_LABEL_TOOL,"&Auto label","",1) self.main_menu.Check(AUTO_LABEL_TOOL, self.auto_label) self.main_menu.Append(SERIAL_TOOL,"&Number minis","",1) self.main_menu.Check(SERIAL_TOOL, self.use_serial) self.main_menu.Append(MAP_REFRESH_MINI_URLS,"&Refresh miniatures") # Add the menu item self.main_menu.AppendSeparator() self.main_menu.Append(MIN_MOVE, "Move") self.canvas.Bind(wx.EVT_MENU, self.on_map_board_menu_item, id=MAP_REFRESH_MINI_URLS) # Set the handler self.canvas.Bind(wx.EVT_MENU, self.on_show_labels, id=SHOW_LABELS_TOOL) self.canvas.Bind(wx.EVT_MENU, self.on_auto_label, id=AUTO_LABEL_TOOL) self.canvas.Bind(wx.EVT_MENU, self.on_serial, id=SERIAL_TOOL) # build miniature meenu self.min_menu = wx.Menu() # Rectangles and hexagons require slightly different menus because of # facing and heading possibilities. heading_menu = wx.Menu() face_menu = wx.Menu() face_menu.Append(MIN_FACING_NONE,"&None") face_menu.Append(MIN_FACING_NORTH,"&North") face_menu.Append(MIN_FACING_NORTHEAST,"Northeast") face_menu.Append(MIN_FACING_EAST,"East") face_menu.Append(MIN_FACING_SOUTHEAST,"Southeast") face_menu.Append(MIN_FACING_SOUTH,"&South") face_menu.Append(MIN_FACING_SOUTHWEST,"Southwest") face_menu.Append(MIN_FACING_WEST,"West") face_menu.Append(MIN_FACING_NORTHWEST,"Northwest") heading_menu.Append(MIN_HEADING_NONE,"&None") heading_menu.Append(MIN_HEADING_NORTH,"&North") heading_menu.Append(MIN_HEADING_NORTHEAST,"Northeast") heading_menu.Append(MIN_HEADING_EAST,"East") heading_menu.Append(MIN_HEADING_SOUTHEAST,"Southeast") heading_menu.Append(MIN_HEADING_SOUTH,"&South") heading_menu.Append(MIN_HEADING_SOUTHWEST,"Southwest") heading_menu.Append(MIN_HEADING_WEST,"West") heading_menu.Append(MIN_HEADING_NORTHWEST,"Northwest") align_menu = wx.Menu() align_menu.Append(MIN_ALIGN_GRID_CENTER,"&Center") align_menu.Append(MIN_ALIGN_GRID_TL,"&Top-Left") # This is a hack to simulate a menu title, due to problem in Linux if wx.Platform == '__WXMSW__': self.min_menu.SetTitle(label) else: self.min_menu.Append(MIN_TITLE_HACK,label) self.min_menu.AppendSeparator() self.min_menu.Append(MIN_SHOW_HIDE,"Show / Hide") self.min_menu.Append(MIN_LOCK_UNLOCK, "Lock / Unlock") self.min_menu.Append(MIN_REMOVE,"&Remove") self.min_menu.Append(MIN_TO_GAMETREE,"To &Gametree") self.min_menu.AppendMenu(MIN_HEADING_SUBMENU,"Set &Heading",heading_menu) self.min_menu.AppendMenu(MIN_FACING_SUBMENU,"Set &Facing",face_menu) self.min_menu.AppendMenu(MIN_ALIGN_SUBMENU,"Snap-to &Alignment",align_menu) self.min_menu.AppendSeparator() zorder_menu = wx.Menu() zorder_menu.Append(MIN_BACK_ONE,"Back one") zorder_menu.Append(MIN_FORWARD_ONE,"Forward one") zorder_menu.Append(MIN_TO_BACK,"To back") zorder_menu.Append(MIN_TO_FRONT,"To front") zorder_menu.AppendSeparator() zorder_menu.Append(MIN_LOCK_BACK,"Lock to back") zorder_menu.Append(MIN_LOCK_FRONT,"Lock to front") zorder_menu.Append(MIN_FRONTBACK_UNLOCK,"Unlock Front/Back") self.min_menu.AppendMenu(MIN_ZORDER_SUBMENU, "Miniature Z-Order",zorder_menu) #self.min_menu.Append(MIN_LOCK,"&Lock") self.min_menu.AppendSeparator() self.min_menu.Append(MIN_PROP_DLG,"&Properties") self.min_menu.AppendSeparator() self.min_menu.Append(MIN_MOVE, "Move") self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_MOVE) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_SHOW_HIDE) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_LOCK_UNLOCK) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_REMOVE) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_TO_GAMETREE) #self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_LOCK) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_PROP_DLG) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_NONE) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_EAST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_WEST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_NORTH) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_SOUTH) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_NORTHEAST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_SOUTHEAST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_SOUTHWEST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FACING_NORTHWEST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_NONE) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_EAST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_WEST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_NORTH) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_SOUTH) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_NORTHEAST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_SOUTHEAST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_SOUTHWEST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_HEADING_NORTHWEST) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_ALIGN_GRID_CENTER) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_ALIGN_GRID_TL) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_BACK_ONE) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FORWARD_ONE) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_TO_BACK) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_TO_FRONT) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_LOCK_BACK) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_LOCK_FRONT) self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=MIN_FRONTBACK_UNLOCK) ######### add plugin added menu items ######### if len(self.mini_rclick_menu_extra_items)>0: self.min_menu.AppendSeparator() for item in self.mini_rclick_menu_extra_items.items(): self.min_menu.Append(item[1], item[0]) if len(self.background_rclick_menu_extra_items)>0: self.main_menu.AppendSeparator() for item in self.background_rclick_menu_extra_items.items(): self.main_menu.Append(item[1], item[0]) def do_min_menu(self,pos): self.canvas.PopupMenu(self.min_menu,pos) def do_min_select_menu(self, min_list, pos): # to prevent another event being processed self.lastMenuChoice = None self.min_select_menu = wx.Menu() self.min_select_menu.SetTitle("Select Miniature") loop_count = 1 try: for m in min_list: # Either use the miniatures label for the selection list if m.label: self.min_select_menu.Append(loop_count, m.label) # Or use part of the images filename as an identifier else: string_split = string.split(m.path,"/",) last_string = string_split[len(string_split)-1] self.min_select_menu.Append(loop_count, 'Unlabeled - ' + last_string[:len(last_string)-4]) self.canvas.Bind(wx.EVT_MENU, self.min_selected, id=loop_count) loop_count += 1 self.canvas.PopupMenu(self.min_select_menu,pos) except: pass def min_selected(self,evt): # this is the callback function for the menu that is used to choose # between minis when you right click, left click or left double click # on a stack of two or more self.canvas.Refresh(False) self.canvas.send_map_data() self.lastMenuChoice = evt.GetId()-1 def on_min_menu_item(self,evt): id = evt.GetId() if id == MIN_MOVE: if self.sel_min: self.moveSelectedMini(self.last_rclick_pos) self.deselectAndRefresh() return elif id == MIN_REMOVE: self.canvas.layers['miniatures'].del_miniature(self.sel_rmin) elif id == MIN_TO_GAMETREE: ### Alpha ### implements ElementTree mini = self.sel_rmin.toxml(action="new") node = Element('nodehandler') node.set('module', 'map_miniature_nodehandler') node.set('class', 'map_miniature_handler') name = self.sel_rmin.label if self.sel_rmin.label else 'Unnamed Miniature' node.set('name', name) node.append(fromstring(mini)) gametree = component.get('tree_fs') gametree.insert_xml(tostring(node)) ### ElementTree is a nice decision from Core, kudos!! ### elif id == MIN_SHOW_HIDE: if self.sel_rmin.hide: self.sel_rmin.hide = 0 else: self.sel_rmin.hide = 1 elif id == MIN_LOCK_UNLOCK: if self.sel_rmin.locked: self.sel_rmin.locked = False else: self.sel_rmin.locked = True if self.sel_rmin == self.sel_min: # when we lock / unlock the selected mini make sure it isn't still selected # or it might easily get moved by accident and be hard to move back self.sel_min.selected = False self.sel_min.isUpdated = True self.sel_min = None recycle_bin = {MIN_HEADING_NONE: FACE_NONE, MIN_HEADING_NORTH: FACE_NORTH, MIN_HEADING_NORTHWEST: FACE_NORTHWEST, MIN_HEADING_NORTHEAST: FACE_NORTHEAST, MIN_HEADING_EAST: FACE_EAST, MIN_HEADING_SOUTHEAST: FACE_SOUTHEAST, MIN_HEADING_SOUTHWEST: FACE_SOUTHWEST, MIN_HEADING_SOUTH: FACE_SOUTH, MIN_HEADING_WEST: FACE_WEST} if recycle_bin.has_key(id): self.sel_rmin.heading = recycle_bin[id] del recycle_bin recycle_bin = {MIN_FACING_NONE: FACE_NONE, MIN_FACING_NORTH: FACE_NORTH, MIN_FACING_NORTHWEST: FACE_NORTHWEST, MIN_FACING_NORTHEAST: FACE_NORTHEAST, MIN_FACING_EAST: FACE_EAST, MIN_FACING_SOUTHEAST: FACE_SOUTHEAST, MIN_FACING_SOUTHWEST: FACE_SOUTHWEST, MIN_FACING_SOUTH: FACE_SOUTH, MIN_FACING_WEST: FACE_WEST} if recycle_bin.has_key(id): self.sel_rmin.face = recycle_bin[id] del recycle_bin elif id == MIN_ALIGN_GRID_CENTER: self.sel_rmin.snap_to_align = SNAPTO_ALIGN_CENTER elif id == MIN_ALIGN_GRID_TL: self.sel_rmin.snap_to_align = SNAPTO_ALIGN_TL elif id == MIN_PROP_DLG: old_lock_value = self.sel_rmin.locked dlg = min_edit_dialog(self.canvas.frame.GetParent(),self.sel_rmin) if dlg.ShowModal() == wx.ID_OK: if self.sel_rmin == self.sel_min and self.sel_rmin.locked != old_lock_value: # when we lock / unlock the selected mini make sure it isn't still selected # or it might easily get moved by accident and be hard to move back self.sel_min.selected = False self.sel_min.isUpdated = True self.sel_min = None self.canvas.Refresh(False) self.canvas.send_map_data() return elif id == MIN_BACK_ONE: # This assumes that we always start out with a z-order # that starts at 0 and goes up to the number of # minis - 1. If this isn't the case, then execute # a self.canvas.layers['miniatures'].collapse_zorder() # before getting the oldz to test # Save the selected minis current z-order oldz = self.sel_rmin.zorder # Make sure the mini isn't sticky front or back if (oldz != MIN_STICKY_BACK) and (oldz != MIN_STICKY_FRONT): self.sel_rmin.zorder -= 1 # Re-collapse to normalize # Note: only one update (with the final values) will be sent self.canvas.layers['miniatures'].collapse_zorder() elif id == MIN_FORWARD_ONE: # This assumes that we always start out with a z-order # that starts at 0 and goes up to the number of # minis - 1. If this isn't the case, then execute # a self.canvas.layers['miniatures'].collapse_zorder() # before getting the oldz to test # Save the selected minis current z-order oldz = self.sel_rmin.zorder self.sel_rmin.zorder += 1 # Re-collapse to normalize # Note: only one update (with the final values) will be sent self.canvas.layers['miniatures'].collapse_zorder() elif id == MIN_TO_FRONT: # This assumes that we always start out with a z-order # that starts at 0 and goes up to the number of # minis - 1. If this isn't the case, then execute # a self.canvas.layers['miniatures'].collapse_zorder() # before getting the oldz to test # Save the selected minis current z-order oldz = self.sel_rmin.zorder # Make sure the mini isn't sticky front or back if (oldz != MIN_STICKY_BACK) and (oldz != MIN_STICKY_FRONT): # The new z-order will be one more than the last index newz = len(self.canvas.layers['miniatures'].miniatures) self.sel_rmin.zorder = newz # Re-collapse to normalize # Note: only one update (with the final values) will be sent self.canvas.layers['miniatures'].collapse_zorder() elif id == MIN_TO_BACK: # This assumes that we always start out with a z-order # that starts at 0 and goes up to the number of # minis - 1. If this isn't the case, then execute # a self.canvas.layers['miniatures'].collapse_zorder() # before getting the oldz to test # Save the selected minis current z-order oldz = self.sel_rmin.zorder # Make sure the mini isn't sticky front or back if (oldz != MIN_STICKY_BACK) and (oldz != MIN_STICKY_FRONT): # Since 0 is the lowest in a normalized order, be one less newz = -1 self.sel_rmin.zorder = newz # Re-collapse to normalize # Note: only one update (with the final values) will be sent self.canvas.layers['miniatures'].collapse_zorder() elif id == MIN_FRONTBACK_UNLOCK: if self.sel_rmin.zorder == MIN_STICKY_BACK: self.sel_rmin.zorder = MIN_STICKY_BACK + 1 elif self.sel_rmin.zorder == MIN_STICKY_FRONT: self.sel_rmin.zorder = MIN_STICKY_FRONT - 1 elif id == MIN_LOCK_BACK: self.sel_rmin.zorder = MIN_STICKY_BACK elif id == MIN_LOCK_FRONT: self.sel_rmin.zorder = MIN_STICKY_FRONT # Pretty much, we always want to refresh when we go through here # This helps us remove the redundant self.Refresh() on EVERY menu event # that we process above. self.sel_rmin.isUpdated = True self.canvas.Refresh(False) self.canvas.send_map_data() def on_miniature(self, evt): session = self.canvas.frame.session if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER) and (session.use_roles()): self.infoPost("You must be either a player or GM to use the miniature Layer") return min_url = self.min_url.GetValue() # build url if min_url == "" or min_url == "http://": return if min_url[:7] != "http://" : min_url = "http://" + min_url # make label if self.auto_label and min_url[-4:-3] == '.': start = min_url.rfind("/") + 1 min_label = min_url[start:len(min_url)-4] if self.use_serial: min_label = '%s %d' % ( min_label, self.canvas.layers['miniatures'].next_serial() ) else: min_label = "" if self.min_url.FindString(min_url) == -1: self.min_url.Append(min_url) try: id = 'mini-' + self.canvas.frame.session.get_next_id() # make the new mini appear in top left of current viewable map dc = wx.ClientDC(self.canvas) self.canvas.PrepareDC(dc) dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale) x = dc.DeviceToLogicalX(0) y = dc.DeviceToLogicalY(0) self.canvas.layers['miniatures'].add_miniature(id, min_url, pos=cmpPoint(x,y), label=min_label) except: # When there is an exception here, we should be decrementing the serial_number for reuse!! unablemsg= "Unable to load/resolve URL: " + min_url + " on resource \"" + min_label + "\"!!!\n\n" dlg = wx.MessageDialog(self,unablemsg, 'Url not found',wx.ICON_EXCLAMATION) dlg.ShowModal() dlg.Destroy() self.canvas.layers['miniatures'].rollback_serial() self.canvas.send_map_data() self.canvas.Refresh(False) #except Exception, e: #wx.MessageBox(str(e),"Miniature Error") def on_show_labels(self, evt): show_labels = not self.canvas.layers['miniatures'].show_labels self.canvas.layers['miniatures'].show_labels = show_labels self.canvas.Refresh() def on_auto_label(self,evt): self.auto_label = not self.auto_label #self.auto_label_cb.SetValue(self.auto_label) #self.send_map_data() #self.Refresh() def on_min_list(self,evt): session = self.canvas.frame.session if (session.my_role() != session.ROLE_GM): self.infoPost("You must be a GM to use this feature") return #d = min_list_panel(self.frame.GetParent(),self.canvas.layers,"Miniature list") d = min_list_panel(self.canvas.frame, self.canvas.layers, "Miniature list") if d.ShowModal() == wx.ID_OK: d.Destroy() self.canvas.Refresh(False) def on_serial(self, evt): self.use_serial = not self.use_serial def on_map_board_menu_item(self,evt): id = evt.GetId() if id == MAP_REFRESH_MINI_URLS: # Note: this doesn't change the mini, so no need to update the map for mini in self.canvas.layers['miniatures'].miniatures: # For all minis mini.set_bmp(ImageHandler.load(mini.path, 'miniature', mini.id)) # Reload their bmp member self.canvas.Refresh(False) #################################################################### ## old functions, changed an awful lot def on_left_down(self, evt): if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return mini = self.find_mini(evt, evt.CmdDown() and self.role_is_gm()) if mini: deselecting_selected_mini = (mini == self.sel_min) #clicked on the selected mini self.deselectAndRefresh() self.drag_mini = mini if deselecting_selected_mini: return self.sel_min = mini self.sel_min.selected = True self.canvas.Refresh() else: self.drag_mini = None pos = self.getLogicalPosition(evt) self.moveSelectedMini(pos) self.deselectAndRefresh() def on_right_down(self, evt): if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return self.last_rclick_pos = self.getLogicalPosition(evt) mini = self.find_mini(evt, evt.CmdDown() and self.role_is_gm()) if mini: self.sel_rmin = mini if self.sel_min: self.min_menu.Enable(MIN_MOVE, True) else: self.min_menu.Enable(MIN_MOVE, False) self.prepare_mini_rclick_menu(evt) self.do_min_menu(evt.GetPosition()) else:# pass it on if self.sel_min: self.main_menu.Enable(MIN_MOVE, True) else: self.main_menu.Enable(MIN_MOVE, False) self.prepare_background_rclick_menu(evt) base_layer_handler.on_right_down(self, evt) #################################################################### ## new functions def on_drop_files(self, x, y, filepaths): # currently we ignore multiple files filepath = filepaths[0] start1 = filepath.rfind("\\") + 1 # check for both slashes in path to be on the safe side start2 = filepath.rfind("/") + 1 if start1 < start2: start1 = start2 filename = filepath[start1:] pos = filename.rfind('.') ext = filename[pos:].lower() #ext = filename[-4:].lower() if(ext != ".bmp" and ext != ".gif" and ext != ".jpg" and ext != ".jpeg" and ext != ".png"): self.infoPost("Supported file extensions are: *.bmp, *.gif, *.jpg, *.jpeg, *.png") return file = open(filepath, "rb") imgdata = file.read() file.close() dc = wx.ClientDC(self.canvas) self.canvas.PrepareDC(dc) dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale) x = dc.DeviceToLogicalX(x) y = dc.DeviceToLogicalY(y) (imgtype,j) = mimetypes.guess_type(filename) postdata = urllib.urlencode({'filename':filename, 'imgdata':imgdata, 'imgtype':imgtype}) if self.settings.get_setting('LocalorRemote') == 'Remote': thread.start_new_thread(self.canvas.layers['miniatures'].upload, (postdata, filepath), {'pos':cmpPoint(x,y)}) else: self.CherryPit(filepath, '') def on_tooltip_timer(self, *args): pos = args[0] dc = wx.ClientDC(self.canvas) self.canvas.PrepareDC(dc) dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale) pos = wx.Point(dc.DeviceToLogicalX(pos.x), dc.DeviceToLogicalY(pos.y)) mini_list = self.getMiniListOrSelectedMini(pos) if len(mini_list) > 0: tooltip = mini_list[0].label #self.canvas.SetToolTipString(mini_list[0].label) #Once set, it never unsets, so it sucks. else: self.canvas.SetToolTipString('') def on_motion(self,evt): if evt.Dragging() and evt.LeftIsDown(): if self.canvas.drag is None and self.drag_mini is not None: drag_bmp = self.drag_mini.bmp if self.drag_mini.width and self.drag_mini.height: tmp_image = drag_bmp.ConvertToImage() tmp_image.ConvertAlphaToMask() drag_bmp = tmp_image.ConvertToBitmap() mask = wx.Mask(drag_bmp, wx.Colour(tmp_image.GetMaskRed(), tmp_image.GetMaskGreen(), tmp_image.GetMaskBlue())) drag_bmp.SetMask(mask) tmp_image = tmp_image.ConvertToGreyscale() self.drag_mini.gray = True self.drag_mini.isUpdated = True def refresh(): self.canvas.drag.Hide() self.canvas.Refresh(False) wx.CallAfter(refresh) self.canvas.drag = wx.DragImage(drag_bmp) self.drag_offset = self.getLogicalPosition(evt)- self.drag_mini.pos self.canvas.drag.BeginDrag((int(self.drag_offset.x * self.canvas.layers['grid'].mapscale), int(self.drag_offset.y * self.canvas.layers['grid'].mapscale)), self.canvas, False) elif self.canvas.drag is not None: self.canvas.drag.Move(evt.GetPosition()) self.canvas.drag.Show() # reset tool tip timer self.canvas.SetToolTipString('') self.tooltip_timer.Restart(self.tooltip_delay_miliseconds, evt.GetPosition()) def on_left_up(self,evt): if self.canvas.drag: self.canvas.drag.Hide() self.canvas.drag.EndDrag() self.canvas.drag = None pos = self.getLogicalPosition(evt) pos = pos - self.drag_offset if self.canvas.layers['grid'].snap: nudge = int(self.canvas.layers['grid'].unit_size/2) if self.canvas.layers['grid'].mode != GRID_ISOMETRIC: if self.drag_mini.snap_to_align == SNAPTO_ALIGN_CENTER: pos = pos + (int(self.drag_mini.bmp.GetWidth()/2),int(self.drag_mini.bmp.GetHeight()/2)) else: pos = pos + (nudge, nudge) else:# GRID_ISOMETRIC if self.drag_mini.snap_to_align == SNAPTO_ALIGN_CENTER: pos = pos + (int(self.drag_mini.bmp.GetWidth()/2), self.drag_mini.bmp.GetHeight()) else: pass # no nudge for the isomorphic / top-left self.sel_min = self.drag_mini # check to see if the mouse is inside the window still w = self.canvas.GetClientSizeTuple() # this is the window size, minus any scrollbars p = evt.GetPosition() # compare the window size, w with the non-logical position c = self.canvas.size # this is the grid size, compare with the logical position, pos # both are [width, height] if p.x>=0 and pos.x<c[0] and p.x<w[0] and p.y>=0 and pos.y<c[1] and p.y<w[1]: self.moveSelectedMini(pos) self.sel_min.gray = False self.sel_min.selected = False self.sel_min.isUpdated = True self.canvas.Refresh(False) self.canvas.send_map_data() self.sel_min = None self.drag_mini = None def on_left_dclick(self,evt): if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return mini = self.find_mini(evt, evt.CmdDown() and self.role_is_gm()) if mini: self.on_mini_dclick(evt, mini) else: base_layer_handler.on_left_dclick(self, evt) #################################################################### ## hook functions (although with python you can override any of the functions) def prepare_mini_rclick_menu(self, evt): # override the entire right-click on a mini menu pass def prepare_background_rclick_menu(self, evt): # override the entire right-click on the map menu pass def get_mini_tooltip(self, mini_list): # override to create a tooltip return "" def on_mini_dclick(self, evt, mini): # do something after the mini was left double clicked pass #################################################################### ## easy way to add a single menu item def set_mini_rclick_menu_item(self, label, callback_function): # remember you might want to call these at the end of your callback function: # mini_handler.sel_rmin.isUpdated = True # canvas.Refresh(False) # canvas.send_map_data() if callback_function == None: del self.mini_rclick_menu_extra_items[label] else: if not self.mini_rclick_menu_extra_items.has_key(label): self.mini_rclick_menu_extra_items[label]=wx.NewId() menu_id = self.mini_rclick_menu_extra_items[label] self.canvas.Bind(wx.EVT_MENU, callback_function, id=menu_id) self.build_menu() def set_background_rclick_menu_item(self, label, callback_function): if callback_function == None: del self.background_rclick_menu_extra_items[label] else: if not self.background_rclick_menu_extra_items.has_key(label): self.background_rclick_menu_extra_items[label]=wx.NewId() menu_id = self.background_rclick_menu_extra_items[label] self.canvas.Bind(wx.EVT_MENU, callback_function, id=menu_id) self.build_menu() #################################################################### ## helper functions def infoPost(self, message): component.get("chat").InfoPost(message) def role_is_gm_or_player(self): session = self.canvas.frame.session if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER) and (session.use_roles()): self.infoPost("You must be either a player or GM to use the miniature Layer") return False return True def role_is_gm(self): session = self.canvas.frame.session if (session.my_role() != session.ROLE_GM) and (session.use_roles()): return False return True def alreadyDealingWithMenu(self): return self.lastMenuChoice is not None def getLastMenuChoice(self): choice = self.lastMenuChoice self.lastMenuChoice = None return choice def getLogicalPosition(self, evt): dc = wx.ClientDC(self.canvas) self.canvas.PrepareDC(dc) dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale) pos = evt.GetLogicalPosition(dc) return pos def getMiniListOrSelectedMini(self, pos, include_locked=False): if self.sel_min and self.sel_min.hit_test(pos): # clicked on the selected mini - assume that is the intended target # and don't give a choice of it and any other minis stacked with it mini_list = [] mini_list.append(self.sel_min) return mini_list mini_list = self.canvas.layers['miniatures'].find_miniature(pos, (not include_locked)) if mini_list: return mini_list mini_list = [] return mini_list def deselectAndRefresh(self): if self.sel_min: self.sel_min.selected = False self.sel_min.isUpdated = True self.canvas.Refresh(False) self.canvas.send_map_data() self.sel_min = None def moveSelectedMini(self, pos): if self.sel_min: self.moveMini(pos, self.sel_min) def moveMini(self, pos, mini): grid = self.canvas.layers['grid'] mini.pos = grid.get_snapped_to_pos(pos, mini.snap_to_align, mini.bmp.GetWidth(), mini.bmp.GetHeight()) def find_mini(self, evt, include_locked): if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return pos = self.getLogicalPosition(evt) mini_list = self.getMiniListOrSelectedMini(pos, include_locked) mini = None if len(mini_list) > 1: try: self.do_min_select_menu(mini_list, evt.GetPosition()) except: pass choice = self.getLastMenuChoice() if choice == None: return None # left menu without making a choice, eg by clicking outside menu mini = mini_list[choice] elif len(mini_list) == 1: mini = mini_list[0] return mini