///////////////////////////////////////////////////////////////////////////// // // This file is Copyright 1992,1993 by Warwick W. Allison. // This file is part of the gem++ library. // You are free to copy and modify these sources, provided you acknowledge // the origin by retaining this notice, and adhere to the conditions // described in the file COPYING.LIB. // // (Extensions made in 1992 by Andre Pareis.) // ///////////////////////////////////////////////////////////////////////////// #include #include #include "gema.h" #include "gemw.h" #include "contract.h" //////////////////////////////////////// // constructors, destructor //////////////////////////////////////// GEMwindow::GEMwindow(GEMactivity& in, int Parts) : parts(Parts), xoff(0), yoff(0), opened(FALSE), created(FALSE), initialized(FALSE), xalign(1), yalign(1), act(0) { Pos = Max = GRect(0, 0, 0, 0); *(info = new char[1]) = 0; // create an empty string *(name = new char[1]) = 0; // create an empty name visibleLines = 0; totalLines = 0; actualTopLine = 0; visibleColumns = 0; totalColumns = 0; actualLeftColumn = 0; lineHeight = 1; columnWidth = 1; if (parts<0) { handle = 0; Create(); } InActivity(in); } GEMwindow::GEMwindow(GEMactivity& in, int Parts, const GRect& actWorkArea, const GRect& maxWorkArea) : parts(Parts), Pos(actWorkArea), Max(maxWorkArea), handle(0), opened(FALSE), created(FALSE), initialized(TRUE), xoff(0), yoff(0), xalign(1), yalign(1), act(0) { *(info = new char[1]) = 0; // create an empty string *(name = new char[1]) = 0; // create an empty name visibleLines = 0; totalLines = 0; actualTopLine = 0; visibleColumns = 0; totalColumns = 0; actualLeftColumn = 0; lineHeight = 1; columnWidth = 1; if (parts<0) { handle = 0; Create(); } InActivity(in); } GEMwindow::GEMwindow(GEMactivity& in, int Parts, const GRect& workArea) : parts(Parts), Pos(workArea), Max(workArea), handle(0), opened(FALSE), created(FALSE), initialized(TRUE), xoff(0), yoff(0), xalign(1), yalign(1), act(0) { *(info = new char[1]) = 0; // create an empty string *(name = new char[1]) = 0; // create an empty name visibleLines = 0; totalLines = 0; actualTopLine = 0; visibleColumns = 0; totalColumns = 0; actualLeftColumn = 0; lineHeight = 1; columnWidth = 1; if (parts<0) { handle = 0; Create(); } InActivity(in); } GEMwindow::GEMwindow(const GEMwindow& copy) : parts(copy.parts), Pos(copy.Pos), Max(copy.Max), handle(0), opened(FALSE), created(FALSE), initialized(copy.initialized), xoff(copy.xoff), yoff(copy.yoff), xalign(copy.xalign), yalign(copy.yalign), info(strdup(copy.info)), name(strdup(copy.name)), visibleLines(copy.visibleLines), totalLines(copy.totalLines), actualTopLine(copy.actualTopLine), visibleColumns(copy.visibleColumns), totalColumns(copy.totalColumns), actualLeftColumn(copy.actualLeftColumn), act(0), storer(copy.storer), vSize(copy.vSize), vPosition(copy.vSize), hSize(copy.vSize), hPosition(copy.vSize), lineHeight(copy.lineHeight), columnWidth(copy.columnWidth) { GRect win=BorderRect(); win.g_x+=10; win.g_y+=10; Move(win.g_x,win.g_y); // If the original window was in an activity, put this one in there too. if (copy.act) InActivity(*copy.act); } GEMwindow::~GEMwindow() { if (act) act->RemoveWindow(*this); if (IsOpen()) Close(); if (IsCreated()) Delete(); delete info; delete name; Ensure(!IsCreated()); } void GEMwindow::RedrawOverlaps(const GRect& area) // Standard algorithm found in all GEM code... that's why we have classes! { GRect box, dirty_dest, work_area = WorkRect(); wind_update(BEG_UPDATE); graf_mouse(M_OFF, 0); wind_get(handle, WF_FIRSTXYWH, &box.g_x, &box.g_y, &box.g_w, &box.g_h); while (box.g_w && box.g_h) { if (rc_intersect(&(GRect&)area, &box)) { rc_copy(&box, &dirty_dest); if (rc_intersect(&work_area, &dirty_dest)) Redraw(dirty_dest); } wind_get(handle, WF_NEXTXYWH, &box.g_x, &box.g_y, &box.g_w, &box.g_h); } graf_mouse(M_ON, 0); wind_update(END_UPDATE); } //////////////////////////////////////////////////////////////////////// // // SPC: Redraw(GRect& area) // -- this method should be redefined in client classes // //////////////////////////////////////////////////////////////////////// void GEMwindow::Redraw(const GRect& area) { // clients should clip the 'area' and draw the window contents } void GEMwindow::Open() { if (!IsOpen()) { Create(); if (!IsCreated()) return; VAlignSlider(); HAlignSlider(); VFlushSlider(); HFlushSlider(); wind_set(handle, WF_NAME, name, 0, 0); wind_set(handle, WF_INFO, info, 0, 0); GRect win = BorderRect(); wind_open(handle, win.g_x, win.g_y, win.g_w, win.g_h); opened = TRUE; } else { wind_set(handle, WF_TOP); } if (act) act->Topped(*this); Ensure(IsOpen()); } void GEMwindow::Close() { if (IsOpen() && handle) { wind_close(handle); opened = FALSE; act->Bottomed(*this); Delete(); } } //////////////////////////////////////////////////////////////////////// // // SPC: move(int x, int y) // -- Move() moves the window to the upper left point (x, y) and // -- performs the move at screen if the window is opened // //////////////////////////////////////////////////////////////////////// void GEMwindow::Move(int x, int y) { GRect win = BorderRect(); GRect root; win.MoveAbs((x+xoff+xalign/2)/xalign*xalign-xoff, (y+yoff+yalign/2)/yalign*yalign-yoff); wind_get(0, WF_WORKXYWH, &root.g_x, &root.g_y, &root.g_w, &root.g_h); if (win.g_x < root.g_x) win.g_x = root.g_x; if (win.g_y < root.g_y) win.g_y = root.g_y; SetBorderRect(win); } void GEMwindow::Top(const GEMevent&) { if (handle && IsOpen()) { wind_set(handle, WF_TOP); act->Topped(*this); } } GEMfeedback GEMwindow::Click(const GEMevent&) { return ContinueInteraction; } void GEMwindow::Align(int x, int y, int xmult=8, int ymult=1) { xoff=x; yoff=y; // Ignore 0 multiples! if (xmult) xalign=xmult; if (ymult) yalign=ymult; } void GEMwindow::InActivity(GEMactivity& in) { in.AddWindow(*this); act=∈ } //////////////////////////////////////////////////////////////////////// // // Here are the extensions added by A.Pareis: // //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // SPC: Create() // -- Create() creates the real window how GEM knows it; // -- the returned value signals the success of the operation // //////////////////////////////////////////////////////////////////////// bool GEMwindow::Create() { Require(!IsCreated()); if (IsCreated()) return FALSE; GRect win; if (!initialized) { // i.e., no rectangles given wind_get(0, WF_WORKXYWH, &win.g_x, &win.g_y, &win.g_w, &win.g_h); Pos = Max = Win2Work(win); initialized = TRUE; } else win = BorderRect(); if (parts<0) { handle=0; // ie. Desktop parts=0; } else { handle = wind_create(parts, win.g_x, win.g_y, win.g_w, win.g_h); } if (handle>=0) // if succesfully created created = TRUE; return created; } //////////////////////////////////////////////////////////////////////// // // SPC: Delete() // -- delete the real GEM window, but not the instance of this class // -- if the window is opened, close it first // //////////////////////////////////////////////////////////////////////// void GEMwindow::Delete() { if (IsCreated()) { if (IsOpen()) Close(); if (handle) wind_delete(handle); created = FALSE; Ensure(!IsCreated()); } } //////////////////////////////////////////////////////////////////////// // // SPC: BecomeDeleted() // //////////////////////////////////////////////////////////////////////// void GEMwindow::BecomeDeleted() { if (IsCreated()) { opened = FALSE; created = FALSE; Ensure(!IsCreated()); } } //////////////////////////////////////////////////////////////////////// // // SPC: SetBorderRect(GRect& newPos) // -- the window may be moved and resized, if it is opened, the // -- action will be performed at screen, too // //////////////////////////////////////////////////////////////////////// void GEMwindow::SetBorderRect(const GRect& newPos) { SetWorkRect(Win2Work(newPos)); } void GEMwindow::SetWorkRect(const GRect& newPos) { Pos = newPos; if (parts&HSLIDE && Pos.g_w/columnWidth > totalColumns) Pos.g_w=columnWidth * totalColumns; if (parts&HSLIDE && Pos.g_h/lineHeight > totalLines) Pos.g_h=lineHeight * totalLines; // Round size down to nearest document unit Pos.g_w-=Pos.g_w%columnWidth; Pos.g_h-=Pos.g_h%lineHeight; GRect win = BorderRect(); if (IsOpen() && handle) wind_set(handle, WF_CURRXYWH, win.g_x, win.g_y, win.g_w, win.g_h); if (parts&HSLIDE && Pos.g_w/columnWidth != visibleColumns) SetVisibleColumns(Pos.g_w/columnWidth); if (parts&VSLIDE && Pos.g_h/lineHeight != visibleLines) SetVisibleLines(Pos.g_h/lineHeight); } //////////////////////////////////////////////////////////////////////// // // SPC: Resize(int w, int h) // //////////////////////////////////////////////////////////////////////// void GEMwindow::Resize(int w, int h) { GRect win = BorderRect(); win.Resize(w, h); SetBorderRect(win); } GRect GEMwindow::Win2Work(const GRect& outer) const { GRect tmp; wind_calc(WC_WORK, parts < 0 ? 0 : parts, outer.g_x, outer.g_y, outer.g_w, outer.g_h, &tmp.g_x, &tmp.g_y, &tmp.g_w, &tmp.g_h); return tmp; } GRect GEMwindow::Work2Win(const GRect& work) const { GRect tmp; wind_calc(WC_BORDER, parts < 0 ? 0 : parts, work.g_x, work.g_y, work.g_w, work.g_h, &tmp.g_x, &tmp.g_y, &tmp.g_w, &tmp.g_h); return tmp; } //////////////////////////////////////////////////////////////////////// // // SPC: SetInfoText(const char *line) // -- store and set the new info line for the window // -- this _must_! be at least created // //////////////////////////////////////////////////////////////////////// void GEMwindow::SetInfoText(const char *line) { if (handle) { delete info; info = new char[ strlen(line)+1 ]; info = strcpy(info, line); if (IsCreated()) wind_set(handle, WF_INFO, info, 0, 0); Ensure(strcmp(info, line)==0); } } //////////////////////////////////////////////////////////////////////// // // SPC: UserFulled() // -- usually called by an application manager, the default action // -- is to toggle between two possible positions and sizes // //////////////////////////////////////////////////////////////////////// void GEMwindow::UserFulled() { Require(IsOpen()); if (!IsOpen()) return; if (Max==WorkRect()) // i.e., is fulled SetWorkRect(storer); // use stored value else { storer = WorkRect(); // store actual rectangle SetWorkRect(Max); } } //////////////////////////////////////////////////////////////////////// // // SPC: UserResized(int w, int h) // -- this function may be redefined to align the new size, but // -- here it does nothing else than to call the standard Resize() // -- of a simple GEMwindow with the same values // //////////////////////////////////////////////////////////////////////// void GEMwindow::UserResized(int w, int h) { Resize(w, h); } void GEMwindow::SetName(const char *newName) { delete name; name = new char[ strlen(newName)+1 ]; name = strcpy(name, newName); if (IsCreated()) wind_set(handle, WF_NAME, name, 0, 0); } //////////////////////////////////////////////////////////////////////// // // SPC: Flush() // -- this method will be called, if the user changed the state // -- of a slider, e.g., by moving it; descendant classes may // -- refine it, with the aim of a more efficient implementation // //////////////////////////////////////////////////////////////////////// void GEMwindow::Flush() { if (IsOpen()) RedrawOverlaps(WorkRect()); } //////////////////////////////////////////////////////////////////////// // // SPC: SetVisibleLines(int noOfLines) // -- change the number of visible lines, this change will be // -- flushed through to the GEM window // //////////////////////////////////////////////////////////////////////// void GEMwindow::SetVisibleLines(int noOfLines) { Require(noOfLines >= 0); Require(noOfLines <= totalLines); visibleLines = noOfLines; if (actualTopLine > totalLines - visibleLines) actualTopLine = totalLines - visibleLines; VAlignSlider(); VFlushSlider(); } void GEMwindow::SetTotalLines(int noOfLines) { Require(noOfLines >= 0); bool change = FALSE; totalLines = noOfLines; if (noOfLines < visibleLines) { visibleLines = totalLines; change = TRUE; } if (actualTopLine > totalLines - visibleLines) { actualTopLine = totalLines - visibleLines; change = TRUE; } VAlignSlider(); VFlushSlider(); if (change) Flush(); } void GEMwindow::SetTopLine(int noOfLine) { Require(noOfLine >= 0); Require(noOfLine <= totalLines - visibleLines); if (actualTopLine != noOfLine) { actualTopLine = noOfLine; VAlignSlider(); VFlushSlider(); Flush(); } } //////////////////////////////////////////////////////////////////////// // // SPC: VCalculateGEMvalues() and CalculateValues(): // -- use the members totalLines, visibleLines and // -- actualTopLine to calculate the size and position of the GEM // -- slider and vice versa; // //////////////////////////////////////////////////////////////////////// void GEMwindow::VCalculateGEMvalues() { if (totalLines > 0) { vSize = 1000 * visibleLines / totalLines; if (totalLines == visibleLines) vPosition = 1; else vPosition = actualTopLine * 1000 / (totalLines-visibleLines); } } //////////////////////////////////////////////////////////////////////// // // SPC: VCalculateValues() // -- that method converts the GEM representation of the slider // -- into the document representation // //////////////////////////////////////////////////////////////////////// void GEMwindow::VCalculateValues() { // We shouldn't change visibleLines! The user cannot manipulate the size of // the slider knob anyway. // //visibleLines = vSize * totalLines / 1000; actualTopLine = vPosition*(totalLines-visibleLines)/1000; } //////////////////////////////////////////////////////////////////////// // // SPC: VFlushSlider() // -- is the counterpart of Flush(), // -- and is used to flush any changes in the internal slider // -- representation through to GEM; it doesn't need to be redefined // //////////////////////////////////////////////////////////////////////// void GEMwindow::VFlushSlider() { if (handle) { VCalculateGEMvalues(); // ensures correctness if (IsCreated()) { wind_set(handle, WF_VSLIDE, vPosition, 0, 0, 0); wind_set(handle, WF_VSLSIZE, vSize, 0, 0, 0); } } } //////////////////////////////////////////////////////////////////////// // // SPC: VSlidered(int newPos) // -- this function is called by the application manager, if // -- the user has moved the vertical slide box to a new position; // -- the default action performed by it is to store the new value, // -- then to call VCalculateValues(), and then to return // -- the value RedrawMe to the caller // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::VSlidered(int newPos) { vPosition = newPos; VCalculateValues(); VAlignSlider(); // let the client decide VFlushSlider(); return RedrawMe; } //////////////////////////////////////////////////////////////////////// // // SPC: LineUp() // -- this method will be called, if the user clicked the up arrow // -- of the vertical slider, the result is a decraesed actualTopLine // -- value and a redraw request // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::LineUp() { if (actualTopLine > 0) { actualTopLine--; VAlignSlider(); VFlushSlider(); return RedrawMe; } return ContinueInteraction; } //////////////////////////////////////////////////////////////////////// // // SPC: LineDown() // -- this method will be called, if the user clicked the down arrow // -- of the vertical slider, the result is an incraesed actualTopLine // -- value and a redraw request // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::LineDown() { if (actualTopLine < totalLines - visibleLines) { actualTopLine++; VAlignSlider(); VFlushSlider(); return RedrawMe; } return ContinueInteraction; } //////////////////////////////////////////////////////////////////////// // // SPC: PageUp() and PageDown() // -- this methods (de|in)craese the actualTopLine value and change // -- the contents of the window like in LineUp (Down) // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::PageUp() { if (actualTopLine > 0) { actualTopLine = (actualTopLine>=visibleLines)? actualTopLine-visibleLines : 0; VAlignSlider(); VFlushSlider(); return RedrawMe; } return ContinueInteraction; } GEMfeedback GEMwindow::PageDown() { if (actualTopLine < totalLines - visibleLines) { actualTopLine = (actualTopLine+visibleLines > totalLines-visibleLines)? totalLines-visibleLines : actualTopLine+visibleLines; VAlignSlider(); VFlushSlider(); return RedrawMe; } return ContinueInteraction; } //////////////////////////////////////////////////////////////////////// // // SPC: VAlignSlider() // -- this is a place holder for a function that may be redefined // -- be any client class (or not), it is called after any change // -- to the slider made by the user; when the member is executed, // -- it should take the values of visibleLines, // -- totalLines and actualTopLine as input and generate // -- a new actualTopLine, e.g., align the top of a text line with // -- the top line of the work area of the window; if any value of // -- the slider representation changed, then TRUE should be returned // -- to tell the called, that a redraw is needed // //////////////////////////////////////////////////////////////////////// bool GEMwindow::VAlignSlider() { // do nothing return FALSE; // i.e., nothing changed, no redraw is needed } //////////////////////////////////////////////////////////////////////// // // SPC: SetVisibleColumns(int noOfLines) // -- change the number of visible columns, this change will be // -- flushed through to the GEM window // //////////////////////////////////////////////////////////////////////// void GEMwindow::SetVisibleColumns(int noOfColumns) { Require(noOfColumns >= 0); Require(noOfColumns <= totalColumns); visibleColumns = noOfColumns; if (actualLeftColumn > totalColumns - visibleColumns) actualLeftColumn = totalColumns - visibleColumns; HAlignSlider(); HFlushSlider(); } void GEMwindow::SetTotalColumns(int noOfColumns) { Require(noOfColumns >= 0); bool change = FALSE; totalColumns = noOfColumns; if (noOfColumns < visibleColumns) { visibleColumns = totalColumns; change = TRUE; } if (actualLeftColumn > totalColumns - visibleColumns) { actualLeftColumn = totalColumns - visibleColumns; change = TRUE; } HAlignSlider(); HFlushSlider(); if (change) Flush(); } void GEMwindow::SetLeftColumn(int noOfColumn) { Require(noOfColumn >= 0); Require(noOfColumn <= totalColumns - visibleColumns); if (actualLeftColumn != noOfColumn) { actualLeftColumn = noOfColumn; HAlignSlider(); HFlushSlider(); Flush(); } } //////////////////////////////////////////////////////////////////////// // // SPC: HCalculateGEMvalues() and HCalculateValues(): // -- use the members totalColumns, visibleColumns and // -- actualLeftColumn to calculate the size and position of the GEM // -- slider and vice versa; // //////////////////////////////////////////////////////////////////////// void GEMwindow::HCalculateGEMvalues() { if (totalColumns>0) { hSize = 1000 * visibleColumns / totalColumns; if (totalColumns == visibleColumns) hPosition = 1; else hPosition = actualLeftColumn * 1000 / (totalColumns-visibleColumns); } } //////////////////////////////////////////////////////////////////////// // // SPC: HCalculateValues() // -- that method converts the GEM representation of the slider // -- into the document representation // //////////////////////////////////////////////////////////////////////// void GEMwindow::HCalculateValues() { // We shouldn't change visibleColumns! The user cannot manipulate the size of // the slider knob anyway. // //visibleColumns = hSize * totalColumns / 1000; actualLeftColumn = hPosition*(totalColumns-visibleColumns)/1000; } //////////////////////////////////////////////////////////////////////// // // SPC: HFlushSlider() // -- is the counterpart of GEMwindow::Flush(), // -- and is used to flush any changes in the internal slider // -- representation through to GEM; it doesn't need to be redefined // //////////////////////////////////////////////////////////////////////// void GEMwindow::HFlushSlider() { if (handle) { HCalculateGEMvalues(); // ensures correctness if (IsCreated()) { wind_set(handle, WF_HSLIDE, hPosition, 0, 0, 0); wind_set(handle, WF_HSLSIZE, hSize, 0, 0, 0); } } } //////////////////////////////////////////////////////////////////////// // // SPC: HSlidered(int newPos) // -- this function is called by the application manager, if // -- the user has moved the horizontal slide box to a new position; // -- the default action performed by it is to store the new value, // -- then to call HCalculateValues(), then to return the // -- value RedrawMe to the caller // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::HSlidered(int newPos) { hPosition = newPos; HCalculateValues(); HAlignSlider(); // let the client decide HFlushSlider(); return RedrawMe; } //////////////////////////////////////////////////////////////////////// // // SPC: ColumnLeft() // -- this method will be called, if the user clicked the left arrow // -- of the horizontal slider, the result is a decraesed // -- actualLeftColumn value and a RedrawMe request to the caller // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::ColumnLeft() { if (actualLeftColumn > 0) { actualLeftColumn--; HAlignSlider(); HFlushSlider(); return RedrawMe; } return ContinueInteraction; } //////////////////////////////////////////////////////////////////////// // // SPC: ColumnRight() // -- this method will be called, if the user clicked the right arrow // -- of the horizontal slider, the result is an incraesed // -- actualLeftColumn value // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::ColumnRight() { if (actualLeftColumn < totalColumns - visibleColumns) { actualLeftColumn++; HAlignSlider(); HFlushSlider(); return RedrawMe; } return ContinueInteraction; } //////////////////////////////////////////////////////////////////////// // // SPC: PageLeft() and PageRight() // -- this methods (de|in)craese the actualLeftColumn value and change // -- the contents of the window like in ColumnLeft (Right) // //////////////////////////////////////////////////////////////////////// GEMfeedback GEMwindow::PageLeft() { if (actualLeftColumn > 0) { actualLeftColumn = (actualLeftColumn>=visibleColumns)? actualLeftColumn-visibleColumns : 0; HAlignSlider(); HFlushSlider(); return RedrawMe; } return ContinueInteraction; } GEMfeedback GEMwindow::PageRight() { if (actualLeftColumn < totalColumns-visibleColumns) { actualLeftColumn = (actualLeftColumn+visibleColumns > totalColumns-visibleColumns) ? totalColumns - visibleColumns : actualLeftColumn + visibleColumns; HAlignSlider(); HFlushSlider(); return RedrawMe; } return ContinueInteraction; } //////////////////////////////////////////////////////////////////////// // // SPC: HAlignSlider() // -- this is a place holder for a function that may be redefined // -- be any client class (or not), it is called after any change // -- to the slider made by the user; when the member is executed, // -- it should take the values of visibleColumns, // -- totalColumns and actualLeftColumn as input and generate // -- a new actualLeftColumn, e.g., align the left column of a text line // -- with the beginning of the leftmost column of the work area of the // -- window // //////////////////////////////////////////////////////////////////////// bool GEMwindow::HAlignSlider() { return FALSE; } bool GEMwindow::IsOpen() const { return created && opened; }