This article contains the following executables: DFLT15.ARC D15TXT.ARC
This month continues the development of D-Flat++ with the base DFWindow class from which all other windows derive. Because this is the first issue of the new year, I'll begin by reviewing what D-Flat++ is and where it came from. About two years ago I went looking for a C function library that implemented the CUA interface for DOS text-mode applications. CUA is the common term for the menu-bar/pop-down, menu/dialog-box, mouse/keyboard user interface common to so many applications and operating environments. Windows and Presentation Manager are CUA-compliant in their graphical user interfaces. Most Microsoft and Borland text-mode programs use the CUA conventions. Even if it is not perfect, CUA could have the beneficial effect of killing all those stupid look-and-feel lawsuits. More about them later. If your application uses CUA, then no one--not even those nit-picking magazine reviewers--can criticize the user interface. Good or bad, it's a standard.
In my search, I found a few DOS text-mode CUA libraries, but each of them was either more or less than what I needed. So, with only a smattering of Windows programming experience, I decided to build such a library, one that uses an event-driven, message-based programming model similar to the one Windows uses. Thus, D-Flat was born. It started out to be a simple, small package to support my requirements, and I decided to publish it in the column. Reader response was positive, and the library grew accordingly, going through 15 versions and taking a year and a half to publish. Many readers asked about a C++ version, and those requests launched an experiment to see how the event-driven, message-based model fits into the C++ object-oriented paradigm. D-Flat++ was the result.
The previous two columns dispensed with the DF++ desktop and the platform-dependent device objects. This month we look at the portability layer to normalize the code for different compilers and the definition of the base DFWindow class.
Listing One, page 136, is dflatdef.h, a header file that defines global values and converts Borland C++ idioms to Microsoft C++. The WndType enumerated type identifies the window types for the various window classes in the DF++ class hierarchy. You can see from the list which window types are in the design so far. I'll probably add to it as the project continues. The Bool enum and the min and max macros are defined here. The statements that follow allow me to compile the DF++ that I developed under Borland C++ with Microsoft C++. The differences are in function calls specific to the PC. The two compilers are compatible with respect to pure C++ code. This portability layer assures that the FP_SEG, FP_OFF, and MK_FP macros work the same as the functions that read and write I/O ports, access BIOS keyboard functions, and get and set interrupt vectors. The two compilers implement interrupt functions differently, and the INTERRUPTARGS macro supports the difference, as do some compile-time conditionals wherever the program declares pointers to interrupt functions. The peek and poke macros in dflatdef.h provide those functions for Microsoft C users.
Later, when I port DF++ to other C++ compilers, this file is where the majority of the changes will occur. Also, as the project develops, I'll put any new portability dependencies here.
Listing Two, page 136, is dfwindow.h, the header file that defines the DFWindow base class, the class from which all DF++ windows will derive. The class encapsulates all that is common to all windows, regardless of their type. The header file defines the values for a window's attribute flags. Each flag is its own bit position, and they specify whether a window may be moved or resized; whether it has a border, title, control box, min box, max box, shadow, menu bar, status bar, or scroll bars; whether it saves the video memory it occupies or simply repaints itself on request; whether its display is clipped to the client area of its parent; and whether or not it is a component of its parent's frame, such as a scroll bar or status bar.
The dfwindow.h file defines window-display characteristics such as the structure for specifying window colors, the colors for a window's shadow, and the text characters that form a window's frame. The DFWindow class definition is the base class for all windows. It includes the data members that specify the window type, its size and screen position, title, attributes and state words, video save buffer address, colors, and the address of the window's parent window. There is a list head for a linked list of child windows, and next and previous pointers to adjacent sibling windows.
Then there are the member functions. A DFWindow object has several constructors which allow users to instantiate a window by combinations of parameters that include the title, size, position, and parent. There are functions that allow derived windows to write characters and strings to the window's video space. There are functions that both return the window's position and size, colors, parent, attributes, state, and type, and that change those things. Lastly, there are the API functions, which are the equivalent of messages in the D-Flat message-based programming model.
Listing Three, page 138, is dfwindow.cpp, the source code for the DF-Window member functions that are not inline. These functions construct and destroy the window, open and close it, display and hide it, and process its messages, including moving, resizing, minimizing, maximizing, restoring the window, and processing the keyboard and mouse messages that are passed on by derived window classes.
You must understand the relationships between constructing a window and opening it and destroying and closing it. A program constructs a window by instantiating it as an object--either as a global object, an object within the scope of a brace-surrounded block, or with the C++ new operator. The window's constructor also opens the window. The program destroys the window by allowing it to go out of scope or, in the case of a window constructed with the new operator, by using the C++ delete operator. The window's destructor also closes the window. The program can close a constructed window without destroying it. The program can then reopen the same window without invoking the window's constructor. A constructed window is one that has been instantiated. An open window is one that is available for use by the program. This distinction is necessary because of the CUA convention that allows the user to close a window with a keyboard or mouse action. This could happen in a manner asynchronous to the program's instantiation of the window and the window going out of scope. It is not possible to define a C++ class with objects declared only with the new operator. Therefore, because DF++ cannot enforce a convention that says users will only instantiate windows with the new operator, DF++ cannot assume that it can always use the delete operator when the user closes the window. Furthermore, there is no way for the system to know where the using program might save the address of the instantiated window, so there is no way to set it to NULL. It would be cumbersome to require a window to notify its software "owner" that it has been destroyed from within as the result of a user action. For this reason, DF++ implements a model where the window is constructed and the window may be closed prior to destruction. The C++ object still exists, but the logical window is closed. A window knows that it is being closed and will receive no further event messages, even though the object is still within scope.
To further complicate matters, a constructed open window may be visible or it may be hidden. A hidden window is not in the view of the user and will, therefore, receive no device-related event messages.
You will learn in later columns how the pop-down menu system takes advantage of this behavior. The system constructs all of the pop-down menus when it constructs the menu bar. Then it opens and closes the pop-down menu windows when the user selects them.
D-Flat++ is available as an early version from the DDJ Forum libraries on CompuServe and M&T Online or directly from me. Send a stamped, addressed diskette mailer and a formatted 360K or 720K diskette to me at Dr. Dobb's Journal, 411 Borel Ave., San Mateo, CA 94402-3522. Specify that you want D-Flat++. If you also want the D-Flat C library, specify that, too. The software is free, but if you wish, you can participate in our Careware program by including a dollar, which I will donate to the Brevard County Food Bank. A word now about that.
Your support of careware in the past year helped the Food Bank to provide much-needed assistance this past summer to the victims of Hurricane Andrew in southern Florida. There are a lot of people still staying in tents and wondering where they will live and where they will work. Imagine owning a mortgaged parcel of ground with a pile of rubble in a neighborhood of rubble as far as the eye can see. If you are one of the lucky ones, you have an insurance check in your pocket. How do you rebuild? Why would you? You lived there because there was a cooperative society consisting of neighbors, shops, schools, and jobs. Now it's all gone, reduced to debris. I went there and saw it. I do not have the words to describe it. The effects of this tragedy will be felt for a long time to come. The folks at the Food Bank asked me to thank you for your support of their effort to make the recovery just a little bit easier.
C++ Programming Style by Tom Cargill (Addison-Wesley, 1992) is yet another addition to the growing body of C++ literature in the new generation of good C++ books. The author takes a unique approach. He presents a number of programs taken from what we can presume to be the earlier generation of C++ books when things were not quite so good, but from the works of some respected authors. Then he proceeds to critique the code, pointing out where it is flawed, identifying what needs to be fixed, and rewriting it using the C++ styles he recommends. Each example makes a point about a particular issue of style, and every one makes good sense.
The examples show how C++ programmers can misuse such things as inheritance, virtual functions, and function overloading, applying them in ways that might work but that do not appropriately model what the objects represent. You learn the essential differences between value and behavior, implementation and interface, and data and functions in a class design. You learn the inherent strength of a loosely coupled class design. You learn to use default arguments instead of function overloading. You are lured away from clever operator overloading. The author develops one-liner rules that will lead you into a style of programming that adds durability and clarity to your design. For example, "When overloading operator =, remember x=x." If that doesn't make sense, then you need this book.
One of the most valuable of Cargill's rules is the one that says, "Do not try to learn the semantics of multiple inheritance from your compiler." The author cites his experience with three compilers, none of which worked properly with virtual base classes. During this time when C++ is not formally defined and the compilers are subject to the vendor's interpretation of whatever ambiguous language definition does exist, that rule might be simplified to say, "Do not try to learn anything from your compiler."
Cargill is a good influence on C++ programmers. This book moved me to examine some of my own work and change it. The examples went right to the heart of some of my habits, revealing coding idioms better expressed in other ways. This is highly recommended reading. No matter how good a C++ programmer you are, you will be a better one after you read this book.
Write to your Congressman or woman. Insist that the next confirmation hearing for a Supreme Court justice include an in-depth investigation into the nominee's computer literacy. We need folks on the bench who understand the complexities and intricacies of computers and the law. I read today that Lotus won another round in its look-and-feel lawsuit against Borland's alleged infringement of the long-since obsolete Lotus user interface. Borland promises to press on with appeals. Lotus vows to carry it all the way to the Supreme Court. When it gets there, wouldn't it be just dandy if there was someone on the bench who understood what the heck they were talking about?
Copyright © 1993, Dr. Dobb's JournalThe Portability Layer
The DFWindow Class
How to Get the Source Code
Book Report: C++ Programming Style
Clarence and Friends
_C PROGRAMMING COLUMN_
by Al Stevens
[LISTING ONE]
// -------- dflatdef.h
#ifndef DFLATDEF_H
#define DFLATDEF_H
// -------- window class types
enum WndType {
DFlatWindow,
ApplicationWindow,
TextboxWindow,
FrameWindow,
ScrollbarWindow,
MenubarWindow,
ListboxWindow,
PopdownWindow,
EditboxWindow,
DialogboxWindow,
PushButtonWindow,
RadioButtonWindow,
StatusbarWindow
};
enum Bool {False, True};
inline int max(int a, int b) { return a > b ? a : b; }
inline int min(int a, int b) { return a < b ? a : b; }
// ----- portablility layer for MSC C++
#ifdef MSC
#define keyhit kbhit
#undef FP_OFF
#undef FP_SEG
#undef MK_FP
#define FP_OFF(p) ((unsigned)(p))
#define FP_SEG(p) ((unsigned)((unsigned long)(p) >> 16))
#define MK_FP(s,o) ((void far *) \
(((unsigned long)(s) << 16) | (unsigned)(o)))
#define outp _outp
#define inp _inp
#define bioskey _bios_keybrd
#define getvect(v) _dos_getvect(v)
#define setvect(v,f) _dos_setvect(v,f)
#define INTERRUPTARGS void
#else
#define INTERRUPTARGS ...
#endif
#define poke(a,b,c) (*((int far*)MK_FP((a),(b))) = (int)(c))
#define peek(a,b) (*((int far*)MK_FP((a),(b))))
#endif
[LISTING TWO]
// ------------ dfwindow.h
#ifndef DFWINDOW_H
#define DFWINDOW_H
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <stdlib.h>
#include "dflatdef.h"
#include "rectangl.h"
#include "strings.h"
// -------- window attribute flags
const int MOVEABLE = 0x0001;
const int SIZEABLE = 0x0002;
const int BORDER = 0x0004;
const int TITLEBAR = 0x0008;
const int CONTROLBOX = 0x0010;
const int MINBOX = 0x0020;
const int MAXBOX = 0x0040;
const int SHADOW = 0x0080;
const int SAVESELF = 0x0100;
const int NOCLIP = 0x0200;
const int MENUBAR = 0x0400;
const int STATUSBAR = 0x0800;
const int VSCROLLBAR = 0x1000;
const int HSCROLLBAR = 0x2000;
const int FRAMEWND = 0x4000;
// --------------- Color Macros
enum Colors {
BLACK,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY,
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
};
// ------ window shadow attributes
const unsigned char ShadowFG = DARKGRAY;
const unsigned char ShadowBG = BLACK;
// ----- minimized icon dimensions
const int IconWidth = 10;
const int IconHeight = 3;
// --------------- border characters
const unsigned char FOCUS_NW = '\xc9';
const unsigned char FOCUS_NE = '\xbb';
const unsigned char FOCUS_SE = '\xbc';
const unsigned char FOCUS_SW = '\xc8';
const unsigned char FOCUS_SIDE = '\xba';
const unsigned char FOCUS_LINE = '\xcd';
const unsigned char NW = '\xda';
const unsigned char NE = '\xbf';
const unsigned char SE = '\xd9';
const unsigned char SW = '\xc0';
const unsigned char SIDE = '\xb3';
const unsigned char LINE = '\xc4';
// ----------------- title bar characters
const unsigned char CONTROLBOXCHAR = '\xf0';
const unsigned char MAXPOINTER = 24;
const unsigned char MINPOINTER = 25;
const unsigned char RESTOREPOINTER = 18;
// ----------- window states
enum WndState {
OPENING,
ISRESTORED,
ISMINIMIZED,
ISMAXIMIZED,
ISCLOSING,
CLOSED
};
// ---------- window colors
struct Color {
Colors fg, bg; // standard colors
Colors sfg, sbg; // selected text colors
Colors ffg, fbg; // window frame colors
Colors hfg, hbg; // highlighted text colors
};
class Application;
class StatusBar;
class PopDown;
class DFWindow {
protected:
WndType windowtype; // window type
int prevmouseline; // holders for
int prevmousecol; // mouse coordinates
private:
String *title; // window title
// -------------- window attributes
int restored_attrib; // attributes when restored
Bool clipoverride; // True to override clipping
Rect restored_rc; // restored state rect
// ------- for painting overlapping windows
void PaintOverLappers();
// ----- control menu
PopDown *ctlmenu;
void OpenCtlMenu();
void DeleteCtlMenu();
// --------- common window contructor code
void InitWindow(char *ttl,
int lf, int tp, int ht, int wd, DFWindow *par);
void InitWindow(int lf, int tp, int ht, int wd,
DFWindow *par);
virtual void SetColors();
void Enqueue();
void Dequeue();
Bool ClipParent(int &x, int y, String *ln);
void WriteChar(int ch, int x, int y,
Rect &rc, int fg, int bg);
void WriteString(String &ln, int x, int y,
Rect &rc, int fg, int bg);
Rect PositionIcon();
friend class StatusBar;
protected:
// --------------- video memory save data
char *videosave; // video save buffer
Bool visible; // True = window has been shown
int attrib; // Window attribute flags
Bool DblBorder; // True = dbl border on focus
Color colors; // window colors
WndState windowstate; // Restored, Maximized, Minimized, Closing
Rect rect; // window coordinates (0/0 to 79/24)
char clearch; // for clearing the window
// ------ previous capture focus handle
DFWindow *prevcapture;
// -------------- window geneology
DFWindow *parent; // parent window
// -------- children
DFWindow *first; // first child window
DFWindow *last; // last child window
// -------- siblings
DFWindow *next; // next sibling window
DFWindow *prev; // previous sibling window
// -------- system-wide
void NextSiblingFocus();
void WriteClientString(String &ln,int x,int y,int fg,int bg);
void WriteWindowString(String &ln,int x,int y,int fg,int bg);
void WriteWindowChar(int ch,int x,int y,int fg,int bg);
void WriteClientChar(int ch,int x,int y,int fg,int bg);
// ------------- client window coordinate adjustments
virtual void AdjustBorders();
int BorderAdj; // adjust for border
int TopBorderAdj; // adjust for top border
int BottomBorderAdj; // adjust for bottom border
// -----
Bool HitControlBox(int x, int y)
{ return (Bool)(x-Left() == 2 && y-Top() == 0 &&
(attrib & CONTROLBOX)); }
friend void DispatchEvents(Application *ApWnd);
friend DFWindow *MouseWindow();
friend DFWindow *inWindow(int x, int y, int &fg, int &bg);
public:
// -------- constructors
DFWindow(char *ttl, int lf, int tp, int ht, int wd,
DFWindow *par)
{ InitWindow(ttl, lf, tp, ht, wd, par); }
DFWindow(char *ttl, int ht, int wd, DFWindow *par)
{ InitWindow(ttl, -1, -1, ht, wd, par); }
DFWindow(int lf, int tp, int ht, int wd, DFWindow *par)
{ InitWindow(lf, tp, ht, wd, par); }
DFWindow(int ht, int wd, DFWindow *par)
{ InitWindow(-1, -1, ht, wd, par); }
DFWindow(char *ttl, DFWindow *par = NULL)
{ InitWindow(ttl, 0, 0, -1, -1, par); }
// -------- destructor
virtual ~DFWindow()
{ if (windowstate != CLOSED) CloseWindow(); }
// ------- window dimensions and position
Rect WindowRect() { return rect; }
Rect ShadowedRect();
int Right() { return rect.Right(); }
int Left() { return rect.Left(); }
int Top() { return rect.Top(); }
int Bottom() { return rect.Bottom(); }
int Height() { return Bottom() - Top() + 1; }
int Width() { return Right() - Left() + 1; }
// ------ client space dimensions and position
Rect ClientRect();
int ClientRight() { return Right()-BorderAdj; }
int ClientLeft() { return Left()+BorderAdj; }
int ClientTop() { return Top()+TopBorderAdj; }
int ClientBottom() { return Bottom()-BottomBorderAdj; }
int ClientHeight() { return Height()-TopBorderAdj-
BottomBorderAdj; }
int ClientWidth() { return Width()-BorderAdj*2; }
DFWindow *Parent() { return parent; }
Bool isVisible() { return visible; }
int Attribute() { return attrib; }
void SetAttribute(int atr) { attrib |= atr; AdjustBorders(); }
void ClearAttribute(int atr) { attrib &= ~atr; AdjustBorders(); }
WndType WindowType() { return windowtype; }
// ----- Control Menu messages
void CtlMenuMove();
void CtlMenuSize();
// -------- API messages
virtual void OpenWindow();
virtual void CloseWindow();
virtual void Show();
virtual void Hide();
virtual Bool SetFocus();
virtual void ResetFocus();
virtual void EnterFocus(DFWindow *child) {}
virtual void LeaveFocus(DFWindow *child) {}
void CaptureFocus();
void ReleaseFocus();
virtual void Paint();
virtual void Paint(Rect rc);
virtual void Border();
virtual void Shadow();
virtual void Title();
virtual void ClearWindow();
virtual void ShiftChanged(int sk);
virtual void Keyboard(int key);
virtual void DoubleClick(int mx, int my);
virtual void LeftButton(int mx, int my);
virtual void ButtonReleased(int, int);
virtual void MouseMoved(int, int) {}
virtual void Move(int x, int y);
virtual void Size(int x, int y);
virtual void ParentSized(int, int) {}
virtual void ClockTick() {}
void Minimize();
void Maximize();
void Restore();
WndState State() { return windowstate; }
Rect &VisibleRect();
Colors CLientFG() { return colors.fg; }
Colors ClientBG() { return colors.bg; }
Colors SelectedFG() { return colors.sfg; }
Colors SelectedBG() { return colors.sbg; }
Colors FrameFG() { return colors.ffg; }
Colors FrameBG() { return colors.fbg; }
Colors HighlightFG() { return colors.ffg; }
Colors HighlightBG() { return colors.fbg; }
};
inline DFWindow *inWindow(int x, int y)
{
int fg, bg;
return inWindow(x, y, fg, bg);
}
inline void WriteClientString(String &ln,int x,int y,int fg,int bg)
{
WriteString(ln,x+ClientLeft(),y+ClientTop(),ClientRect(),fg,bg);
}
inline void WriteWindowString(String &ln,int x,int y,int fg,int bg)
{
WriteString(ln,x+Left(),y+Top(),ShadowedRect(),fg,bg);
}
inline void WriteWindowChar(int ch,int x,int y,int fg,int bg)
{
WriteChar(ch, x+Left(), y+Top(),ShadowedRect(), fg, bg);
}
inline void WriteClientChar(int ch,int x,int y,int fg,int bg)
{
WriteChar(ch, x+ClientLeft(),y+ClientTop(),ClientRect(),fg,bg);
}
#endif
[LISTING THREE]
// ------------ dfwindow.cpp
#include "dflatpp.h"
#include "frame.h"
#include "desktop.h"
// -------- common constructor initialization code
void DFWindow::InitWindow(int lf, int tp,int ht, int wd, DFWindow *par)
{
windowtype = DFlatWindow;
if (lf == -1)
lf = (desktop.screen().Width()-wd)/2;
if (tp == -1)
tp = (desktop.screen().Height()-ht)/2;
if (ht == -1)
ht = desktop.screen().Height();
if (wd == -1)
wd = desktop.screen().Width();
attrib = restored_attrib = 0;
title = NULL;
ctlmenu = NULL;
videosave = NULL;
visible = False;
clipoverride = False;
first = NULL;
last = NULL;
next = NULL;
prev = NULL;
prevcapture = NULL;
parent = par;
BorderAdj = TopBorderAdj = BottomBorderAdj = 0;
Rect rcc(lf, tp, lf+wd-1, tp+ht-1);
restored_rc = rect = rcc;
SetColors();
clearch = ' ';
DblBorder = True;
Enqueue();
if (parent == NULL)
SetAttribute(SAVESELF);
windowstate = ISRESTORED;
}
void DFWindow::InitWindow(char *ttl, int lf, int tp,
int ht, int wd, DFWindow *par)
{
InitWindow(lf, tp, ht, wd, par);
attrib |= TITLEBAR;
title = new String(ttl);
}
void DFWindow::OpenWindow()
{
if (windowstate == CLOSED)
InitWindow(*title, Left(), Top(),Height(), Width(), parent);
}
void DFWindow::CloseWindow()
{
windowstate = ISCLOSING;
Hide();
// ------- close window's children
DFWindow *Wnd = first;
while (Wnd != NULL) {
Wnd->CloseWindow();
Wnd = Wnd->next;
}
// ------ delete this window's memory
if (title != NULL)
delete title;
if (videosave != NULL)
delete videosave;
DeleteCtlMenu();
if (this == desktop.InFocus()) {
if (desktop.FocusCapture() == this)
ReleaseFocus();
else if (parent == NULL ||
parent->windowstate == ISCLOSING)
desktop.SetFocus(parent ? parent : NULL);
else {
NextSiblingFocus();
if (this == desktop.InFocus())
if (!parent->SetFocus())
desktop.SetFocus(NULL);
}
}
Dequeue();
windowstate = CLOSED;
}
// -------- set the fg/bg colors for the window
void DFWindow::SetColors()
{
colors.fg =
colors.sfg =
colors.ffg =
colors.hfg = WHITE;
colors.bg =
colors.sbg =
colors.fbg =
colors.hbg = BLACK;
}
// ---------- display the window
void DFWindow::Show()
{
if (attrib & SAVESELF) {
Rect rc = ShadowedRect();
if (videosave == NULL) {
int sz = rc.Height() * rc.Width() * 2;
videosave = new char[sz];
}
if (!visible)
desktop.screen().GetBuffer(rc, videosave);
}
visible = True;
clipoverride = True;
Paint();
Border();
Shadow();
clipoverride = False;
if (windowstate != ISMINIMIZED) {
// --- show the children of this window
DFWindow *Wnd = first;
while (Wnd != NULL) {
if (Wnd->windowstate != ISCLOSING)
Wnd->Show();
Wnd = Wnd->next;
}
}
}
Rect DFWindow::ShadowedRect()
{
Rect rc = rect;
if (attrib & SHADOW) {
rc.Right()++;
rc.Bottom()++;
}
return rc;
}
void DFWindow::Hide()
{
if (visible) {
Rect rc = rect;
Bool HasShadow = (Bool)((attrib & SHADOW) != 0);
if (HasShadow) {
rc.Bottom()++;
rc.Right()++;
}
visible = False;
// ----- hide the children
DFWindow *Wnd = first;
while (Wnd != NULL) {
Wnd->Hide();
Wnd = Wnd->next;
}
if (videosave != NULL) {
desktop.screen().PutBuffer(rc, videosave);
delete videosave;
videosave = NULL;
}
else if (parent != NULL) {
if (parent->isVisible()) {
parent->Paint(ShadowedRect());
PaintOverLappers();
}
}
}
}
void DFWindow::Keyboard(int key)
{
switch (key) {
case CTRL_F4:
CloseWindow();
break;
case ALT_HYPHEN:
OpenCtlMenu();
break;
default:
// --- send all unprocessed keystrokes
// to the parent window
if (parent != NULL)
parent->Keyboard(key);
break;
}
}
void DFWindow::ShiftChanged(int sk)
{
if (parent != NULL)
parent->ShiftChanged(sk);
}
void DFWindow::DoubleClick(int mx, int my)
{
if (HitControlBox(mx, my))
CloseWindow();
}
void DFWindow::LeftButton(int mx, int my)
{
if (my == Top()) {
// ----- hit the top border
int x = mx-Left();
int wd = Width();
if (x == wd-2) {
// ---- hit the restore or maximize box
if (windowstate != ISRESTORED)
Restore();
else if (attrib & MAXBOX)
Maximize();
}
else if (x == wd-3) {
// ----- hit the minimize box
if (windowstate != ISMINIMIZED &&
(attrib & MINBOX))
Minimize();
}
else if (HitControlBox(mx, my) &&
(attrib & CONTROLBOX))
// ------- hit the control box
OpenCtlMenu();
else if ((attrib & MOVEABLE) &&
windowstate != ISMAXIMIZED)
// ---- none of the above, move the window
new Frame(this, mx);
}
else if ((attrib & SIZEABLE) && windowstate == ISRESTORED)
if (mx == Right() && my == Bottom())
// --- hit the lower right corner, size the window
new Frame(this);
prevmouseline = my;
prevmousecol = mx;
}
void DFWindow::ButtonReleased(int, int)
{
prevmouseline = -1;
prevmousecol = -1;
}
Rect DFWindow::ClientRect()
{
Rect rc(ClientLeft(), ClientTop(),
ClientRight(), ClientBottom());
return rc;
}
// ------------ move a window
void DFWindow::Move(int x, int y)
{
int xdif = x - Left();
int ydif = y - Top();
if (xdif == 0 && ydif == 0)
return;
Bool wasVisible = visible;
if (wasVisible)
Hide();
int ht = Height();
int wd = Width();
rect.Left() = x;
rect.Top() = y;
rect.Right() = Left()+wd-1;
rect.Bottom() = Top()+ht-1;
if (windowstate == ISRESTORED)
restored_rc = rect;
DFWindow *Wnd = first;
while (Wnd != NULL) {
Wnd->Move(Wnd->Left()+xdif, Wnd->Top()+ydif);
Wnd = Wnd->next;
}
if (wasVisible)
Show();
}
// ------------ size a window
void DFWindow::Size(int x, int y)
{
int xdif = x - Right();
int ydif = y - Bottom();
if (xdif == 0 && ydif == 0)
return;
Bool wasVisible = visible;
if (wasVisible)
Hide();
rect.Right() = x;
rect.Bottom() = y;
if (windowstate == ISRESTORED)
restored_rc = rect;
DFWindow *Wnd = first;
while (Wnd != NULL) {
Wnd->ParentSized(xdif, ydif);
Wnd = Wnd->next;
}
if (wasVisible)
Show();
}
void DFWindow::Minimize()
{
if (windowstate == ISRESTORED)
restored_rc = rect;
Rect rc = PositionIcon();
Hide();
windowstate = ISMINIMIZED;
Move(rc.Left(), rc.Top());
Size(rc.Right(), rc.Bottom());
Show();
}
void DFWindow::Maximize()
{
restored_rc = rect;
Rect rc(0, 0, desktop.screen().Width()-1,desktop.screen().Height()-1);
if (parent != NULL)
rc = parent->ClientRect();
Hide();
windowstate = ISMAXIMIZED;
Move(rc.Left(), rc.Top());
Size(rc.Right(), rc.Bottom());
Show();
}
void DFWindow::Restore()
{
Hide();
Move(restored_rc.Left(), restored_rc.Top());
Size(restored_rc.Right(), restored_rc.Bottom());
windowstate = ISRESTORED;
if (this == desktop.InFocus())
Show();
else
SetFocus();
}
// ---- compute lower right icon space in a rectangle
static Rect LowerRight(Rect &prc)
{
Rect rc(prc.Right()-IconWidth,prc.Bottom()-IconHeight,0,0);
rc.Right() = rc.Left() + IconWidth-1;
rc.Bottom() = rc.Top() + IconHeight-1;
return rc;
}
// ----- compute a position for a minimized window icon
Rect DFWindow::PositionIcon()
{
Rect rc(desktop.screen().Width()-IconWidth,
desktop.screen().Height()-IconHeight,
desktop.screen().Width()-1,
desktop.screen().Height()-1);
if (parent != NULL) {
Rect prc = parent->rect;
rc = LowerRight(prc);
// --- search for icon available location
DFWindow *Wnd = parent->first;
while (Wnd != NULL) {
if (Wnd->windowstate == ISMINIMIZED) {
Rect rc1= Wnd->rect;
if (rc1.Left() == rc.Left() &&
rc1.Top() == rc.Top()) {
rc.Left() -= IconWidth;
rc.Right() -= IconWidth;
if (rc.Left() < prc.Left()+1) {
rc.Left() =
prc.Right()-IconWidth;
rc.Right() =
rc.Left()+IconWidth-1;
rc.Top() -= IconHeight;
rc.Bottom() -= IconHeight;
if (rc.Top() < prc.Top()+1)
return LowerRight(prc);
}
break;
}
}
Wnd = Wnd->next;
}
}
return rc;
}