Taquis - PC Base FFT Spectrum Analyzers, Oscilloscopes, Data Analysis, Data Acquisition, and Application Frameworks   AtOne Application Framework
"A Framework working with you, not against you..."

 
Home
Features
Programming
License
Downloads
Directions

Contact
info@taquis.com


 
 

Commonly used Classes

AtOne is an extensive application framework and within that framework are a host of very useful and commonly used classes. Before touching upon the details of GUI programming in AtOne it is worthwhile introducing these classes to you as you are likely to encounter some of then during later discussions and you will be glad to know of the existence of the others so that you need not re-invent the wheel. 
 

Handling Strings

Every GUI application framework seems to have it's own string class and AtOne is no exception. However you might find its style quite different from any other. In putting together the OsiString class I wanted a class in which I had the full range of functionality of the C string support, but the ability to use this functionality easily in-situ. I also wanted to support both unicode and MBCS (multi-byte character sets) using the one notation that was consistent with the C style libraries. By that I am refering to the name given to strings and characters. MFC, for example, uses TCHAR to designate a wide character which equates to a char in a non-unicode build. I wanted to have a naming convention in which strings where strings and nothing else. From this perspective AtOne uses the keyword character to designate a single character in a string and the keyword string to designate a pointer to a string. Internally these are macro defined as,

#ifdef _UNICODE

#define character unsigned short
#define string    unsigned short*

#else

#define character char
#define string    char*

#endif

The lowercase naming convention is consitent with the C standard libraries and intrinsic variable types (as strings are). Therefore if you use these types for strings and characters your code will compile correctly to unicode or MBCS depending upon the option you chose. Furthermore, you will find your code takes on a more readable appearance as a result of the keyword names. In cases where you would like to call the C string handling functions you can use macro over-ridden versions supplied with AtOne to ensure that the correct version is called under the string model used. These macros have the same name as the standard C functions except that they are prefixed with Osi_ (Osi_strcpy for example). You will find these macro definitions in common.hpp

The OsiString class definition is,

class OsiString
{
.
.
.
public:
  OsiString();
  OsiString(const OsiString& rString);
  OsiString(character aChar, int nRepeat = 1);
  OsiString(const string pString);
  OsiString(const string pString, int nLength);
#ifdef _UNICODE
  OsiString(const char* pString, int nCodePage = CP_ACP);
  OsiString(const char* pString, int nLength, int nCodePage = CP_ACP);
#endif
  ~OsiString();

  int               bufferLength() const;
  int               length() const;
  bool              isNull() const;
  void              null();
  character         characterAt(int nIndex) const;
  void              characterAt(int nIndex, character aChar);
  const string      buffer() const;

                    operator const string() const;
  character         operator [](int nIndex) const;
  const OsiString&  operator =(const OsiString& rString);
  const OsiString&  operator =(character aChar);
  const OsiString&  operator =(const string pString);
#ifdef _UNICODE
  const OsiString&  operator =(const char* pString);
  const OsiString&  assign(const char* pString, int nCodePage = CP_ACP);
#endif
  const OsiString&  operator +=(const OsiString& rString);
  const OsiString&  operator +=(character aChar);
  const OsiString&  operator +=(const string pString);
  friend OsiString  operator +(const OsiString& rString1, const OsiString& rString2);
  friend OsiString  operator +(const OsiString& rString, character aChar);
  friend OsiString  operator +(character aChar, const OsiString& rString);
  friend OsiString  operator +(const OsiString& rString1, const string pString2);
  friend OsiString  operator +(const string pString1, const OsiString& rString2);
  friend bool       operator ==(const string pString1, const OsiString& rString2);
  friend bool       operator !=(const string pString1, const OsiString& rString2);
  bool              operator ==(const OsiString& rString) const;
  bool              operator ==(const string pString) const;
  bool              operator !=(const OsiString& rString) const;
  bool              operator !=(const string pString) const;

