# # GFX/WINDOW.D # public Frame root; private global Frame @cacheParent; private global Frame @cacheLast; library global Frame @xRoot; # CREATEFRAME() # # Create a new frame. A fixed width and height may be specified or # you can allow the graphics subsystem to dynamically size either or # both by specifying 0. # public method void Frame.createFrame(lvalue Frame @this, const char *name, Frame @parent, int w, int h, int mount) { # Link into the parent. Anything relative to the root is automatically # a window. # if (parent == &root) { mount |= Frame.WINDOW; root.validPlacement = 1; if (root.darkPen == NULL) root.darkPen = root.allocPen(0x3200, 0x3200, 0x3200); if (root.mediumPen == NULL) root.mediumPen = root.allocPen(0xb800, 0xb800, 0xb800); if (root.lightPen == NULL) root.lightPen = root.allocPen(0xc800, 0xc800, 0xc800); if (root.textPen == NULL) { root.textPen = root.allocPen(); root.textPen->setFGColor(0xFFFF, 0xFFFF, 0xFFFF); root.textPen->setBGColor(65535, 45746, 19018); } } this.new(); this->name = name; this->parent = parent; this->lightPen = parent->lightPen; this->mediumPen = parent->mediumPen; this->darkPen = parent->darkPen; this->textPen = parent->textPen; if (parent->base == NULL) { parent->base = this; } else { if (cacheParent != parent) { cacheParent = parent; cacheLast = parent->base; while (cacheLast->next != NULL) cacheLast = cacheLast->next; } cacheLast->next = this; cacheLast = this; } # Common code. Set expansion mode, width, and height. Note # that the width and height may be 0. The initial bounds are # left 0 indicating the last cached object size was unknown. # this->expansionMode = mount; this->minp.w = this->maxp.w = w; this->minp.h = this->maxp.h = h; if (this->maxp.w == 0) this->maxp.w = 32767; if (this->maxp.h == 0) this->maxp.h = 32767; if (this->expansionMode & Frame.WINDOW) { this->expansionMode |= Frame.WINDOWLNK; if (this->minp.w > 0 && this->minp.h > 0) this->xid = Frame.xCreateWindow(parent->xid, 0, 0, this->minp.w, this->minp.h); else this->xid = Frame.xCreateWindow(parent->xid, 0, 0, 1, 1); if (this->xid > 0) Frame.eventThread(); this->xnext = xRoot; xRoot = this; this->newPen(); } else { this->xid = parent->xid; this->pen = parent->pen; } } public destructor method void Frame.unlinkFrame() { Frame @next; if (cacheParent == &this) { cacheParent = NULL; cacheLast = NULL; } if (this.expansionMode & Frame.WINDOWLNK) { this.expansionMode &= ~Frame.WINDOWLNK; if (xRoot == &this) { xRoot = this.xnext; } else { next = xRoot; while (next->xnext != &this) { next = next->xnext; } next->xnext = this.xnext; } this.xnext = NULL; } if (this.parent != NULL) { if (this.parent->base == &this) { this.parent->base = this.next; } else { next = this.parent->base; while (next->next != &this) next = next->next; next->next = this.next; } this.next = NULL; this.parent = NULL; } } public destructor method void Frame.destroyFrame() { this.pen = NULL; if (this.xid) { Frame.xDestroyWindow(this.xid); this.flush(); this.xid = 0; } } # CLIP # # This method cleans up the frame hierarchy by determining the relative # location and size of this frame and sub-frames and creates or resizes # windows as necessary. public method void Frame.recalc() { this.calculateMinDimensions(); this.calculatePositions(); this.refreshVisibleFrame(); this.flush(); } public method void Frame.calculateMinDimensions() { Frame @scan; int left, int right, int top, int bot, int minw, int minh; # Degenerate case # if (this.base == NULL) { this.bounds.w = this.minp.w; this.bounds.h = this.minp.h; return; } # Scan subframes and expand our width and height accordingly # for (scan = this.base; scan != NULL; scan = scan->next) { scan->calculateMinDimensions(); if ((scan->expansionMode & Frame.CLIP) != 0) continue; switch (scan->expansionMode & Frame.DIRMASK) { case Frame.ABOVE: top += scan->bounds.h; if (minw < scan->bounds.w + left + right) minw = scan->bounds.w + left + right; break; case Frame.BELOW: bot += scan->bounds.h; if (minw < scan->bounds.w + left + right) minw = scan->bounds.w + left + right; break; case Frame.LEFT: left += scan->bounds.w; if (minh < scan->bounds.h + top + bot) minh = scan->bounds.h + top + bot; break; case Frame.RIGHT: right += scan->bounds.w; if (minh < scan->bounds.h + top + bot) minh = scan->bounds.h + top + bot; break; case Frame.BODY: left += scan->bounds.w; top += scan->bounds.h; break; } } if (minw < this.minp.w) minw = this.minp.w; if (minw < left + right) minw = left + right; if (minh < this.minp.h) minh = this.minp.h; if (minh < top + bot) minh = top + bot; this.bounds.w = minw; this.bounds.h = minh; } public method void Frame.calculatePositions() { Frame @scan; Frame @parent = this.parent; Rect mbox; # Step #1 - create or inherit window parameters and relative # positioning. # # Finish up the initialization of this particular node. # this.frameGeometryChanged(); this.validPlacement = 1; # Step #2 - position our subframes based on their expansion mode # and recurse. # mbox.x = 0; mbox.y = 0; mbox.w = this.bounds.w; mbox.h = this.bounds.h; for (scan = this.base; scan != NULL; scan = scan->next) { # FILLs # if ((scan->expansionMode & Frame.FILLX) && scan->bounds.w < mbox.w ) { scan->bounds.w = mbox.w - mbox.x; } if ((scan->expansionMode & Frame.FILLY) && scan->bounds.h < mbox.h ) { scan->bounds.h = mbox.h - mbox.y; } # Primary frame positioning # switch (scan->expansionMode & Frame.DIRMASK) { case Frame.ABOVE: scan->pos.x = mbox.x; scan->pos.y = mbox.y; mbox.y += scan->bounds.h; break; case Frame.BELOW: scan->pos.x = mbox.x; scan->pos.y = mbox.h - scan->bounds.h; mbox.h -= scan->bounds.h; break; case Frame.LEFT: scan->pos.x = mbox.x; scan->pos.y = mbox.y; mbox.x += scan->bounds.w; break; case Frame.RIGHT: scan->pos.x = mbox.w - scan->bounds.w; scan->pos.y = mbox.y; mbox.w -= scan->bounds.w; break; case Frame.BODY: scan->pos.x = mbox.x; scan->pos.y = mbox.y; mbox.x += scan->bounds.w; mbox.y += scan->bounds.h; break; } scan->calculatePositions(); } } # FRAMEGEOMETRYCHANGED() # # This function is called after the frame geometry has been changed and # is responsible for cleaning up the wpos and clip, for adjusting # any physical window parameters, and also for ensuring that the system # properly redraws the area. # # At the moment we depend on X events for redraws. XXX we do not yet # support independant redraws of sub-frames. # public method void Frame.frameGeometryChanged() { if (this.expansionMode & Frame.WINDOW) { this.wpos.x = 0; this.wpos.y = 0; if (this.xid) { if (this.parent == &root) { Frame.xResizeWindow(this.xid, this.bounds.w, this.bounds.h); #Frame.xSync(this.xid, 1); # XXX #Frame.xClearWindow(this.xid); Frame.xMapWindow(this.xid); this.clip.x = 0; this.clip.y = 0; this.clip.w = this.bounds.w; this.clip.h = this.bounds.h; } else { #this.xSync(this.xid, 1); # XXX this.xMoveResizeWindow(this.xid, this.pos.x + this.vpos.x, this.pos.y + this.vpos.y, this.bounds.w, this.bounds.h); this.xMapWindow(this.xid); if (this.pos.x < 0) { this.clip.x = -this.pos.x; this.clip.w = this.parent->bounds.w; } else { this.clip.x = 0; this.clip.w = this.parent->bounds.w - this.pos.x; } if (this.pos.y < 0) { this.clip.y = -this.pos.y; this.clip.h = this.parent->bounds.h; } else { this.clip.y = 0; this.clip.h = this.parent->bounds.h - this.pos.y; } } } else { this.clip.x = 0; this.clip.y = 0; this.clip.w = 0; this.clip.h = 0; } } else { this.wpos.x = this.parent->wpos.x + this.pos.x; this.wpos.y = this.parent->wpos.y + this.pos.y; this.clip.x = 0; this.clip.y = 0; this.clip.w = this.bounds.w; this.clip.h = this.bounds.h; } } # MOVEWINDOW() # # Move the position of the frame and adjust the frame geometry as # necessary. # public method void Frame.moveWindow() { this.frameGeometryChanged(); } public method void Frame.newPen() { this.pen = Pen.allocPen(this.xid, 0xFFFF, 0xFFFF, 0xFFFF); } public method Pen * Frame.allocPen(int r = 0, int g = 0, int b = 0) { Pen *pen = Pen.allocPen(this.xid, r, g, b); return(pen); } public method void Frame.setFGColor(int r, int g, int b) { this.pen->setFGColor(r, g, b); } public method void Frame.setBGColor(int r, int g, int b) { this.pen->setBGColor(r, g, b); } public method int Frame.textHeight() { return(this.pen->textHeight()); } public method int Frame.textDescent() { return(this.pen->textDescent()); } public method void Frame.flush() { this.xFlush(this.xid); } # Translate the event window and position to the Frame's window. # public method bool Frame.translateWPosFrom(xid_t xid, Point *wpos) { Frame @base; Frame @scan; Point trans; # See if xid is a parent window of this.xid # trans = ( x: 0, y: 0 ); for (scan = &this; scan != NULL; scan = scan->parent) { if (scan->expansionMode & Frame.WINDOW) { trans.x += scan->vpos.x; trans.y += scan->vpos.y; if (scan->xid == xid) { wpos->x += trans.x; wpos->y += trans.y; return(TRUE); } } trans.x -= scan->pos.x; trans.y -= scan->pos.y; } # xid must be a sub-window of this.xid, find it and then run up. # trans = ( x: 0, y: 0 ); for (scan = xRoot; scan != NULL; scan = scan->xnext) { if (xid == scan->xid) break; } while (scan != NULL) { if (scan->expansionMode & Frame.WINDOW) { trans.x += scan->vpos.x; trans.y += scan->vpos.y; if (scan->xid == xid) { wpos->x += trans.x; wpos->y += trans.y; return(TRUE); } } trans.x -= scan->pos.x; trans.y -= scan->pos.y; scan = scan->parent; } return(FALSE); }