1# Copyright (c) 2009, Tomohiro Kusumi 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# 1. Redistributions of source code must retain the above copyright notice, this 8# list of conditions and the following disclaimer. 9# 2. Redistributions in binary form must reproduce the above copyright notice, 10# this list of conditions and the following disclaimer in the documentation 11# and/or other materials provided with the distribution. 12# 13# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 24from . import console 25from . import edit 26from . import extension 27from . import panel 28from . import screen 29from . import setting 30from . import status 31from . import util 32from . import virtual 33from . import visual 34from . import void 35from . import window 36 37BUILD_FAILED = -1 38BUILD_RETRY = -2 39 40class Workspace (object): 41 def __init__(self, bpl): 42 self.__bpl = 1 43 self.set_bytes_per_line(bpl) 44 assert self.__bpl >= 1, self.__bpl 45 self.__windows = () 46 self.__fileopss = [] 47 self.__consoles = [] 48 self.__vwindows = {} 49 self.__bwindows = {} 50 self.__twindows = {} 51 self.__swindows = {} 52 self.__cur_fileops = None 53 self.__cur_console = None 54 self.__set_window(None) 55 self.__def_vwindow, \ 56 self.__def_bwindow, \ 57 self.__def_twindow, \ 58 self.__def_swindow \ 59 = self.__windows 60 61 def __getattr__(self, name): 62 if name == "_Workspace__cur_fileops": 63 raise AttributeError(name) 64 return getattr(self.__cur_fileops, name) 65 66 def __len__(self): 67 return len(self.__windows) 68 69 def dispatch(self): 70 return -1 71 72 def __set_buffer(self, o): 73 self.__cur_fileops = o 74 self.set_console(None, None) 75 76 def set_console(self, con, arg): 77 if isinstance(con, console.Console): 78 self.__cur_console = con 79 else: 80 i = self.__fileopss.index(self.__cur_fileops) 81 self.__cur_console = self.__consoles[i] 82 self.__set_window(self.__cur_console) 83 for o in self.__windows: 84 if o: 85 o.set_buffer(self.__cur_fileops) 86 def dispatch(): 87 self.require_full_repaint() 88 return self.__cur_console.dispatch(arg) 89 self.dispatch = dispatch 90 91 def __set_window(self, con): 92 # virtual window comes first 93 if isinstance(con, console.Console): 94 cls = util.get_class(con) 95 else: 96 cls = console.get_default_class() 97 self.__windows = \ 98 self.__get_virtual_window(cls), \ 99 self.__get_binary_window(cls), \ 100 self.__get_text_window(cls), \ 101 self.__get_status_window(cls) 102 103 def disconnect_window(self): 104 # only leave virtual window to disable repaint 105 self.__windows = self.__windows[0], 106 107 def reconnect_window(self): 108 # bring back windows of current console 109 self.__set_window(self.__cur_console) 110 111 def clone(self): 112 ret = Workspace(self.__bpl) 113 for i, o in enumerate(self.__fileopss): 114 # do shallow copy, but don't use copy.copy() 115 ret.add_buffer(i, o.clone(), self.__consoles[i]) 116 return ret 117 118 def add_buffer(self, i, fop, con): 119 self.__fileopss.insert(i, fop) 120 self.__consoles.insert(i, con) 121 if len(self.__fileopss) == 1: 122 self.__set_buffer(fop) 123 124 def remove_buffer(self, i): 125 o = self.__fileopss[i] 126 i = self.__fileopss.index(o) 127 if self.__cur_fileops == o: 128 if i == len(self.__fileopss) - 1: 129 self.switch_to_prev_buffer() 130 else: 131 self.switch_to_next_buffer() 132 self.__fileopss.remove(self.__fileopss[i]) 133 self.__consoles.remove(self.__consoles[i]) 134 135 def switch_to_buffer(self, i): 136 self.__set_buffer(self.__fileopss[i]) 137 138 def switch_to_first_buffer(self): 139 self.__set_buffer(self.__fileopss[0]) 140 141 def switch_to_last_buffer(self): 142 self.__set_buffer(self.__fileopss[-1]) 143 144 def switch_to_next_buffer(self): 145 if len(self.__fileopss) > 1: 146 i = self.__fileopss.index(self.__cur_fileops) 147 if i >= len(self.__fileopss) - 1: 148 o = self.__fileopss[0] 149 else: 150 o = self.__fileopss[i + 1] 151 self.__set_buffer(o) 152 153 def switch_to_prev_buffer(self): 154 if len(self.__fileopss) > 1: 155 i = self.__fileopss.index(self.__cur_fileops) 156 if i <= 0: 157 o = self.__fileopss[len(self.__fileopss) - 1] 158 else: 159 o = self.__fileopss[i - 1] 160 self.__set_buffer(o) 161 162 def iter_buffer(self): 163 for o in self.__fileopss: 164 yield o 165 166 def get_build_size(self): 167 return self.get_height(), self.get_width() 168 169 def get_height(self): 170 bin_hei = self.__def_bwindow.get_size_y() 171 tex_hei = self.__def_twindow.get_size_y() if setting.use_text_window \ 172 else bin_hei 173 sta_hei = self.__def_swindow.get_size_y() 174 sta_hei_ = self.__get_status_window_height() 175 # screen size may change before build() while resizing 176 if not screen.test_soft_resize(): 177 assert bin_hei == tex_hei, (bin_hei, tex_hei) 178 assert sta_hei == sta_hei_, (sta_hei, sta_hei_) 179 return bin_hei + sta_hei 180 181 def get_width(self): 182 bin_wid = self.__def_bwindow.get_size_x() 183 tex_wid = self.__def_twindow.get_size_x() if setting.use_text_window \ 184 else 0 185 sta_wid = self.__def_swindow.get_size_x() 186 ret = bin_wid + tex_wid 187 # screen size may change before build() while resizing 188 if not screen.test_soft_resize(): 189 assert ret == sta_wid, (bin_wid, tex_wid, sta_wid) 190 return ret 191 192 def __get_status_window_class(self): 193 return status.get_status_canvas_class(), status.get_status_frame_class() 194 195 def __get_status_window_height(self): 196 return window.get_status_window_height( 197 *self.__get_status_window_class()) 198 199 # horizontal split 200 def build_dryrun_delta(self, hei_delta, beg_delta): 201 hei = self.__def_bwindow.get_size_y() + self.__def_swindow.get_size_y() 202 beg = self.__def_bwindow.get_position_y() 203 return self.build_dryrun(hei + hei_delta, beg + beg_delta) 204 205 def build_dryrun(self, hei, beg): 206 sta_hei = self.__get_status_window_height() 207 min_hei = window.get_min_binary_window_height() + sta_hei 208 return self.__test_build(hei, beg, min_hei) 209 210 def build(self, hei, beg): 211 sta_hei = self.__get_status_window_height() 212 self.__build(hei, beg, sta_hei) 213 return hei 214 215 def build_fixed_size_dryrun(self, lpw, beg): 216 sta_hei = self.__get_status_window_height() 217 hei = window.get_min_binary_window_height(lpw) + sta_hei 218 min_hei = window.get_min_binary_window_height() + sta_hei 219 return self.__test_build(hei, beg, min_hei) 220 221 def build_fixed_size(self, lpw, beg): 222 sta_hei = self.__get_status_window_height() 223 hei = window.get_min_binary_window_height(lpw) + sta_hei 224 self.__build(hei, beg, sta_hei) 225 return hei 226 227 def __test_build(self, hei, beg, min_hei): 228 scr_hei = screen.get_size_y() 229 scr_wid = screen.get_size_x() 230 if hei <= 0: 231 return BUILD_FAILED 232 if beg < 0: 233 return BUILD_FAILED 234 if scr_hei < min_hei: # screen height < minimum workspace height 235 return BUILD_FAILED 236 if hei < min_hei: # height < minimum workspace height 237 return BUILD_FAILED 238 if hei > scr_hei: # height > screen height 239 return BUILD_FAILED 240 if beg + hei >= scr_hei: # this workspace exceeds screen size 241 return BUILD_FAILED 242 if self.guess_width() > scr_wid: # test width 243 return BUILD_FAILED 244 return hei 245 246 def __build(self, hei, beg, sta_hei): 247 def vfn(o): 248 siz = hei - sta_hei, self.__guess_virtual_window_width() 249 pos = beg, 0 250 self.__build_window(o, siz, pos) 251 def bfn(o): 252 siz = hei - sta_hei, self.__guess_binary_window_width() 253 pos = beg, 0 254 self.__build_window(o, siz, pos) 255 def tfn(o): 256 siz = hei - sta_hei, self.__guess_text_window_width() 257 pos = beg, self.__def_bwindow.get_size_x() 258 self.__build_window(o, siz, pos) 259 def sfn(o): 260 siz = sta_hei, self.guess_width() 261 pos = beg + self.__def_bwindow.get_size_y(), 0 262 self.__build_window(o, siz, pos) 263 self.__do_build(vfn, bfn, tfn, sfn) 264 265 # vertical split 266 def vbuild_dryrun(self, beg): 267 sta_hei = self.__get_status_window_height() 268 hei = console.get_position_y() 269 min_hei = window.get_min_binary_window_height() + sta_hei 270 return self.__test_vbuild(hei, beg, min_hei) 271 272 def vbuild(self, beg): 273 sta_hei = self.__get_status_window_height() 274 hei = console.get_position_y() 275 self.__vbuild(hei, beg, sta_hei) 276 return beg 277 278 def vbuild_fixed_size_dryrun(self, lpw, beg): 279 sta_hei = self.__get_status_window_height() 280 hei = window.get_min_binary_window_height(lpw) + sta_hei 281 min_hei = window.get_min_binary_window_height() + sta_hei 282 return self.__test_vbuild(hei, beg, min_hei) 283 284 def vbuild_fixed_size(self, lpw, beg): 285 sta_hei = self.__get_status_window_height() 286 hei = window.get_min_binary_window_height(lpw) + sta_hei 287 self.__vbuild(hei, beg, sta_hei) 288 return beg 289 290 def __test_vbuild(self, hei, beg, min_hei): 291 scr_hei = screen.get_size_y() 292 scr_wid = screen.get_size_x() 293 if hei <= 0: 294 return BUILD_FAILED 295 if beg < 0: 296 return BUILD_FAILED 297 if scr_hei < min_hei: # screen height < minimum workspace height 298 return BUILD_FAILED 299 if hei < min_hei: # height < minimum workspace height 300 return BUILD_FAILED 301 if hei > scr_hei: # height > screen height (redundant) 302 return BUILD_FAILED 303 if hei >= scr_hei: # this workspace exceeds screen size 304 return BUILD_FAILED 305 if beg + self.guess_width() > scr_wid: # test width 306 return BUILD_FAILED 307 return hei 308 309 def __vbuild(self, hei, beg, sta_hei): 310 def vfn(o): 311 siz = hei - sta_hei, self.__guess_virtual_window_width() 312 pos = 0, beg 313 self.__build_window(o, siz, pos) 314 def bfn(o): 315 siz = hei - sta_hei, self.__guess_binary_window_width() 316 pos = 0, beg 317 self.__build_window(o, siz, pos) 318 def tfn(o): 319 siz = hei - sta_hei, self.__guess_text_window_width() 320 pos = 0, beg + self.__def_bwindow.get_size_x() 321 self.__build_window(o, siz, pos) 322 def sfn(o): 323 siz = sta_hei, self.guess_width() 324 pos = self.__def_bwindow.get_size_y(), beg 325 self.__build_window(o, siz, pos) 326 self.__do_build(vfn, bfn, tfn, sfn) 327 328 # common for both horizontal and vertical split 329 def __do_build(self, vfn, bfn, tfn, sfn): 330 self.__build_virtual_window = vfn 331 self.__build_binary_window = bfn 332 self.__build_text_window = tfn 333 self.__build_status_window = sfn 334 335 # update first for potential window parameter changes 336 # (if yes, dryrun must have been done with new parameters) 337 for o in self.__vwindows.values(): 338 if o: 339 o.update() 340 for o in self.__bwindows.values(): 341 if o: 342 o.update() 343 for o in self.__twindows.values(): 344 if o: 345 o.update() 346 for o in self.__swindows.values(): 347 if o: 348 o.update() 349 350 for o in self.__vwindows.values(): 351 self.__build_virtual_window(o) 352 for o in self.__bwindows.values(): 353 self.__build_binary_window(o) 354 for o in self.__twindows.values(): 355 self.__build_text_window(o) 356 for o in self.__swindows.values(): 357 self.__build_status_window(o) 358 359 bin_hei = self.__def_bwindow.get_size_y() 360 tex_hei = self.__def_twindow.get_size_y() if setting.use_text_window \ 361 else bin_hei 362 assert bin_hei == tex_hei, (bin_hei, tex_hei) 363 364 bin_wid = self.__def_bwindow.get_size_x() 365 tex_wid = self.__def_twindow.get_size_x() if setting.use_text_window \ 366 else 0 367 sta_wid = self.__def_swindow.get_size_x() 368 assert bin_wid + tex_wid == sta_wid, (bin_wid, tex_wid, sta_wid) 369 370 def __build_virtual_window(self, o): 371 self.__build_window(o, None, None) 372 373 def __build_binary_window(self, o): 374 self.__build_window(o, None, None) 375 376 def __build_text_window(self, o): 377 self.__build_window(o, None, None) 378 379 def __build_status_window(self, o): 380 self.__build_window(o, None, None) 381 382 # Note that update_capacity() can't be called from window.Window.__init__, 383 # because initial panel size hasn't been set yet, while get_bufmap() needs 384 # panel size to update bufmap. 385 def __build_window(self, o, siz, pos): 386 if o: # None if window is disabled 387 o.resize(siz, pos) 388 o.update_capacity(self.__bpl) # XXX 389 390 def __guess_virtual_window_width(self): 391 return window.get_width(panel.BinaryCanvas, self.__bpl) 392 393 def __guess_binary_window_width(self): 394 return window.get_width(panel.BinaryCanvas, self.__bpl) 395 396 def __guess_text_window_width(self): 397 if setting.use_text_window: 398 return window.get_width(panel.TextCanvas, self.__bpl) 399 else: 400 return 0 401 402 def guess_width(self): 403 return self.__guess_binary_window_width() + \ 404 self.__guess_text_window_width() 405 406 def __get_virtual_window(self, cls): 407 if cls not in self.__vwindows: 408 if cls is console.get_default_class(): 409 o = window.Window(virtual.BinaryCanvas, virtual.NullFrame) 410 elif cls is void.ExtConsole: 411 o = window.Window(virtual.ExtCanvas, virtual.NullFrame) 412 elif cls is visual.Console: 413 o = self.__def_vwindow 414 elif cls is visual.ExtConsole: 415 o = self.__get_virtual_window(void.ExtConsole) 416 elif util.is_subclass(cls, edit.Console): 417 o = self.__def_vwindow 418 else: 419 assert False, cls 420 self.__build_virtual_window(o) 421 self.__vwindows[cls] = o 422 return self.__vwindows[cls] 423 424 def __get_binary_window(self, cls): 425 if cls not in self.__bwindows: 426 if cls is console.get_default_class(): 427 o = window.Window(panel.BinaryCanvas, panel.Frame) 428 elif cls is void.ExtConsole: 429 o = window.Window(extension.ExtBinaryCanvas, panel.Frame) 430 elif cls is visual.Console: 431 o = window.Window(visual.BinaryCanvas, panel.Frame) 432 elif cls is visual.ExtConsole: 433 o = window.Window(visual.ExtBinaryCanvas, panel.Frame) 434 elif util.is_subclass(cls, edit.WriteBinaryConsole): 435 o = window.Window(edit.WriteBinaryCanvas, panel.Frame) 436 elif util.is_subclass(cls, edit.WriteAsciiConsole): 437 o = self.__def_bwindow 438 elif util.is_subclass(cls, edit.DeleteConsole): 439 o = self.__def_bwindow 440 else: 441 assert False, cls 442 self.__build_binary_window(o) 443 self.__bwindows[cls] = o 444 return self.__bwindows[cls] 445 446 def __get_text_window(self, cls): 447 if cls not in self.__twindows: 448 if not setting.use_text_window: 449 o = None 450 elif cls is console.get_default_class(): 451 o = window.Window(panel.TextCanvas, panel.Frame) 452 elif cls is void.ExtConsole: 453 o = window.Window(extension.ExtTextCanvas, panel.Frame) 454 elif cls is visual.Console: 455 o = window.Window(visual.TextCanvas, panel.Frame) 456 elif cls is visual.ExtConsole: 457 o = self.__get_text_window(void.ExtConsole) 458 elif util.is_subclass(cls, edit.Console): 459 o = self.__def_twindow 460 else: 461 assert False, cls 462 self.__build_text_window(o) 463 self.__twindows[cls] = o 464 return self.__twindows[cls] 465 466 def __get_status_window(self, cls): 467 if cls not in self.__swindows: 468 if cls is console.get_default_class(): 469 o = window.Window(*self.__get_status_window_class()) 470 elif cls is void.ExtConsole: 471 o = self.__def_swindow 472 elif cls is visual.Console: 473 o = self.__def_swindow 474 elif cls is visual.ExtConsole: 475 o = self.__def_swindow 476 elif util.is_subclass(cls, edit.Console): 477 o = self.__def_swindow 478 else: 479 assert False, cls 480 self.__build_status_window(o) 481 self.__swindows[cls] = o 482 return self.__swindows[cls] 483 484 def init_buffer(self, b): 485 return self.__cur_fileops.init_buffer(b) 486 487 def read(self, x, n): 488 return self.__cur_fileops.read(x, n) 489 490 def read_current(self, n): 491 return self.__cur_fileops.read(self.get_pos(), n) 492 493 def insert(self, x, l, rec=True): 494 self.__cur_fileops.insert(x, l, rec) 495 496 def insert_current(self, l, rec=True): 497 self.__cur_fileops.insert(self.get_pos(), l, rec) 498 499 def replace(self, x, l, rec=True): 500 self.__cur_fileops.replace(x, l, rec) 501 502 def replace_current(self, l, rec=True): 503 self.__cur_fileops.replace(self.get_pos(), l, rec) 504 505 def delete(self, x, n, rec=True): 506 self.__cur_fileops.delete(x, n, rec) 507 508 def delete_current(self, n, rec=True): 509 self.__cur_fileops.delete(self.get_pos(), n, rec) 510 511 def set_focus(self, x): 512 for o in self.__windows: 513 if o: 514 if o in self.__bwindows.values(): 515 o.set_focus(x) 516 elif o in self.__twindows.values(): 517 o.set_focus(x) 518 else: 519 o.lrepaint(x, False) # entire window 520 521 def require_full_repaint(self): 522 for o in self.__windows: 523 if o: 524 o.require_full_repaint() 525 526 def repaint(self, is_current): 527 for o in self.__windows: 528 if o: 529 o.repaint(is_current) 530 531 def lrepaint(self, is_current, low): 532 for o in self.__windows: 533 if o: 534 o.lrepaint(is_current, low) 535 536 def prepaint(self, is_current, low, num): 537 for o in self.__windows: 538 if o: 539 o.prepaint(is_current, low, num) 540 541 def xrepaint(self, is_current): 542 for o in self.__windows: 543 if o: 544 o.xrepaint(is_current) 545 546 def go_up(self, n=1): 547 for o in self.__windows: 548 if o: 549 if o.go_up(n) == -1: 550 return -1 551 552 def go_down(self, n=1): 553 for o in self.__windows: 554 if o: 555 if o.go_down(n) == -1: 556 return -1 557 558 def go_left(self, n=1): 559 for o in self.__windows: 560 if o: 561 if o.go_left(n) == -1: 562 return -1 563 564 def go_right(self, n=1): 565 for o in self.__windows: 566 if o: 567 if o.go_right(n) == -1: 568 return -1 569 570 def go_pprev(self, n): 571 for o in self.__windows: 572 if o: 573 if o.go_pprev(n) == -1: 574 return -1 575 576 def go_hpprev(self, n): 577 for o in self.__windows: 578 if o: 579 if o.go_hpprev(n) == -1: 580 return -1 581 582 def go_pnext(self, n): 583 for o in self.__windows: 584 if o: 585 if o.go_pnext(n) == -1: 586 return -1 587 588 def go_hpnext(self, n): 589 for o in self.__windows: 590 if o: 591 if o.go_hpnext(n) == -1: 592 return -1 593 594 def go_head(self, n): 595 for o in self.__windows: 596 if o: 597 if o.go_head(n) == -1: 598 return -1 599 600 def go_tail(self, n): 601 for o in self.__windows: 602 if o: 603 if o.go_tail(n) == -1: 604 return -1 605 606 def go_lhead(self): 607 for o in self.__windows: 608 if o: 609 if o.go_lhead() == -1: 610 return -1 611 612 def go_ltail(self, n): 613 for o in self.__windows: 614 if o: 615 if o.go_ltail(n) == -1: 616 return -1 617 618 def go_phead(self, n): 619 for o in self.__windows: 620 if o: 621 if o.go_phead(n) == -1: 622 return -1 623 624 def go_pcenter(self): 625 for o in self.__windows: 626 if o: 627 if o.go_pcenter() == -1: 628 return -1 629 630 def go_ptail(self, n): 631 for o in self.__windows: 632 if o: 633 if o.go_ptail(n) == -1: 634 return -1 635 636 def go_to(self, n): 637 for o in self.__windows: 638 if o: 639 if o.go_to(n) == -1: 640 return -1 641 642 def get_bytes_per_line(self): 643 return self.__bpl 644 645 def set_bytes_per_line(self, bpl): 646 assert isinstance(bpl, int) 647 assert self.__bpl > 0 648 if bpl == -1: # possible on container init 649 return -1 650 self.__bpl = bpl 651 652 def get_capacity(self): 653 return self.__def_bwindow.get_capacity() 654 655 def has_geom(self, y, x): 656 for o in self.__windows: # virtual window has geom 657 if o.has_geom(y, x): 658 return True 659 return False 660 661 def get_geom_pos(self, y, x): 662 for o in self.__windows: # virtual window has geom 663 pos = o.get_geom_pos(y, x) 664 if pos != -1: 665 return pos 666 return -1 667