  const OsiString&  toUpper();
  const OsiString&  toLower();
  const OsiString&  reverse();
  OsiString         mid(int nFirst, int nCount) const;
  OsiString         mid(int nFirst) const;
  OsiString         left(int nCount) const;
  OsiString         right(int nCount) const;
  OsiString         inclusiveSpan(const string pCharSet);
  OsiString         exclusiveSpan(const string pCharSet);
  const OsiString&  removePrefix(character nChar);
  const OsiString&  removePostfix(character nChar);

  // Access to string implementation buffer as "C" character array
  string            getBuffer(int nMinBufLength);
  void              releaseBuffer(int nNewLength = -1);

  //Concatentation
  const OsiString&  strcat(const string pString);
  const OsiString&  strncat(const string pString, int nMaxChars);

  //search for character
  string            strchr(character aChar, const string pStringNext = 0) const;
  string            strrchr(character aChar, const string pStringNext = 0) const;

  //string comparison
  int               strcmp(const string pString) const;
  int               strcmpi(const string pString) const;
  int               strncmp(const string pString, int nMaxChars) const;
  int               strncmpi(const string pString, int nMaxChars) const;
  int               strcoll(const string pString) const;

  //string copying
  const OsiString&  strcpy(const string pString);
  const OsiString&  strncpy(const string pString, int nMaxChars);

  //search for substring in string
  string            strstr(const string pString, const string pStringNext = 0) const;

  //string initialisation
  const OsiString&  strnset(int nFill, int nMaxChars);
  const OsiString&  strset(int nFill);

  //string token parsing
  string            strtok(bool bFirst, const string pDelimeters);

  //string duplication
  OsiString         strdup() const;

  //to lower case, to upper case and order reversing
  const OsiString&  strlwr();
  const OsiString&  strupr();
  const OsiString&  strrev();

  //Incrementing and decremening character pointers
  const string      strdec(const string pCurrent) const;
  const string      strinc(const string pCurrent) const;

  //string length
  int               strlen() const;

  //String formating : time, date, numbers and general formated output
  const OsiString&  strftime(int nMaxSize, const string pFormat, const struct tm* pTime);
  const OsiString&  strdate();
  const OsiString&  strtime();
  double            strtod(string* ppEnd) const;
  long              strtol(string* ppEnd, int nBase) const;
  unsigned long     strtoul(string* ppEnd, int nBase) const;
  int               sprintf(const string pFormat, ...);
  int               vsprintf(const string pFormat, va_list argList);
};

This string class has a very similar set of constructors and overloaded operators to the CString class in MFC. The main difference lies in the addition of members corresponding to the C string handling functions. Rather than come up with new names to methods corresponding to the C functions (that you would need to remember) I chose to give them the same name as the corresponding C function. Thus, if you familiar with C string manipulation you'll easily be able to use the OsiString class. The only thing to remember is that the methods will generally have differing parameter lists owing to the fact that the method is acting on this string and not an externally specified one (sprintf() for example, does not have the destination argument since the destination is the class instance string buffer). That aside, the inclusion of these methods gives the flexibility of C string manipulation which the convenience of C++ string memory management. The string class is declared and implemented in costr.hpp and costr.cpp respectively. 
 

Handling Files

I have always found C file handling far more flexible, convenient and easy to understand than the ANSI streams library in C++. In fact, whilst the ANSI streams library performs streaming adequately it lacks a vast range of functionality that is only accessible through the C libraries. For this reason I have added C++ wrappers arround the most useful (and portable) C file handling rountines. These include OsiFileStream for reading and writing to files, OsiFilePermission giving file access rights information, OsdFileAttributes giving general file attributes, OsiFileStatus giving status information about a file and or stream, and OsiDir for navigating directories. These file handling classes are declared and implemented in cofile.hpp and cofile.cpp respectively. 
 

Specifying Colours

GUI programming invariably involves the use of colour. WIN32 provide a multitude of ways to specify colour depending upon whether your using palettes, or simply specifying system colours. GuiColour is a class designed to unify the specification of colour under all these circumstances. The class definition for GuiColour is,

