| 1 | #---------------------------------------------------------------------------- |
| 2 | # Name: GridColMover.py |
| 3 | # Purpose: Grid Column Mover Extension |
| 4 | # |
| 5 | # Author: Gerrit van Dyk (email: gerritvd@decillion.net) |
| 6 | # |
| 7 | # Version 0.1 |
| 8 | # Date: Nov 19, 2002 |
| 9 | # RCS-ID: $Id$ |
| 10 | # Licence: wxWindows license |
| 11 | #---------------------------------------------------------------------------- |
| 12 | # 12/07/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
| 13 | # |
| 14 | # o 2.5 Compatability changes |
| 15 | # |
| 16 | # 12/18/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
| 17 | # |
| 18 | # o wxGridColMoveEvent -> GridColMoveEvent |
| 19 | # o wxGridRowMoveEvent -> GridRowMoveEvent |
| 20 | # o wxGridColMover -> GridColMover |
| 21 | # o wxGridRowMover -> GridRowMover |
| 22 | # |
| 23 | |
| 24 | |
| 25 | import wx |
| 26 | import wx.grid |
| 27 | |
| 28 | #---------------------------------------------------------------------------- |
| 29 | # event class and macros |
| 30 | # |
| 31 | # New style 12/7/03 |
| 32 | # |
| 33 | |
| 34 | wxEVT_COMMAND_GRID_COL_MOVE = wx.NewEventType() |
| 35 | wxEVT_COMMAND_GRID_ROW_MOVE = wx.NewEventType() |
| 36 | |
| 37 | EVT_GRID_COL_MOVE = wx.PyEventBinder(wxEVT_COMMAND_GRID_COL_MOVE, 1) |
| 38 | EVT_GRID_ROW_MOVE = wx.PyEventBinder(wxEVT_COMMAND_GRID_ROW_MOVE, 1) |
| 39 | |
| 40 | #---------------------------------------------------------------------------- |
| 41 | |
| 42 | class GridColMoveEvent(wx.PyCommandEvent): |
| 43 | def __init__(self, id, dCol, bCol): |
| 44 | wx.PyCommandEvent.__init__(self, id = id) |
| 45 | self.SetEventType(wxEVT_COMMAND_GRID_COL_MOVE) |
| 46 | self.moveColumn = dCol |
| 47 | self.beforeColumn = bCol |
| 48 | |
| 49 | def GetMoveColumn(self): |
| 50 | return self.moveColumn |
| 51 | |
| 52 | def GetBeforeColumn(self): |
| 53 | return self.beforeColumn |
| 54 | |
| 55 | |
| 56 | class GridRowMoveEvent(wx.PyCommandEvent): |
| 57 | def __init__(self, id, dRow, bRow): |
| 58 | wx.PyCommandEvent.__init__(self,id = id) |
| 59 | self.SetEventType(wxEVT_COMMAND_GRID_ROW_MOVE) |
| 60 | self.moveRow = dRow |
| 61 | self.beforeRow = bRow |
| 62 | |
| 63 | def GetMoveRow(self): |
| 64 | return self.moveRow |
| 65 | |
| 66 | def GetBeforeRow(self): |
| 67 | return self.beforeRow |
| 68 | |
| 69 | |
| 70 | #---------------------------------------------------------------------------- |
| 71 | # graft new methods into the wxGrid class |
| 72 | |
| 73 | def _ColToRect(self,col): |
| 74 | if self.GetNumberRows() > 0: |
| 75 | rect = self.CellToRect(0,col) |
| 76 | else: |
| 77 | rect = wx.Rect() |
| 78 | rect.height = self.GetColLabelSize() |
| 79 | rect.width = self.GetColSize(col) |
| 80 | |
| 81 | for cCol in range(0,col): |
| 82 | rect.x += self.GetColSize(cCol) |
| 83 | |
| 84 | rect.y = self.GetGridColLabelWindow().GetPosition()[1] |
| 85 | return rect |
| 86 | |
| 87 | wx.grid.Grid.ColToRect = _ColToRect |
| 88 | |
| 89 | |
| 90 | def _RowToRect(self,row): |
| 91 | if self.GetNumberCols() > 0: |
| 92 | rect = self.CellToRect(row,0) |
| 93 | else: |
| 94 | rect = wx.Rect() |
| 95 | rect.width = self.GetRowLabelSize() |
| 96 | rect.height = self.GetRowSize(row) |
| 97 | |
| 98 | for cRow in range(0,row): |
| 99 | rect.y += self.GetRowSize(cRow) |
| 100 | |
| 101 | rect.x = self.GetGridRowLabelWindow().GetPosition()[0] |
| 102 | return rect |
| 103 | |
| 104 | wx.grid.Grid.RowToRect = _RowToRect |
| 105 | |
| 106 | |
| 107 | #---------------------------------------------------------------------------- |
| 108 | |
| 109 | class ColDragWindow(wx.Window): |
| 110 | def __init__(self,parent,image,dragCol): |
| 111 | wx.Window.__init__(self,parent,-1, style=wx.SIMPLE_BORDER) |
| 112 | self.image = image |
| 113 | self.SetSize((self.image.GetWidth(),self.image.GetHeight())) |
| 114 | self.ux = parent.GetScrollPixelsPerUnit()[0] |
| 115 | self.moveColumn = dragCol |
| 116 | |
| 117 | self.Bind(wx.EVT_PAINT, self.OnPaint) |
| 118 | |
| 119 | def DisplayAt(self,pos,y): |
| 120 | x = self.GetPositionTuple()[0] |
| 121 | if x == pos: |
| 122 | self.Refresh() # Need to display insertion point |
| 123 | else: |
| 124 | self.MoveXY(pos,y) |
| 125 | |
| 126 | def GetMoveColumn(self): |
| 127 | return self.moveColumn |
| 128 | |
| 129 | def _GetInsertionInfo(self): |
| 130 | parent = self.GetParent() |
| 131 | sx = parent.GetViewStart()[0] * self.ux |
| 132 | sx -= parent.GetRowLabelSize() |
| 133 | x = self.GetPosition()[0] |
| 134 | w = self.GetSize()[0] |
| 135 | sCol = parent.XToCol(x + sx) |
| 136 | eCol = parent.XToCol(x + w + sx) |
| 137 | iPos = xPos = xCol = 99999 |
| 138 | centerPos = x + sx + (w / 2) |
| 139 | |
| 140 | for col in range(sCol,eCol + 1): |
| 141 | cx = parent.ColToRect(col)[0] |
| 142 | |
| 143 | if abs(cx - centerPos) < iPos: |
| 144 | iPos = abs(cx - centerPos) |
| 145 | xCol = col |
| 146 | xPos = cx |
| 147 | |
| 148 | if xCol < 0 or xCol > parent.GetNumberCols(): |
| 149 | xCol = parent.GetNumberCols() |
| 150 | |
| 151 | return (xPos - sx - x,xCol) |
| 152 | |
| 153 | def GetInsertionColumn(self): |
| 154 | return self._GetInsertionInfo()[1] |
| 155 | |
| 156 | def GetInsertionPos(self): |
| 157 | return self._GetInsertionInfo()[0] |
| 158 | |
| 159 | def OnPaint(self,evt): |
| 160 | dc = wx.PaintDC(self) |
| 161 | w,h = self.GetSize() |
| 162 | dc.DrawBitmap(self.image, 0,0) |
| 163 | dc.SetPen(wx.Pen(wx.BLACK,1,wx.SOLID)) |
| 164 | dc.SetBrush(wx.TRANSPARENT_BRUSH) |
| 165 | dc.DrawRectangle(0,0, w,h) |
| 166 | iPos = self.GetInsertionPos() |
| 167 | dc.DrawLine(iPos,h - 10, iPos,h) |
| 168 | |
| 169 | |
| 170 | |
| 171 | |
| 172 | class RowDragWindow(wx.Window): |
| 173 | def __init__(self,parent,image,dragRow): |
| 174 | wx.Window.__init__(self,parent,-1, style=wx.SIMPLE_BORDER) |
| 175 | self.image = image |
| 176 | self.SetSize((self.image.GetWidth(),self.image.GetHeight())) |
| 177 | self.uy = parent.GetScrollPixelsPerUnit()[1] |
| 178 | self.moveRow = dragRow |
| 179 | |
| 180 | self.Bind(wx.EVT_PAINT, self.OnPaint) |
| 181 | |
| 182 | def DisplayAt(self,x,pos): |
| 183 | y = self.GetPosition()[1] |
| 184 | if y == pos: |
| 185 | self.Refresh() # Need to display insertion point |
| 186 | else: |
| 187 | self.MoveXY(x,pos) |
| 188 | |
| 189 | def GetMoveRow(self): |
| 190 | return self.moveRow |
| 191 | |
| 192 | def _GetInsertionInfo(self): |
| 193 | parent = self.GetParent() |
| 194 | sy = parent.GetViewStart()[1] * self.uy |
| 195 | sy -= parent.GetColLabelSize() |
| 196 | y = self.GetPosition()[1] |
| 197 | h = self.GetSize()[1] |
| 198 | sRow = parent.YToRow(y + sy) |
| 199 | eRow = parent.YToRow(y + h + sy) |
| 200 | iPos = yPos = yRow = 99999 |
| 201 | centerPos = y + sy + (h / 2) |
| 202 | |
| 203 | for row in range(sRow,eRow + 1): |
| 204 | cy = parent.RowToRect(row)[1] |
| 205 | |
| 206 | if abs(cy - centerPos) < iPos: |
| 207 | iPos = abs(cy - centerPos) |
| 208 | yRow = row |
| 209 | yPos = cy |
| 210 | |
| 211 | if yRow < 0 or yRow > parent.GetNumberRows(): |
| 212 | yRow = parent.GetNumberRows() |
| 213 | |
| 214 | return (yPos - sy - y,yRow) |
| 215 | |
| 216 | def GetInsertionRow(self): |
| 217 | return self._GetInsertionInfo()[1] |
| 218 | |
| 219 | def GetInsertionPos(self): |
| 220 | return self._GetInsertionInfo()[0] |
| 221 | |
| 222 | def OnPaint(self,evt): |
| 223 | dc = wx.PaintDC(self) |
| 224 | w,h = self.GetSize() |
| 225 | dc.DrawBitmap(self.image, 0,0) |
| 226 | dc.SetPen(wx.Pen(wx.BLACK,1,wx.SOLID)) |
| 227 | dc.SetBrush(wx.TRANSPARENT_BRUSH) |
| 228 | dc.DrawRectangle(0,0, w,h) |
| 229 | iPos = self.GetInsertionPos() |
| 230 | dc.DrawLine(w - 10,iPos, w,iPos) |
| 231 | |
| 232 | #---------------------------------------------------------------------------- |
| 233 | |
| 234 | class GridColMover(wx.EvtHandler): |
| 235 | def __init__(self,grid): |
| 236 | wx.EvtHandler.__init__(self) |
| 237 | |
| 238 | self.grid = grid |
| 239 | self.lwin = grid.GetGridColLabelWindow() |
| 240 | self.lwin.PushEventHandler(self) |
| 241 | self.colWin = None |
| 242 | self.ux = self.grid.GetScrollPixelsPerUnit()[0] |
| 243 | self.startX = -10 |
| 244 | self.cellX = 0 |
| 245 | self.didMove = False |
| 246 | self.isDragging = False |
| 247 | |
| 248 | self.Bind(wx.EVT_MOTION, self.OnMouseMove) |
| 249 | self.Bind(wx.EVT_LEFT_DOWN, self.OnPress) |
| 250 | self.Bind(wx.EVT_LEFT_UP, self.OnRelease) |
| 251 | |
| 252 | def OnMouseMove(self,evt): |
| 253 | if self.isDragging: |
| 254 | _rlSize = self.grid.GetRowLabelSize() |
| 255 | if abs(self.startX - evt.m_x) >= 3 \ |
| 256 | and abs(evt.m_x - self.lastX) >= 3: |
| 257 | self.lastX = evt.m_x |
| 258 | self.didMove = True |
| 259 | sx,y = self.grid.GetViewStart() |
| 260 | w,h = self.lwin.GetClientSize() |
| 261 | x = sx * self.ux |
| 262 | |
| 263 | if (evt.m_x + x) < x: |
| 264 | x = evt.m_x + x |
| 265 | elif evt.m_x > w: |
| 266 | x += evt.m_x - w |
| 267 | |
| 268 | if x < 1: x = 0 |
| 269 | else: x /= self.ux |
| 270 | |
| 271 | if x != sx: |
| 272 | if wx.Platform == '__WXMSW__': |
| 273 | self.colWin.Show(False) |
| 274 | |
| 275 | self.grid.Scroll(x,y) |
| 276 | |
| 277 | x,y = self.lwin.ClientToScreenXY(evt.m_x,0) |
| 278 | x,y = self.grid.ScreenToClientXY(x,y) |
| 279 | |
| 280 | if not self.colWin.IsShown(): |
| 281 | self.colWin.Show(True) |
| 282 | |
| 283 | px = x - self.cellX |
| 284 | |
| 285 | if px < 0 + _rlSize: px = 0 + _rlSize |
| 286 | |
| 287 | if px > w - self.colWin.GetSize()[0] + _rlSize: |
| 288 | px = w - self.colWin.GetSize()[0] + _rlSize |
| 289 | |
| 290 | self.colWin.DisplayAt(px,y) |
| 291 | return |
| 292 | |
| 293 | |
| 294 | def OnPress(self,evt): |
| 295 | self.startX = self.lastX = evt.m_x |
| 296 | _rlSize = self.grid.GetRowLabelSize() |
| 297 | sx = self.grid.GetViewStart()[0] * self.ux |
| 298 | sx -= _rlSize |
| 299 | px,py = self.lwin.ClientToScreenXY(evt.m_x,evt.m_y) |
| 300 | px,py = self.grid.ScreenToClientXY(px,py) |
| 301 | |
| 302 | if self.grid.XToEdgeOfCol(px + sx) != wx.NOT_FOUND: |
| 303 | evt.Skip() |
| 304 | return |
| 305 | |
| 306 | self.isDragging = True |
| 307 | self.didMove = False |
| 308 | col = self.grid.XToCol(px + sx) |
| 309 | rect = self.grid.ColToRect(col) |
| 310 | self.cellX = px + sx - rect.x |
| 311 | size = self.lwin.GetSize() |
| 312 | rect.y = 0 |
| 313 | rect.x -= sx + _rlSize |
| 314 | rect.height = size[1] |
| 315 | colImg = self._CaptureImage(rect) |
| 316 | self.colWin = ColDragWindow(self.grid,colImg,col) |
| 317 | self.colWin.Show(False) |
| 318 | self.lwin.CaptureMouse() |
| 319 | evt.Skip() |
| 320 | |
| 321 | def OnRelease(self,evt): |
| 322 | if self.isDragging: |
| 323 | self.lwin.ReleaseMouse() |
| 324 | self.colWin.Show(False) |
| 325 | self.isDragging = False |
| 326 | |
| 327 | if not self.didMove: |
| 328 | px = self.lwin.ClientToScreenXY(self.startX,0)[0] |
| 329 | px = self.grid.ScreenToClientXY(px,0)[0] |
| 330 | sx = self.grid.GetViewStart()[0] * self.ux |
| 331 | sx -= self.grid.GetRowLabelSize() |
| 332 | col = self.grid.XToCol(px+sx) |
| 333 | |
| 334 | if col != wx.NOT_FOUND: |
| 335 | self.grid.SelectCol(col,evt.m_controlDown) |
| 336 | |
| 337 | return |
| 338 | else: |
| 339 | bCol = self.colWin.GetInsertionColumn() |
| 340 | dCol = self.colWin.GetMoveColumn() |
| 341 | wx.PostEvent(self, |
| 342 | GridColMoveEvent(self.grid.GetId(), dCol, bCol)) |
| 343 | |
| 344 | self.colWin.Destroy() |
| 345 | evt.Skip() |
| 346 | |
| 347 | def _CaptureImage(self,rect): |
| 348 | bmp = wx.EmptyBitmap(rect.width,rect.height) |
| 349 | memdc = wx.MemoryDC() |
| 350 | memdc.SelectObject(bmp) |
| 351 | dc = wx.WindowDC(self.lwin) |
| 352 | memdc.Blit(0,0, rect.width, rect.height, dc, rect.x, rect.y) |
| 353 | memdc.SelectObject(wx.NullBitmap) |
| 354 | return bmp |
| 355 | |
| 356 | |
| 357 | class GridRowMover(wx.EvtHandler): |
| 358 | def __init__(self,grid): |
| 359 | wx.EvtHandler.__init__(self) |
| 360 | |
| 361 | self.grid = grid |
| 362 | self.lwin = grid.GetGridRowLabelWindow() |
| 363 | self.lwin.PushEventHandler(self) |
| 364 | self.rowWin = None |
| 365 | self.uy = self.grid.GetScrollPixelsPerUnit()[1] |
| 366 | self.startY = -10 |
| 367 | self.cellY = 0 |
| 368 | self.didMove = False |
| 369 | self.isDragging = False |
| 370 | |
| 371 | self.Bind(wx.EVT_MOTION, self.OnMouseMove) |
| 372 | self.Bind(wx.EVT_LEFT_DOWN, self.OnPress) |
| 373 | self.Bind(wx.EVT_LEFT_UP, self.OnRelease) |
| 374 | |
| 375 | def OnMouseMove(self,evt): |
| 376 | if self.isDragging: |
| 377 | _clSize = self.grid.GetColLabelSize() |
| 378 | if abs(self.startY - evt.m_y) >= 3 \ |
| 379 | and abs(evt.m_y - self.lastY) >= 3: |
| 380 | self.lastY = evt.m_y |
| 381 | self.didMove = True |
| 382 | x,sy = self.grid.GetViewStart() |
| 383 | w,h = self.lwin.GetClientSizeTuple() |
| 384 | y = sy * self.uy |
| 385 | |
| 386 | if (evt.m_y + y) < y: |
| 387 | y = evt.m_y + y |
| 388 | elif evt.m_y > h: |
| 389 | y += evt.m_y - h |
| 390 | |
| 391 | if y < 1: |
| 392 | y = 0 |
| 393 | else: |
| 394 | y /= self.uy |
| 395 | |
| 396 | if y != sy: |
| 397 | if wx.Platform == '__WXMSW__': |
| 398 | self.rowWin.Show(False) |
| 399 | |
| 400 | self.grid.Scroll(x,y) |
| 401 | |
| 402 | x,y = self.lwin.ClientToScreenXY(0,evt.m_y) |
| 403 | x,y = self.grid.ScreenToClientXY(x,y) |
| 404 | |
| 405 | if not self.rowWin.IsShown(): |
| 406 | self.rowWin.Show(True) |
| 407 | |
| 408 | py = y - self.cellY |
| 409 | |
| 410 | if py < 0 + _clSize: |
| 411 | py = 0 + _clSize |
| 412 | |
| 413 | if py > h - self.rowWin.GetSize()[1] + _clSize: |
| 414 | py = h - self.rowWin.GetSize()[1] + _clSize |
| 415 | |
| 416 | self.rowWin.DisplayAt(x,py) |
| 417 | return |
| 418 | |
| 419 | |
| 420 | def OnPress(self,evt): |
| 421 | self.startY = self.lastY = evt.m_y |
| 422 | _clSize = self.grid.GetColLabelSize() |
| 423 | sy = self.grid.GetViewStart()[1] * self.uy |
| 424 | sy -= _clSize |
| 425 | px,py = self.lwin.ClientToScreenXY(evt.m_x,evt.m_y) |
| 426 | px,py = self.grid.ScreenToClientXY(px,py) |
| 427 | |
| 428 | if self.grid.YToEdgeOfRow(py + sy) != wx.NOT_FOUND: |
| 429 | evt.Skip() |
| 430 | return |
| 431 | |
| 432 | self.isDragging = True |
| 433 | self.didMove = False |
| 434 | row = self.grid.YToRow(py + sy) |
| 435 | rect = self.grid.RowToRect(row) |
| 436 | self.cellY = py + sy - rect.y |
| 437 | size = self.lwin.GetSize() |
| 438 | rect.x = 0 |
| 439 | rect.y -= sy + _clSize |
| 440 | rect.width = size[0] |
| 441 | rowImg = self._CaptureImage(rect) |
| 442 | self.rowWin = RowDragWindow(self.grid,rowImg,row) |
| 443 | self.rowWin.Show(False) |
| 444 | self.lwin.CaptureMouse() |
| 445 | evt.Skip() |
| 446 | |
| 447 | def OnRelease(self,evt): |
| 448 | if self.isDragging: |
| 449 | self.lwin.ReleaseMouse() |
| 450 | self.rowWin.Show(False) |
| 451 | self.isDragging = False |
| 452 | |
| 453 | if not self.didMove: |
| 454 | py = self.lwin.ClientToScreenXY(0,self.startY)[1] |
| 455 | py = self.grid.ScreenToClientXY(0,py)[1] |
| 456 | sy = self.grid.GetViewStart()[1] * self.uy |
| 457 | sy -= self.grid.GetColLabelSize() |
| 458 | row = self.grid.YToRow(py + sy) |
| 459 | |
| 460 | if row != wx.NOT_FOUND: |
| 461 | self.grid.SelectRow(row,evt.m_controlDown) |
| 462 | return |
| 463 | else: |
| 464 | bRow = self.rowWin.GetInsertionRow() |
| 465 | dRow = self.rowWin.GetMoveRow() |
| 466 | |
| 467 | wx.PostEvent(self, |
| 468 | GridRowMoveEvent(self.grid.GetId(), dRow, bRow)) |
| 469 | |
| 470 | self.rowWin.Destroy() |
| 471 | evt.Skip() |
| 472 | |
| 473 | def _CaptureImage(self,rect): |
| 474 | bmp = wx.EmptyBitmap(rect.width,rect.height) |
| 475 | memdc = wx.MemoryDC() |
| 476 | memdc.SelectObject(bmp) |
| 477 | dc = wx.WindowDC(self.lwin) |
| 478 | memdc.Blit(0,0, rect.width, rect.height, dc, rect.x, rect.y) |
| 479 | memdc.SelectObject(wx.NullBitmap) |
| 480 | return bmp |
| 481 | |
| 482 | |
| 483 | #---------------------------------------------------------------------------- |