view orpg/mapper/whiteboard_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 6081bdc2b8d5
children 13054be69834
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: whiteboard_handler.py,v Traipse 'Ornery-Orc' prof.ebral Exp $
#
# Description: Whiteboard layer handler
#
__version__ = "$Id: whiteboard_handler.py,v Traipse 'Ornery-Orc' prof.ebral Exp $"

from base_handler import *
from math import floor, sqrt

class whiteboard_handler(base_layer_handler):
    def __init__(self, parent, id, canvas):
        self.drawing_mode = 'Freeform'
        self.line_string = "0,0;"
        self.drawing = False
        self.upperleft = wx.Point(0,0)
        self.lowerright = wx.Point(0,0)
        #polyline drawing vars
        self.polypoints = 0
        self.lastpoint = None
        self.selected = None
        #text drawing vars
        self.style = str(wx.NORMAL)
        self.weight = str(wx.NORMAL)
        self.pointsize = str(12)
        self.text_selected_item = None
        #self.r_h = RGBHex()
        base_layer_handler.__init__(self, parent, id, canvas)
        self.build_text_properties_menu()
        self.wb = self.canvas.layers['whiteboard']
        self.temp_circle = None
        self.cone_start = None
        self.temp_edge1 = None
        self.temp_edge2 = None

    def build_ctrls(self):
        base_layer_handler.build_ctrls(self)
        self.color_button = wx.Button(self, wx.ID_ANY, "Pen Color", style=wx.BU_EXACTFIT)
        self.color_button.SetBackgroundColour(wx.BLACK)
        self.color_button.SetForegroundColour(wx.WHITE)
        self.drawmode_ctrl = wx.Choice(self, wx.ID_ANY, choices = ["Freeform", "Polyline","Text", "Cone", "Circle"])
        self.drawmode_ctrl.SetSelection(0) #always start showing "Freeform"
        self.radius = wx.TextCtrl(self, wx.ID_ANY, size=(32,-1) )
        self.radius.SetValue("15")
        self.live_refresh = wx.CheckBox(self, wx.ID_ANY, " Live Refresh")
        self.live_refresh.SetValue(True)
        self.widthList= wx.Choice(self, wx.ID_ANY, size= wx.Size(40, 20), 
                                        choices=['1','2','3','4','5','6','7','8','9','10'])
        self.widthList.SetSelection(0)
        self.sizer.Add(wx.StaticText(self, wx.ID_ANY, "Line Width: "),0,wx.ALIGN_CENTER)
        self.sizer.Add(self.widthList, 0, wx.EXPAND)
        self.sizer.Add(wx.Size(10,25))
        self.sizer.Add(wx.StaticText(self, wx.ID_ANY, "Drawing Mode: "),0,wx.ALIGN_CENTER)
        self.sizer.Add(self.drawmode_ctrl, 0, wx.EXPAND)
        self.sizer.Add(wx.StaticText(self, -1, " Radius: "), 0, wx.ALIGN_CENTER|wx.ALL, 3)
        self.sizer.Add(self.radius, 0, wx.EXPAND|wx.ALL, 2)
        self.sizer.Add(wx.Size(10,25))
        self.sizer.Add(self.live_refresh, 0, wx.EXPAND)
        self.sizer.Add(wx.Size(20,25))
        self.sizer.Add(self.color_button, 0, wx.EXPAND)
        self.sizer.Add(wx.Size(20,25))
        self.Bind(wx.EVT_MOTION, self.on_motion)
        self.Bind(wx.EVT_CHOICE, self.check_draw_mode, self.drawmode_ctrl)
        self.Bind(wx.EVT_BUTTON, self.on_pen_color, self.color_button)
        self.Bind(wx.EVT_CHOICE, self.on_pen_width, self.widthList)

    def build_text_properties_menu(self, label="Text Properties"):
        self.text_properties_dialog = wx.Dialog(self, -1, "Text Properties",  name = "Text Properties")
        self.text_props_sizer = wx.BoxSizer(wx.VERTICAL)
        okay_boxer = wx.BoxSizer(wx.HORIZONTAL)
        okay_button = wx.Button(self.text_properties_dialog, wx.ID_OK, "APPLY")
        cancel_button = wx.Button(self.text_properties_dialog, wx.ID_CANCEL,"CANCEL")
        okay_boxer.Add(okay_button, 1, wx.ALIGN_LEFT)
        okay_boxer.Add(wx.Size(10,10))
        okay_boxer.Add(cancel_button, 1, wx.ALIGN_RIGHT)
        self.txt_boxer = wx.BoxSizer(wx.HORIZONTAL)
        self.txt_static = wx.StaticText(self.text_properties_dialog, -1, "Text: ")
        self.text_control = wx.TextCtrl(self.text_properties_dialog, wx.ID_ANY, "", name = "Text: ")
        self.txt_boxer.Add(self.txt_static,0,wx.EXPAND)
        self.txt_boxer.Add(wx.Size(10,10))
        self.txt_boxer.Add(self.text_control,1,wx.EXPAND)
        self.point_boxer = wx.BoxSizer(wx.HORIZONTAL)
        self.point_static = wx.StaticText(self.text_properties_dialog, -1, "Text Size: ")
        self.point_control = wx.SpinCtrl(self.text_properties_dialog, 
                                        wx.ID_ANY, value = "12",
                                        min = 1, initial = 12, 
                                        name = "Font Size: ")
        self.point_boxer.Add(self.point_static,1,wx.EXPAND)
        self.point_boxer.Add(wx.Size(10,10))
        self.point_boxer.Add(self.point_control,0,wx.EXPAND)
        self.text_color_control = wx.Button(self.text_properties_dialog, wx.ID_ANY, "TEXT COLOR",style=wx.BU_EXACTFIT)
        self.weight_control = wx.RadioBox(self.text_properties_dialog, wx.ID_ANY, "Weight", choices = ["Normal","Bold"])
        self.style_control = wx.RadioBox(self.text_properties_dialog, wx.ID_ANY, "Style", choices = ["Normal", "Italic"])
        self.text_props_sizer.Add(self.txt_boxer,0,wx.EXPAND)
        self.text_props_sizer.Add(self.point_boxer,0, wx.EXPAND)
        self.text_props_sizer.Add(self.weight_control,0, wx.EXPAND)
        self.text_props_sizer.Add(self.style_control,0, wx.EXPAND)
        self.text_props_sizer.Add(self.text_color_control, 0, wx.EXPAND)
        self.text_props_sizer.Add(wx.Size(10,10))
        self.text_props_sizer.Add(okay_boxer,0, wx.EXPAND)
        self.text_props_sizer.Fit(self)
        self.text_properties_dialog.SetSizer(self.text_props_sizer)
        self.text_properties_dialog.Fit()
        self.text_properties_dialog.Bind(wx.EVT_BUTTON, self.on_text_color, self.text_color_control)
        self.text_properties_dialog.Bind(wx.EVT_BUTTON, self.on_text_properties, okay_button)
        #self.text_properties_dialog.Destroy()

    def build_menu(self, label = "Whiteboard"):
        base_layer_handler.build_menu(self,label)
        self.main_menu.AppendSeparator()
        item = wx.MenuItem(self.main_menu, wx.ID_ANY, "&Change Pen Color", "Change Pen Color")
        self.canvas.Bind(wx.EVT_MENU, self.on_pen_color, item)
        self.main_menu.AppendItem(item)
        item = wx.MenuItem(self.main_menu, wx.ID_ANY, "Delete &All Lines", "Delete All Lines")
        self.canvas.Bind(wx.EVT_MENU, self.delete_all_lines, item)
        self.main_menu.AppendItem(item)
        item = wx.MenuItem(self.main_menu, wx.ID_ANY, "&Undo Last Deleted Line", "Undo Last Deleted Line")
        self.canvas.Bind(wx.EVT_MENU, self.undo_line, item)
        self.main_menu.AppendItem(item)
        self.line_menu = wx.Menu("Whiteboard Line")
        self.line_menu.SetTitle("Whiteboard Line")
        item = wx.MenuItem(self.line_menu, wx.ID_ANY, "&Remove", "Remove")
        self.canvas.Bind(wx.EVT_MENU, self.on_line_menu_item, item)
        self.line_menu.AppendItem(item)
        self.text_menu = wx.Menu("Whiteboard Text")
        self.text_menu.SetTitle("Whiteboard Text")
        item = wx.MenuItem(self.text_menu, wx.ID_ANY, "&Properties", "Properties")
        self.canvas.Bind(wx.EVT_MENU, self.get_text_properties, item)
        self.text_menu.AppendItem(item)
        item = wx.MenuItem(self.text_menu, wx.ID_ANY, "&Remove", "Remove")
        self.canvas.Bind(wx.EVT_MENU, self.on_text_menu_item, item)
        self.text_menu.AppendItem(item)

    def do_line_menu(self,pos):
        self.canvas.PopupMenu(self.line_menu, pos)

    def item_selected(self,evt):
        item = evt.GetId()
        self.item_selection = self.selection_list[item]

    def on_text_properties(self,evt):
        text_string = self.text_control.GetValue()
        if self.style_control.GetStringSelection() == 'Normal': style = wx.NORMAL
        else: style = wx.ITALIC
        if self.weight_control.GetStringSelection() == 'Normal': weight = wx.NORMAL
        else: weight = wx.BOLD
        point = str(self.point_control.GetValue())
        c = self.text_color_control.GetForegroundColour()
        color = self.canvas.layers['whiteboard'].r_h.hexstring(c.Red(), c.Green(), c.Blue())
        self.text_selected_item.set_text_props(text_string, style, point, weight, color)
        self.text_to_xml()
        self.text_properties_dialog.Show(False)
        self.text_selected_item.selected = False
        self.text_selected_item.isUpdated = True
        self.text_selected_item = None

    def on_text_color(self,evt):
        dlg = wx.ColourDialog(self)
        if dlg.ShowModal() == wx.ID_OK:
            c = dlg.GetColourData()
            self.text_color_control.SetForegroundColour(c.GetColour())
        dlg.Destroy()

    def text_to_xml(self):
        xml_str = "<map><whiteboard>"
        xml_str += self.text_selected_item.toxml('update')
        xml_str += "</whiteboard></map>"
        self.canvas.frame.session.send(xml_str)
        self.canvas.Refresh(False)

    def get_text_properties(self, event=None):
        self.text_color_control.SetForegroundColour(self.text_selected_item.textcolor)
        self.text_control.SetValue(self.text_selected_item.text_string)
        self.point_control.SetValue(int(self.text_selected_item.pointsize))
        if int(self.text_selected_item.weight) == wx.NORMAL: self.weight_control.SetSelection(0)
        else: self.weight_control.SetSelection(1)

        if int(self.text_selected_item.style) == wx.NORMAL: self.style_control.SetSelection(0)
        else: self.style_control.SetSelection(1)
        self.text_properties_dialog.Center()
        self.text_properties_dialog.Show(True)

    def do_text_menu(self, pos, items=None):
        if items == None: self.canvas.PopupMenu(self.text_menu)
        else:
            menu = wx.Menu()
            self.ItemList = items
            self.tmpPos = pos
            for i in xrange(len(items)):
                menu.Append(i+1, items[i].text_string)
                self.canvas.Bind(wx.EVT_MENU, self.onItemSelect, id=i+1)
            self.canvas.PopupMenu(menu)
        return

    def onItemSelect(self, evt):
        id = evt.GetId()-1
        self.text_selected_item = self.ItemList[id]
        self.text_selected_item.selected = True
        if self.tmpPos == 'right': self.canvas.PopupMenu(self.text_menu)
        self.ItemList = None
        self.tmpPos = None

    def on_right_down(self,evt):
        line = 0
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC(self.canvas)
        self.canvas.PrepareDC(dc)
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)
        if self.drawing_mode == 'Text': self.on_text_right_down(evt, dc)
        elif self.drawing and ((self.drawing_mode == 'Circle') or (self.drawing_mode == 'Cone')):
            self.check_draw_mode()
            self.drawing = False
        elif (self.drawing_mode == 'Freeform') or (self.drawing_mode == 'Polyline') or (self.drawing_mode == 'Circle') or (self.drawing_mode == 'Cone'):
            line_list = self.canvas.layers['whiteboard'].find_line(pos)
            if line_list:
                self.sel_rline = self.canvas.layers['whiteboard'].get_line_by_id(line_list.id)
                if self.sel_rline:
                    self.do_line_menu(evt.GetPosition())
                    self.canvas.Refresh(False)
            else: base_layer_handler.on_right_down(self,evt)
        else: base_layer_handler.on_right_down(self,evt)
        del dc

    def on_pen_color(self,evt):
        data = wx.ColourData()
        data.SetChooseFull(True)
        dlg = wx.ColourDialog(self.canvas, data)
        if dlg.ShowModal() == wx.ID_OK:
            data = dlg.GetColourData()
            color = data.GetColour()
            self.canvas.layers['whiteboard'].setcolor(color)
            self.color_button.SetBackgroundColour(color)
        dlg.Destroy()

    def on_pen_width(self,evt):
        width = int(self.widthList.GetStringSelection())
        self.canvas.layers['whiteboard'].setwidth(width)

    def undo_line(self,evt):
        session = self.canvas.frame.session
        if (session.my_role() != session.ROLE_GM) and (session.use_roles()):
            self.top_frame.openrpg.get("chat").InfoPost("You must be a GM to use this feature")
            return
        self.canvas.layers['whiteboard'].undo_line()
        dc = self.create_dc()
        self.un_highlight(dc)
        self.selected = None
        del dc  

    def delete_all_lines(self,evt):
        session = self.canvas.frame.session
        if (session.my_role() != session.ROLE_GM) and (session.use_roles()):
            component.get("chat").InfoPost("You must be a GM to use this feature")
            return
        dlg = wx.MessageDialog(self, 
                                "Are you sure you want to delete all lines?","Delete All Lines", 
                                wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
        if dlg.ShowModal() != wx.ID_YES: return
        self.canvas.layers['whiteboard'].del_all_lines()
        dc = self.create_dc()
        self.un_highlight(dc)
        self.selected = None
        del dc

    def on_line_menu_item(self, evt):
        dc = self.create_dc()
        self.un_highlight(dc)
        self.canvas.layers['whiteboard'].del_line(self.sel_rline)
        self.selected = None
        del dc

    def on_text_menu_item(self, evt):
        dc = self.create_dc()
        self.un_highlight(dc)
        self.canvas.layers['whiteboard'].del_text(self.selected)
        self.selected = None
        del dc

    # Check Draw Mode
    # Queries the GUI to see what mode to draw in
    # 05-09-2003 Snowdog

    def check_draw_mode(self, evt=None):
        self.drawing_mode = self.drawmode_ctrl.GetStringSelection()

        #because mode can be changed while a polyline is being created
        #clear the current linestring and reset the polyline data
        self.upperleft.x = self.upperleft.y = 0
        self.lowerright.x = self.lowerright.y = 0
        self.lastpoint = None #because the end check function is not called we must force its lastpoint var to None again
        self.polypoints = 0
        self.line_string = "0,0;"
        if self.temp_circle:
            self.canvas.layers['whiteboard'].del_temp_line(self.temp_circle)
            if self.selected == self.temp_circle: self.selected = None
            self.canvas.Refresh(True)
        self.temp_circle = None
        self.cone_start  = None

    # Altered on_left_up to toggle between
    # drawing modes freeform vs polyline
    # 05-09-2003  Snowdog
    def on_left_down(self,evt):
        if not self.drawing: self.check_draw_mode()
        if self.drawing_mode == 'Freeform': pass
        elif self.drawing_mode == 'Polyline': self.polyline_add_point( evt )
        elif self.drawing_mode == 'Text': self.on_text_left_down(evt)
        elif self.drawing_mode == 'Cone':
            if self.cone_start == None: self.on_start_cone(evt)
            else: self.draw_temporary_cone(evt)
        elif self.drawing_mode == 'Circle': self.draw_temporary_circle(evt)

    # Added handling for double clicks within the map
    # 05-09-2003  Snowdog
    def on_left_dclick(self, evt):
        if self.drawing_mode == 'Freeform': pass #Freeform mode ignores the double click
        elif self.drawing_mode == 'Polyline': self.polyline_last_point( evt )
        elif self.drawing_mode == 'Text': pass
        elif self.drawing_mode == 'Circle' or self.drawing_mode == 'Cone':
            self.canvas.layers['whiteboard'].del_temp_line(self.temp_circle)
            #pointArray = self.temp_circle.line_string.split(";")
            self.canvas.layers['whiteboard'].add_line(self.temp_circle.line_string)
            self.temp_circle = None
            self.cone_start  = None
            self.drawing = False

    # Altered on_left_up to toggle between
    # drawing modes freeform vs polyline
    # 05-09-2003  Snowdog
    def on_left_up(self,evt):
        if self.drawing_mode == 'Freeform': self.on_freeform_left_up(evt)
        elif self.drawing_mode == 'Polyline': pass
        elif self.drawing_mode == 'Text': pass

    # Altered on_left_up to toggle between
    # drawing modes freeform vs polyline
    # 05-09-2003  Snowdog
    def on_motion(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()):
            return
        if self.drawing_mode == 'Freeform':
            if evt.m_leftDown: self.freeform_motion(evt)
        elif self.drawing_mode == 'Polyline':
            if self.drawing: self.polyline_preview( evt )
        dc = self.create_dc()
        pos = evt.GetLogicalPosition(dc)
        hit = self.canvas.layers['whiteboard'].hit_test_lines(pos,dc)
        if hit: self.highlight(hit,dc)
        else:
            self.un_highlight(dc)
            hit = self.canvas.layers['whiteboard'].hit_test_text(pos,dc)
            if hit: self.highlight(hit,dc)
            else: self.un_highlight(dc)
        del dc

    def create_dc(self):
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        return dc

    def highlight(self,hit,dc):
        if self.selected:
            self.selected.highlight(False)
            self.selected.draw(self.wb,dc)
        self.selected = hit[0]
        self.selected.highlight()
        self.selected.draw(self.wb,dc)

    def un_highlight(self,dc):
        if self.selected:
            self.selected.highlight(False)
            self.selected.draw(self.wb,dc)

    # Polyline Add Point
    # adds a new point to the polyline
    # 05-09-2003  Snowdog
    def polyline_add_point(self, evt):
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)
        #reset the bounding points
        if pos.x < self.upperleft.x: self.upperleft.x = pos.x
        elif pos.x > self.lowerright.x: self.lowerright.x = pos.x
        if pos.y < self.upperleft.y: self.upperleft.y = pos.y
        elif pos.y > self.lowerright.y: self.lowerright.y = pos.y

        #if this point doens't end the line
        #add a new point into the line string
        if not self.polyline_end_check( pos ):
            if self.drawing == True:
                self.polypoints += 1 #add one to the point counter.
                self.line_string += `pos.x` + "," + `pos.y` + ";"
                self.canvas.layers['whiteboard'].draw_working_line(dc,self.line_string)
            else: #start of line...
                self.polypoints += 1 #add one to the point counter.
                self.line_string = `pos.x` + "," + `pos.y` + ";"
                self.upperleft.x = pos.x
                self.upperleft.y = pos.y
                self.lowerright.x = pos.x
                self.lowerright.y = pos.y
                self.drawing = True
        else: #end of line. Send and reset vars for next line
            self.drawing = False
            if self.polypoints < 2: pass
            else:
                #have enough points to create valid line
                #check to role to make sure user can draw at all....
                session = self.canvas.frame.session
                if (session.my_role() != session.ROLE_GM) and (session.my_role()!=session.ROLE_PLAYER) and (session.use_roles()):
                    component.get("chat").InfoPost("You must be either a player or GM to use this feature")
                    self.canvas.Refresh(False)
                else: line = self.canvas.layers['whiteboard'].add_line(self.line_string,self.upperleft,self.lowerright)
            #resetting variables for next line
            self.upperleft.x = self.upperleft.y = 0
            self.lowerright.x = self.lowerright.y = 0
            self.polypoints = 0
            self.line_string = "0,0;"

    # Polyline Last Point
    # adds a final point to the polyline and ends it
    # 05-09-2003  Snowdog
    def polyline_last_point(self, evt):
        #if we haven't started a line already. Ignore the click
        if self.drawing != True: return
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)
        #reset the bounding points
        if pos.x < self.upperleft.x: self.upperleft.x = pos.x
        elif pos.x > self.lowerright.x: self.lowerright.x = pos.x
        if pos.y < self.upperleft.y: self.upperleft.y = pos.y
        elif pos.y > self.lowerright.y: self.lowerright.y = pos.y
        self.polypoints += 1 #add one to the point counter.
        self.line_string += `pos.x` + "," + `pos.y` + ";"
        self.canvas.layers['whiteboard'].draw_working_line(dc,self.line_string)
        #end of line. Send and reset vars for next line
        self.drawing = False
        if self.polypoints < 2: pass
        else:
            #have enough points to create valid line
            #check to role to make sure user can draw at all....
            session = self.canvas.frame.session
            if (session.my_role() != session.ROLE_GM) and (session.my_role()!=session.ROLE_PLAYER) and (session.use_roles()):
                component.get("chat").InfoPost("You must be either a player or GM to use this feature")
                self.canvas.Refresh(False)
            else: line = self.canvas.layers['whiteboard'].add_line(self.line_string,self.upperleft,self.lowerright)
        #resetting variables for next line
        self.upperleft.x = self.upperleft.y = 0
        self.lowerright.x = self.lowerright.y = 0
        self.lastpoint = None #becuase the end check function is not called we must force its lastpoint var to None again
        self.polypoints = 0
        self.line_string = "0,0;"

    # Polyline End Check
    # checks to see if a double click has occured
    # a second click on the LAST polyline point
    # (or very close proximity) should cause the
    # polyline even to complete and send
    # 05-09-2003  Snowdog
    def polyline_end_check(self, pos):
        # check to see if the position of the give point is within POLYLINE_END_TOLERANCE
        # if it is then the line has been completed and should be sent to the map just like
        # the original freeform version is. A line with fewer than 2 points should be ignored
        x_in = y_in = 0
        tol = 5

        #first point check
        if type(self.lastpoint) == type(None): self.lastpoint = wx.Point(pos.x,pos.y); return 0 #not end of line
        if ((self.lastpoint.x -tol) <= pos.x <= (self.lastpoint.x)): x_in = 1
        if ((self.lastpoint.y -tol) <= pos.y <= (self.lastpoint.y)): y_in = 1
        if x_in and y_in: self.lastpoint = None; return 1
        #if we've reached here the point is NOT a terminal point. Reset the lastpoint and return False
        self.lastpoint.x = pos.x
        self.lastpoint.y = pos.y
        return 0

    # Polyline Preview
    # display a temporary/momentary line to the user
    # from the last point to mouse position
    # 05-09-2003  Snowdog
    def polyline_preview(self, evt):
        if self.drawing != True: return
        if self.live_refresh.GetValue() == 0: return
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)

        #reset the bounding points
        if pos.x < self.upperleft.x: self.upperleft.x = pos.x
        elif pos.x > self.lowerright.x: self.lowerright.x = pos.x
        if pos.y < self.upperleft.y: self.upperleft.y = pos.y
        elif pos.y > self.lowerright.y: self.lowerright.y = pos.y

        #redraw the line with a line connected to the cursor
        temp_string = self.line_string
        temp_string += `pos.x` + "," + `pos.y` + ";"
        self.canvas.layers['whiteboard'].draw_working_line(dc,temp_string)
        self.canvas.Refresh(True)

    # moved original on_motion to this function
    # to allow alternate drawing method to be used
    # 05-09-2003  Snowdog
    def freeform_motion(self, evt):
        #if not self.drawing:
        #    return
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)
        if pos.x < self.upperleft.x: self.upperleft.x = pos.x
        elif pos.x > self.lowerright.x: self.lowerright.x = pos.x
        if pos.y < self.upperleft.y: self.upperleft.y = pos.y
        elif pos.y > self.lowerright.y: self.lowerright.y = pos.y
        if evt.m_leftDown:
            if self.drawing == True:
                self.line_string += `pos.x` + "," + `pos.y` + ";"
                self.canvas.layers['whiteboard'].draw_working_line(dc,self.line_string)
            else:
                self.line_string = `pos.x` + "," + `pos.y` + ";"
                self.upperleft.x = pos.x
                self.upperleft.y = pos.y
                self.lowerright.x = pos.x
                self.lowerright.y = pos.y
            self.drawing = True
        del dc

    # moved original on_left_up to this function
    # to allow alternate drawing method to be used
    # 05-09-2003  Snowdog
    def on_freeform_left_up(self,evt):
        if self.drawing == True:
            self.drawing = False
            session = self.canvas.frame.session
            if (session.my_role() != session.ROLE_GM) and (session.my_role()!=session.ROLE_PLAYER) and (session.use_roles()):
                component.get("chat").InfoPost("You must be either a player or GM to use this feature")
                self.canvas.Refresh(False)
                return
            line = self.canvas.layers['whiteboard'].add_line(self.line_string,self.upperleft,self.lowerright)
            dc = self.create_dc()
            for m in range(30):
                line.highlight()
                line.draw(self.wb,dc)
                line.highlight(False)
                line.draw(self.wb,dc)
            del dc
            self.upperleft.x = self.upperleft.y = 0
            self.lowerright.x = self.lowerright.y = 0

    def on_text_left_down(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()):
            component.get("chat").InfoPost("You must be either a player or GM to use this feature")
            self.canvas.Refresh(False)
            return
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)
        test_text = self.canvas.layers['whiteboard'].hit_test_text(pos,dc)
        if len(test_text) > 0:
            if len(test_text) > 1: self.do_text_menu('left', test_text)
            else:
                self.text_selected_item = test_text[0]
                self.text_selected_item.selected = True
            self.canvas.Refresh(True)
        else:
            if self.text_selected_item == None:
                dlg = wx.TextEntryDialog(self,"Text to add to whiteboard", caption="Enter text",defaultValue=" ")
                if dlg.ShowModal() == wx.ID_OK:
                    text_string = dlg.GetValue()
                    self.canvas.layers['whiteboard'].add_text(text_string,pos, self.style, 
                        self.pointsize, self.weight, self.canvas.layers['whiteboard'].color)
            else:
                self.text_selected_item.posx = pos.x
                self.text_selected_item.posy = pos.y
                self.text_selected_item.isUpdated = True
                self.text_to_xml()
                self.text_selected_item.selected = False
                self.text_selected_item = None
        del dc

    def on_text_right_down(self, evt, dc):
        session = self.canvas.frame.session
        if (session.my_role() != session.ROLE_GM) and (session.my_role()!=session.ROLE_PLAYER) and (session.use_roles()):
            component.get("chat").InfoPost("You must be either a player or GM to use this feature")
            self.canvas.Refresh(False)
            return
        pos = evt.GetLogicalPosition(dc)
        test_text = self.canvas.layers['whiteboard'].hit_test_text(pos, dc)
        if len(test_text) > 0:
            if len(test_text) > 1: self.do_text_menu('right', test_text)
            else:
                self.text_selected_item = test_text[0]
                self.do_text_menu('right')

    def on_start_cone(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()):
            component.get("chat").InfoPost("You must be either a player or GM to use this feature")
            self.canvas.Refresh(False)
            return
        self.cone_start = self.get_snapped_to_logical_pos(evt)
        self.drawing = True

    def get_snapped_to_logical_pos(self, evt):
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)
        if self.canvas.layers['grid'].snap:
            size = self.canvas.layers['grid'].unit_size
            pos.x = int((pos.x+size/2)/size)*size
            pos.y = int((pos.y+size/2)/size)*size
        return pos

    def draw_temporary_cone(self, evt):
        scale = self.canvas.layers['grid'].mapscale
        dc = wx.ClientDC( self.canvas )
        self.canvas.PrepareDC( dc )
        dc.SetUserScale(scale,scale)
        pos = evt.GetLogicalPosition(dc)
        pos2 = self.get_snapped_to_logical_pos(evt)
        size = self.canvas.layers['grid'].unit_size #60
        if abs(pos.x-pos2.x)<=size/10 and abs(pos.y-pos2.y)<=size/10: pos = pos2
        radius = int(int(self.radius.GetValue())/5) 
        curve  = self.calculate_circle(self.cone_start, radius, size)
        edge1 = []
        edge2 = []
        horizontal_inc = wx.Point(size,0)
        if pos.x <= self.cone_start.x: horizontal_inc = wx.Point(-size,0)
        vertical_inc = wx.Point(0,size)
        if pos.y <= self.cone_start.y: vertical_inc = wx.Point(0,-size)
        x_diff = float(pos.x - self.cone_start.x)
        y_diff = float(pos.y - self.cone_start.y)
        ratio = float(1)
        if abs(x_diff) <= abs(y_diff): ratio = x_diff / y_diff
        elif abs(y_diff) < abs(x_diff):
            ratio = -(y_diff / x_diff)
            horizontal_inc,vertical_inc = vertical_inc,horizontal_inc #swap
        horizontal_rotated = wx.Point(-horizontal_inc.y, horizontal_inc.x)
        vertical_rotated   = wx.Point(-vertical_inc.y,   vertical_inc.x)
        on_diagonal = True
        for v in range(radius):
            x = int(floor((v+1)*ratio)) - int(floor(v*ratio))
            if x < 0 and on_diagonal:
                edge1 += [vertical_inc]
                edge1 += [horizontal_inc]
            elif x != 0:
                edge1 += [horizontal_inc]
                edge1 += [vertical_inc]
            else:
                edge1 += [vertical_inc]
                on_diagonal = False
        on_diagonal = True
        for v in range(radius):
            x = int(floor((v+1)*(-ratio))) - int(floor(v*(-ratio)))
            if x < 0 and on_diagonal:
                edge2 += [vertical_rotated]
                edge2 += [horizontal_rotated]
            elif x != 0:
                edge2 += [horizontal_rotated]
                edge2 += [vertical_rotated]
            else:
                edge2 += [vertical_rotated]
                on_diagonal = False
        p = wx.Point(0,0)
        string1 =  self.point_to_string(p, [self.cone_start])
        string1 += self.point_to_string(p, edge1)
        p = wx.Point(0,0)
        string2 =  self.point_to_string(p, [self.cone_start])
        string2 += self.point_to_string(p, edge2)

        # truncate the edges where they meet the curve
        pointArray = string1.split(";")
        string1 = ""
        for p in pointArray:
            p += ";"
            index = (";"+curve).find(";"+p)
            if index >= 0:
                # found intersection, start circle at intersection
                curve = curve[index:]+curve[:index]
                break
            string1 += p

        # truncate the edges where they meet the curve
        pointArray = string2.split(";")
        string2 = ""
        for p in pointArray:
            p += ";"
            string2 = p + string2 #backwards
            index = (";"+curve).find(";"+p)
            if index >= 0:
                # found intersection, end circle at intersection
                curve = curve[:index]
                break
        curve = string1 + curve + string2

        # add the lines that define the real cone edges
        sz = sqrt(x_diff*x_diff + y_diff*y_diff)
        x_diff = radius*size*x_diff/sz
        y_diff = radius*size*y_diff/sz
        pos = wx.Point(self.cone_start.x+x_diff, self.cone_start.y+y_diff)
        qos = wx.Point(self.cone_start.x-y_diff, self.cone_start.y+x_diff)# 90 degree rotation
        curve = `pos.x`+","+`pos.y`+";" + curve + `qos.x`+","+`qos.y`+";"
        if(self.temp_circle):
            self.canvas.layers['whiteboard'].del_temp_line(self.temp_circle)
            if self.selected == self.temp_circle: self.selected = None
        self.temp_circle = self.canvas.layers['whiteboard'].add_temp_line(curve)
        self.canvas.Refresh(True)

    def draw_temporary_circle(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()):
            component.get("chat").InfoPost("You must be either a player or GM to use this feature")
            self.canvas.Refresh(False)
            return
        pos = self.get_snapped_to_logical_pos(evt)
        size = self.canvas.layers['grid'].unit_size #60
        radius = int(int(self.radius.GetValue())/5)
        center = wx.Point(pos.x, pos.y+size*radius)
        curve  = self.calculate_circle(center, radius, size)
        if(self.temp_circle):
            self.canvas.layers['whiteboard'].del_temp_line(self.temp_circle)
            self.selected = None
        self.temp_circle = self.canvas.layers['whiteboard'].add_temp_line(curve)
        self.drawing = True
        self.canvas.Refresh(True)

    def calculate_circle(self, center, radius, size):
        pos = wx.Point(center.x, center.y-size*radius)
        r = int(radius+2/3)
        right = wx.Point(size,0)
        left  = wx.Point(-size,0)
        up    = wx.Point(0,-size)
        down  = wx.Point(0,size)
        v1 = ([right, down, right]*r)[:radius]
        v2 = ([down, right, down]*r)[r*3-radius:]
        v3 = ([down, left, down]*r)[:radius]
        v4 = ([left, down, left]*r)[r*3-radius:]
        v5 = ([left, up, left]*r)[:radius]
        v6 = ([up, left, up]*r)[r*3-radius:]
        v7 = ([up, right, up]*r)[:radius]
        v8 = ([right, up, right]*r)[r*3-radius:]
        p = wx.Point(0,0)
        temp_string =  self.point_to_string(p, [pos])
        temp_string += self.point_to_string(p, v1+v2+v3+v4+v5+v6+v7+v8)
        return temp_string
        
    def point_to_string(self, pos, vec):
        str = ""
        for i in range(len(vec)):
            pos.x = pos.x + vec[i].x
            pos.y = pos.y + vec[i].y
            str += `pos.x` + "," + `pos.y` + ";"
        return str