class GuiColour
{
private: 
  unsigned char     Red; 
  unsigned char     Green; 
  unsigned char     Blue; 
  unsigned char     Reserved; 

public:
  GuiColour();
  GuiColour(GuiSystemColour SystemColour);
  GuiColour(unsigned char nRed, unsigned char nGreen, unsigned char nBlue, GuiColourType aColourType = ColourTypeRGB);
  GuiColour(long nColourRef);
  GuiColour(const GuiColour& rColour);
  GuiColour(const GuiHsv& rColour);
  GuiColour(const GuiHls& rColour);

  unsigned char     red() const;
  void              red(unsigned char val);
  unsigned char     green() const;
  void              green(unsigned char val);
  unsigned char     blue() const;
  void              blue(unsigned char val);
  GuiHsv            toHSV() const;
  GuiHls            toHLS() const;
  int               index() const;
  GuiColourType     colourType() const;
  bool              isTransparent() const;
  const GuiColour&  operator =(const GuiColour& aColour);
                    operator long() const;
};

A unified colour specification is provided through the enumerated type GuiColourType which can be ColourTypeRGB (colour is specified as red green and blue components), ColourTypePaletteIndex (the red component specifies an index into a palette) or ColourTypePaletteRGB (the red, green and blue components specify an RGB colour mapped into the current palette). The enumerated type GuiSystemColour also provides a unified access to WIN32 system colours which can be one of,

SystemColourScrollbar            = COLOR_SCROLLBAR
SystemColourBackground           = COLOR_BACKGROUND
SystemColourActiveCaption        = COLOR_ACTIVECAPTION
SystemColourInactiveCaption      = COLOR_INACTIVECAPTION
SystemColourMenu                 = COLOR_MENU
SystemColourWindow               = COLOR_WINDOW
SystemColourWindowFrame          = COLOR_WINDOWFRAME
SystemColourMenuText             = COLOR_MENUTEXT
SystemColourWindowText           = COLOR_WINDOWTEXT
SystemColourCaptionText          = COLOR_CAPTIONTEXT
SystemColourActiveBorder         = COLOR_ACTIVEBORDER
SystemColourInactiveBorder       = COLOR_INACTIVEBORDER
SystemColourAppWorkspace         = COLOR_APPWORKSPACE
SystemColourHighlight            = COLOR_HIGHLIGHT
SystemColourHighlightText        = COLOR_HIGHLIGHTTEXT
SystemColourButtonFace           = COLOR_BTNFACE
SystemColourButtonShadow         = COLOR_BTNSHADOW
SystemColourGreyText             = COLOR_GRAYTEXT
SystemColourButtonText           = COLOR_BTNTEXT
SystemColourInactiveCaptionText  = COLOR_INACTIVECAPTIONTEXT
SystemColourButtonHighlight      = COLOR_BTNHIGHLIGHT
SystemColour3dDarkShadow         = COLOR_3DDKSHADOW
SystemColour3dLight              = COLOR_3DLIGHT
SystemColourInfoText             = COLOR_INFOTEXT
SystemColourInfoBackground       = COLOR_INFOBK

System colours can also be directly specified (ie. not using a GuiColour object instance) in standard graphics objects such as pens (GuiLogPen) and brushes (GuiLogBrush). This has the advantage that if the user changes any of the system colours while your application is running the pen and brush colours change in unison automatically. 

Another unique feature of colour specification in AtOne is the concept of transparent colour. Often in a GUI application you would like to be able to specify a transparent colour that if used in drawing will result in no action being taken. In AtOne you do just that by nominating a transparent colour using the GuiTransparentColour constructor. The colour nominated will then be assumed to be transparent henceforth. If you then, for example use that colour to specify a brush the brush will automatically evaluate to a null brush producing the expected behaviour. By treating colour in this way code can be dramatically simplified since transparency is just another colour requiring no special condition check code in your application. 

Beyond these features AtOne also supplies you with two other different colour models, HSV - Hue, Saturation and Value, and HLS - Hue,  Lightness and Saturation. These colour models are supported through the classes GuiHsv and GuiHls which support conversion to and from GuiColour. The addition of these colour models makes the implementation of colour gradients simple. 

The colour support in AtOne is declared and implemented in guicol.hpp and guicol.cpp respectively. 
 

Handling Resources

Typically WIN32 applications specify a significant proportion of their graphical and other resources in the form of resource scripts defining dialogs, menus, accelerators, resource strings and the like. To access resources usually requires two parameters : a module instance handle and a resource identifier. This is further complicated by the fact that WIN32 allows resource identifiers to be either numbers or strings. In AtOne the concept of resource identifier has been unified into a single class, namely GuiResId whose definition is,

class GuiResId
{
.
.
.
public:
  GuiResId(HINSTANCE hInstance = 0);
  GuiResId(const string sResName, HINSTANCE hInstance = 0);
  GuiResId(const string sResName, const string pNameAndPath);
  GuiResId(int nResId, HINSTANCE hInstance = 0);
  GuiResId(int nResId, const string pNameAndPath);
  GuiResId(const GuiResId& rCopy);
  virtual ~GuiResId();

  void            instance(HINSTANCE hInstance);
  void            name(const string pName);
  void            id(int nId);
  HINSTANCE       instance() const;
  const string    name() const;
  int             id() const;
                  operator const string() const;
  const GuiResId& operator =(const GuiResId& rResId);
  bool            operator ==(const GuiResId& rResId) const;
  bool            operator !=(const GuiResId& rResId) const;
};

GuiResId's are used extensively to specify the location of menu, accelerator and dialog template resources. In addition to resource Id's AtOne also provides a special class, GuiResString,  that mimmicks a string to load and access resource strings. 

Specifying Graphics Geometry

When programming in a GUI invironment at some point in time the geometry of your graphics must be specified and or manipulated. AtOne gives you a host of classes for this purpose. Points or co-ordinates are specified using the GuiPoint and GuiRealPoint classes. Note that GuiPoint is essentially equivalent to the WIN32 POINT structure with functionality added. We have an additional point structure GuiRealPoint in which the co-ordinates are doubles that is used in the various mapping classes that AtOne supports. Having this extra class avoids unecessary truncation of co-ordinates in complex graphical mappings. The class definition for GuiPoint is,

class GuiPoint
{
private:
  int             X;
  int             Y;

public:
  GuiPoint();
  GuiPoint(int u);
  GuiPoint(int x, int y);
  GuiPoint(const POINT& rPt);
  GuiPoint(const GuiPoint& rPt);
  GuiPoint(const GuiRealPoint& rPt);

  void            null();
  int             x() const;
  void            x(int val);
  int             y() const;
  void            y(int val);
  GuiPoint        maximum(const GuiPoint& rPt) const;
  GuiPoint        minimum(const GuiPoint& rPt) const;
  const GuiPoint& operator =(const GuiPoint& rPt);
  bool            operator !=(const GuiPoint& rPt) const;
  GuiPoint        operator %(int i) const;
  GuiPoint        operator *(int i) const;
  const GuiPoint& operator *=(int i);
  GuiPoint        operator +(const GuiPoint& rPt) const;
  const GuiPoint& operator +=(const GuiPoint& rPt);
  GuiPoint        operator -() const;
  GuiPoint        operator -(const GuiPoint& rPt) const;
  const GuiPoint& operator -=(const GuiPoint& rPt);
  GuiPoint        operator /(int i) const;
  bool            operator <(const GuiPoint& rPt) const;
  bool            operator <=(const GuiPoint& rPt) const;
  bool            operator ==(const GuiPoint& rPt) const;
  bool            operator >(const GuiPoint& rPt) const;
  bool            operator >=(const GuiPoint& rPt) const;
  bool            isNull() const;
                  operator POINT&() const;
                  operator POINT*() const;
};

It is common in WIN32 to have to specify the size or dimension of some graphical operation and that this is usually done with a SIZE structure. AtOne caters for size specification with the GuiDimension class which can be used interchangeably with the SIZE structure. The class definition for GuiDimension is,

class GuiDimension
{
private:
  int                 Width;
  int                 Height;

public:
  GuiDimension();
  GuiDimension(long nDimension);
  GuiDimension(const SIZE& rDimension);
  GuiDimension(int nWidth, int nHeight);
  GuiDimension(const GuiDimension& rDimension);

  void                null();
  int                 width() const;
  void                width(int val);
  int                 height() const;
  void                height(int val);
  const GuiDimension& operator =(const GuiDimension& aDimension);
                      operator long() const;
                      operator SIZE&() const;
                      operator SIZE*() const;
};

Rectangles are also commonly specified in WIN32 using a RECT structure. In AtOne we use GuiRect which can be used interchangeably with RECT but has a host of useful methods making it easier and more convenient to use. The class definition for GuiRect is,

class GuiRect
{
private:
  GuiPoint        TopLeft;
  GuiPoint        BottomRight;

public:
  GuiRect();
  GuiRect(int Left, int Top, int Right, int Bottom);
  GuiRect(const GuiPoint& rPt1, const GuiPoint& rPt2);
  GuiRect(const GuiPoint& rOrigin, const GuiDimension& rSize);
  GuiRect(const RECT& rRect);
  GuiRect(const GuiRect& rRect);

  void            normalise();
  int             left() const;
  void            left(int val);
  int             top() const;
  void            top(int val);
  int             right() const;
  void            right(int val);
  int             bottom() const;
  void            bottom(int val);
  const GuiPoint& topLeft() const;
  void            topLeft(const GuiPoint& rPt);
  GuiPoint        topRight() const;
  GuiPoint        bottomLeft() const;
  const GuiPoint& bottomRight() const;
  void            bottomRight(const GuiPoint& rPt);
  int             width() const;
  int             height() const;
  GuiDimension    size() const;
  bool            contains(const GuiRect& rRect) const;
  bool            contains(const GuiPoint& rPt) const;
  bool            intersects(const GuiRect& rRect) const;
  bool            isNull() const;
  void            null();
  GuiRect         join(const GuiRect& rRect) const;
  GuiRect         intersect(const GuiRect& rRect) const;
  const GuiRect&  operator =(const GuiRect& rRect);
  const GuiRect&  operator +=(const GuiPoint& rPt);
  const GuiRect&  operator -=(const GuiPoint& rPt);
  bool            operator ==(const GuiRect& rRect) const;
                  operator RECT&() const;
                  operator RECT*() const;
};

Less commonly used but also very useful are ranges. The name range here comes from the mathematical definition in which a pair of numbers specify a specific range in which another number can belong to. AtOne has a GuiRange and GuiRealRange class which is used for such range specifications. The GuiRealRange is for real numbers whilst GuiRange is for integers. 

Beyond these basic classes AtOne has a number of very useful point mapping classes that can make graphics programming that much easier. The class GuiPointByVectors is essentially a conversion class that can either determine the scaling of two vector added together required to construct a given point or alternatively, construct a point out of a scaled linear combination of two vectors. This class is typically very useful in building graphs as it allows points to be mapped to and from the graph space. 

The class GuiParallelogram is a mapping class that allows you to map co-ordinates from one parallelogram to another. Unlike a rectangle a parallogram can have corners that are not at right angles. The only constraint is that opposite sides are parallel to one another. By using two instances of this class you can map points and rectangles from a rectangular space to a sheared (that is flatterned) rectangular space with any degree of scaling that you like. This can be particularly useful if, for example, you need to perform a shearing operation on a bitmap. 

The class GuiLineSegment is used to manipulate line segments. In particular, with this class you can check if a line segment is wholly contained within a given rectangle, intersects and given rectangle and clip it to the border of a given rectangle. You can also find out the effective length of the line segment. 

Finally the class GuiCoOrdinateTransform is a generalised 2-dimension co-ordinate transformation supporting translation, rotation, scaling and shearing transformations. It is also structured in a way to concurrently support the forward and inverse co-ordinate transformation of points using the transformation that you have constructed. You should find this class exceptionally useful in any 2-D graphics application that needs to specify graphics transformations in an arbitrary fashion. The class definition for GuiCoOrdinateTransform is,

class GuiCoOrdinateTransform
{
.
.
.
public:
  GuiCoOrdinateTransform();
  GuiCoOrdinateTransform(const GuiCoOrdinateTransform& rCopy);
  ~GuiCoOrdinateTransform();

  void                          reset();
  void                          realizeLineMapping(const GuiPoint& rPt1, const GuiPoint& rPt2, double fValue1, double fValue2);
  const GuiCoOrdinateTransform& shear(const GuiPoint& rShear);
  const GuiCoOrdinateTransform& shear(double fShearX, double fShearY);
  const GuiCoOrdinateTransform& translate(const GuiPoint& rTranslation);
  const GuiCoOrdinateTransform& translate(double cx, double cy);
  const GuiCoOrdinateTransform& scale(double fScaleX, double fScaleY);
  const GuiCoOrdinateTransform& rotate(double fAngle);
  const GuiCoOrdinateTransform& scaleAboutPoint(double x, double y, double fScaleX, double fScaleY);
  const GuiCoOrdinateTransform& rotateAboutPoint(double x, double y, double fAngle);
  const GuiCoOrdinateTransform& preMultiply(const GuiCoOrdinateTransform& rTransform);
  const GuiCoOrdinateTransform& postMultiply(const GuiCoOrdinateTransform& rTransform);
  GuiCoOrdinateTransform        inverse() const;
  const GuiCoOrdinateTransform& invert();
  GuiPoint                      map(int nParameter) const;
  GuiPoint                      map(double fParameter) const;
  GuiPoint                      map(const GuiPoint& rPt) const;
  GuiRect                       map(const GuiRect& rRect) const;
  GuiRealPoint                  map(const GuiRealPoint& rPt) const;
  double                        mapAngle(double fAngle) const;
  double                        mapMagnitudeInX(double fMagnitude) const;
  double                        mapMagnitudeInY(double fMagnitude) const;
  double                        mapMagnitude(double fMagnitude, bool bInX = false) const;
  int                           mapMagnitudeInX(int nMagnitude) const;
  int                           mapMagnitudeInY(int nMagnitude) const;
  int                           mapMagnitude(int nMagnitude, bool bInX = false) const;
  GuiPoint                      inverseMap(int nParameter) const;
  GuiPoint                      inverseMap(double fParameter) const;
  GuiPoint                      inverseMap(const GuiPoint& rPt) const;
  GuiRect                       inverseMap(const GuiRect& rRect) const;
  GuiRealPoint                  inverseMap(const GuiRealPoint& rPt) const;
  double                        inverseMapAngle(double fAngle) const;
  double                        inverseMapMagnitudeInX(double fMagnitude) const;
  double                        inverseMapMagnitudeInY(double fMagnitude) const;
  double                        inverseMapMagnitude(double fMagnitude, bool bInX = false) const;
  int                           inverseMapMagnitudeInX(int nMagnitude) const;
  int                           inverseMapMagnitudeInY(int nMagnitude) const;
  int                           inverseMapMagnitude(int nMagnitude, bool bInX = false) const;
  bool                          isIdentity() const;
  bool                          operator ==(const GuiCoOrdinateTransform& rTransform) const;
  bool                          operator !=(const GuiCoOrdinateTransform& rTransform) const;
};

All of these classes and more are declared and implemented in guigeom.hpp and guigeom.cpp respectively. 
 

Generic Classes


"We use Zeus for Windows and Watcom C/C++ 11.0 as our development environment of choice..."

Paavo Jumppanen
Creator of AtOne Application Framework


This document was last modified on 1st September, 2001
Copyright (C) 2001, Paavo Jumppanen
All rights reserved.