TweetFollow Us on Twitter

Micro-draw
Volume Number:6
Issue Number:7
Column Tag:Jörg's Folder

C++ micro-Draw

By Jörg Langowski, MacTutor Editorial Board

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

C++ micro-Draw

We’re going to expand on the Object Lists demo from V6#2 this month. You’ll probably have noted quite a few deficiencies to that example - it would be nice to be able to draw not only three objects at fixed places within the window, but create some kind of ‘micro-Draw’ utility where you could place as many ovals and rectangles where you like in the window, and also change their aspect.

This example, over the months, will (hopefully) evolve into a useful Draw utility. The first step towards that is to add some code which will allow us to move objects around in the window, change their drawing pattern, delete them etc. At the end of this example you will see that there are still many things left to do - we will add them in the course of the following columns. You should have copies of the preceding example code ready at hand, because we don’t want to reprint everything from scratch each time. The listing at the end gives only the changes that were made compared to the code in V6#2.

Cleaning up the lists

The first thing that we have to take care of is the removal of objects and lists when a document is closed. This was not done in the original example. When a window is closed, the document destructor is called:

//1

TListDoc::~TListDoc(void)
{delete fObjList;
 HideWindow(fDocWindow);  }

and thus, the associated object list will be deleted. However, the objects in it still float around in the heap, since they have been created with new and have not been deleted explicitly! Therefore, we have to define a destructor for the object list as well, in which each object in the list is deleted:

//2

TObjList::~TObjList(void)
{
 TObjLink* temp;
 while (fHeader != nil) 
 {
 temp = fHeader->GetNext();
 delete fHeader->GetmyObj();
 delete fHeader;
 fHeader = temp;
 }
 fNumObjs = 0;
}

This code will clean up properly after us when we close a window.

Creating and Deleting Objects

The next feature that we’d like to add is to be able to position objects by clicking in the window. We can select from the menu what type of object we want; the cursor will then change to a cross, and the object will be placed in the window at the point where we clicked.

To do this, we first define a new routine, ClickInWindow() (see listing); this function waits for a mouse down event and returns the location of the mouse click in the current port’s local coordinates. This routine is called by the menu selection handlers (DoMenuCommand) at the appropriate selections. The center of the object’s bounds rectangle is then set to the position of the mouse click, and the window redrawn. We have not added any means to change the size of an object yet; take this as an exercise, or wait until we give an example in this column.

To erase an object from the window, we simply click on it after the menu selection “Erase”. For implementing this behavior, we define the method FindObj(Point theLoc) which finds the object corresponding to a mouse click at a certain point in the window. This routine returns a pointer to a TDisplObj; to remove the object which we clicked at, we simply write

// 3

fCurListDoc->fObjList->
 RemoveObj(fCurListDoc->fObjList->FindObj(theLoc));

A good example how compact some operations can be expressed in C++.

Changing draw patterns of objects is easy, too; I have added three menu selections that allow to draw objects in either black, gray or white. Again, we find the object that was clicked at, and then set the draw pattern to a new value. For this, we need to define a new instance variable fObjPat in TDisplObj which holds the drawing pattern, and the new method SetObjPat(Pattern pat) which sets this pattern. Caution: a pattern can’t be simply assigned to another pattern variable as in

//4

Pattern myPat = yourPat;

because Pattern is an array, it is defined as unsigned char[8]. We have to write a little loop to do the job (see listing).

Moving objects around

The last method that we add to our example this time is one that allows us to drag an object around in the window. We define a method DragObj(Point *theLoc, Rect theRect) which is called right after the mouse has been clicked at theLoc and an object has been found there. We also need to define a method that allows us to set the bounds rectangle of the object, so it can actually be moved.

DragObj will first create a new region that corresponds to the outline of the object to be dragged. It will then call the Window Manager routine DragGrayRgn to move this outline around the window. The limit and slop rectangles for the dragging routine are set to the port rectangle of the document window, passed through the parameter theRect to DragObj. This means that we cannot exceed the window’s boundaries when we drag the object.

DragGrayRgn waits until the mouse button is released and then returns a longint that contains the distances moved in the vertical and horizontal directions in its high and low words. This information is used to change the value of theLoc. When DragObj returns, theLoc will contain the new mouse coordinates. The main routine then resets the center of the object’s bounds rectangle to this position. The window is redrawn, and the object will have moved.

There is still a small bug in the dragging code; when you drag outside the limit rectangle, the object will disappear. I encourage you to find the reason for this; the bug will be resolved in the next column.

Wish list

We’re on our way to create a ‘MacTutorDraw’ application; but we still have a long way to go, of course. Things that should be added in the future are:

• scrap handling, so that copy, cut and paste are implemented;

• replacement of the selection menu by a palette window;

• changing the size and order of objects;

• implementing more objects, such as lines, arcs, polygons and text boxes;

• code for saving/restoring/printing documents.

Don’t expect all this to happen in the next column, though...

More on Forth

At the end, I would like to tell the remaining Forth enthusiasts that there is very good news regarding object-oriented extensions to Forth. I have now seen three different ways to implement NEON-compatible systems, two of which use fast subroutine-threaded code. I’ll give you news very soon. Until then, happy hacking.

Listing: Changes to the V6#2 example code

void TDocListApp::AdjustMenus(void)
{
 WindowPtrfrontmost;
 MenuHandle menu;
 Boolean undo,cutCopyClear,paste;

 TListDoc* fCurListDoc = (TListDoc*) fCurDoc;

 frontmost = FrontWindow();

 menu = GetMHandle(mFile);
 if ( fDocList->NumDocs() < kMaxOpenDocuments )
   EnableItem(menu, iNew);// New is enabled when we can open more documents 

 else DisableItem(menu, iNew);
 if ( frontmost != (WindowPtr) nil )
   EnableItem(menu, iClose);
 // Close is enabled when there is a window to close 
 else DisableItem(menu, iClose);

 undo = false; cutCopyClear = false; paste = false;
 
 if ( fCurListDoc == nil )
   {  // all editing is enabled for DA windows 
 undo = true;  cutCopyClear = true;
 paste = true;  }
   
 menu = GetMHandle(mEdit);
 if ( undo )EnableItem(menu, iUndo);
 else   DisableItem(menu, iUndo);
 
 if ( cutCopyClear )
   {  EnableItem(menu, iCut);
 EnableItem(menu, iCopy);
 EnableItem(menu, iClear);} 
 else
   {  DisableItem(menu, iCut);
 DisableItem(menu, iCopy);
 DisableItem(menu, iClear); }
   
 if ( paste )  EnableItem(menu, iPaste);
 else   DisableItem(menu, iPaste);
 
 menu = GetMHandle(myMenu);
 EnableItem(menu, item1);
 EnableItem(menu, item2);
 EnableItem(menu, item3);
 EnableItem(menu, item5);
 EnableItem(menu, item7);
 EnableItem(menu, item8);
 EnableItem(menu, item9);
 EnableItem(menu, item11);
} // AdjustMenus

Point ClickInWindow()
{
 EventRecordtheEvent;
 Point  theLoc;
 Booleanev;
 
 CursHandle hCurs;
 hCurs = GetCursor(crossCursor);
 SetCursor(*hCurs);
 while (!(Button())) {} ;
 ev = WaitNextEvent(mDownMask,&theEvent,0,0);
 theLoc = theEvent.where;
 GlobalToLocal(&theLoc);
 return theLoc;
}

void TDocListApp::DoMenuCommand(short menuID, short menuItem)
{
 short  itemHit;
 Str255 daName;
 short  daRefNum;
 WindowPtrwindow;
 Rect   brect;
 Point  theLoc;
 TDisplObj*theObj;
 
 TListDoc* fCurListDoc = (TListDoc*) fCurDoc;

 window = FrontWindow();
 switch ( menuID )
   {  case mApple:
 switch ( menuItem )
   {  case iAbout: // About box
 itemHit = Alert(rAboutAlert, nil);
 break;
 default: // DAs etc.
 GetItem(GetMHandle(mApple), menuItem, daName);
 daRefNum = OpenDeskAcc(daName);
 break; }
 break;
 case mFile:
 switch ( menuItem )
   {  case iNew: DoNew(); break;
 case iClose:
 if (fCurListDoc != nil)
   {  fDocList->RemoveDoc
 (fCurListDoc);
 delete fCurListDoc; }
 else CloseDeskAcc(((WindowPeek)
 fWhichWindow)->windowKind);
 break;
 case iQuit:Terminate();  break; }
 break;
 case mEdit:
 // call SystemEdit for DA editing & MultiFinder 
 if ( !SystemEdit(menuItem-1) )
   {  switch ( menuItem )
   {  case iCut: break;
 case iCopy: break;
 case iPaste: break;
 case iClear: break; }
   }
 break;
 case myMenu:
 if (fCurListDoc != nil) 
 { switch ( menuItem )
   { case item1: // TRoundRect
 theLoc=ClickInWindow();
 SetRect(&brect,theLoc.h-40,
 theLoc.v-20, theLoc.h+40, theLoc.v+20);
 fCurListDoc->fObjList->AddObj(
 new TRoundRect(brect));
 break;
 case item2: // TOval
 theLoc=ClickInWindow();
 SetRect(&brect,theLoc.h-40,
 theLoc.v-20, theLoc.h+40, theLoc.v+20);
 fCurListDoc->fObjList->AddObj(
 new TOval(brect));
 break;
 case item3:  // TRect
 theLoc=ClickInWindow();
 SetRect(&brect,theLoc.h-40,
 theLoc.v-20, theLoc.h+40,
 theLoc.v+20);
 fCurListDoc->fObjList->AddObj(
 new TRect(brect));
 break;
 case item5: // Erase
 theLoc=ClickInWindow();
 fCurListDoc->fObjList->RemoveObj(
 fCurListDoc->fObjList->FindObj(theLoc));
 break;
 case item7: // White
 theLoc=ClickInWindow();
 theObj = fCurListDoc->fObjList->
 FindObj(theLoc);
 if (theObj != nil) 
 theObj->SetObjPat(qd.white);
 break;
 case item8: // Gray
 theLoc=ClickInWindow();
 theObj = fCurListDoc->fObjList->
 FindObj(theLoc);
 if (theObj != nil) 
 theObj->SetObjPat(qd.gray);
 break;
 case item9: // Black
 theLoc=ClickInWindow();
 theObj = fCurListDoc->fObjList->
 FindObj(theLoc);
 if (theObj != nil) 
 theObj->SetObjPat(qd.black);
 break;
 case item11: // Move
 theLoc=ClickInWindow();
 theObj = fCurListDoc->fObjList->
 FindObj(theLoc);
 if (theObj != nil)
 { theObj->DragObj(
 &theLoc,window->portRect);
 SetRect(&brect,theLoc.h-40, 
 theLoc.v-20, theLoc.h+40, 
 theLoc.v+20);
 theObj->SetBoundRect(brect);
 }
 break;
 }
 InvalRect(&(window->portRect));
 fCurListDoc->DoUpdate();
 InitCursor();
 }
 break;
   }
 HiliteMenu(0);
} // DoMenuCommand

// List document class
class TListDoc : public TDocument {
 
  protected:
 void DrawWindow(void);

  public:
 TObjList*fObjList;// list of objects to be drawn
 TListDoc(short resID);
 ~TListDoc(void);
 
 // methods from TDocument we override
 void DoUpdate(void);
};

const int kMinDocDim = 40;

// create and delete the document window

TListDoc::TListDoc(short resID) : (resID)
{
 fObjList = new TObjList();
 ShowWindow(fDocWindow);  
}

void TListDoc::DrawWindow(void)
{TObjLink* temp;
 
 SetPort(fDocWindow);
 EraseRect(&fDocWindow->portRect);
 if (fObjList->NumObjs() != 0)
 for (temp = fObjList->Header(); 
 temp != nil; temp = temp->GetNext())
   temp->GetmyObj()->Draw(
 temp->GetmyObj()->GetObjPat());
} // DrawWindow

class TDisplObj {
  private:
 Boolean HiLiteState;// true : highlighted
  protected:
 TDisplObj(Rect r);
 Rect fBoundRect;
 Pattern fObjPat;
  public:
 virtual void Draw(Pattern) 
 {DebugStr(“\pCall to TDisplObj::Draw”);}
 virtual void Erase()     
 {DebugStr(“\pCall to TDisplObj::Erase”);}
 virtual void DoContent()   
 {DebugStr(“\pCall to TDisplObj::DoContent”);}
 virtual void DoIdle() { /* do nothing by default*/ }
 Rect GetBoundRect() {return fBoundRect;}
 void SetBoundRect(Rect theRect) 
 {fBoundRect = theRect;}
 Pattern GetObjPat() {return fObjPat;}
 void SetObjPat(Pattern pat); 
 void DragObj(Point*, Rect); 
};

class TObjList {
 TObjLink*fHeader;
 int    fNumObjs;
public:
 TObjList(void);  ~TObjList(void);
 
 inline TObjLink* Header() { return fHeader; }
 inline int NumObjs() { return fNumObjs; }

 void   AddObj(TDisplObj* obj);
 void   RemoveObj(TDisplObj* obj);
 TDisplObj* FindObj(Point theLoc);
};

TObjList::~TObjList(void)
{
 TObjLink* temp;
 while (fHeader != nil) 
 {
 temp = fHeader->GetNext();
 delete fHeader->GetmyObj();
 delete fHeader;
 fHeader = temp;
 }
 fNumObjs = 0;
}

TDisplObj* TObjList::FindObj(Point theLoc)
{
 TObjLink* temp;
 Rect theRect;

 for (temp = fHeader; temp != nil; 
 temp = temp->GetNext())
 { theRect = temp->GetmyObj()->GetBoundRect();
 if (PtInRect(theLoc,&theRect))
   return (temp->GetmyObj());
 }
 return nil;
}

TDisplObj::TDisplObj(Rect r)  
 { fBoundRect = r; SetObjPat(qd.gray); }

void TDisplObj::SetObjPat(Pattern pat)  
 { for (int i=0; i<8; i++) fObjPat[i] = pat[i]; }

void TDisplObj::DragObj(Point *theLoc, Rect theRect)  
{
 RgnHandle theRgn = NewRgn();
 OpenRgn();
 this->Draw(fObjPat);
 CloseRgn(theRgn);
 
 long res=DragGrayRgn(theRgn,*theLoc, &theRect,&theRect,0,0);
 if (res != 0x8000)
 { theLoc->v += HiWord(res);
 theLoc->h += LoWord(res);
 }
 DisposeRgn(theRgn);
}

 
AAPL
$570.56
Apple Inc.
+13.59
MSFT
$29.11
Microsoft Corpora
-0.65
GOOG
$609.46
Google Inc.
+8.66
MacTech Search:
Community Search:

Fruit Ninja Gets New Update With Powerup...
Fruit Ninja is about to get its biggest update yet to celebrate its second anniversary on Thursday, May 24th. The key new element in the game appears to be that players will now be able to earn an in-game currency, called starfruit, that can be used... | Read more »
Fotor – CameraBag Review
Fotor – CameraBag Review By Jennifer Allen on May 23rd, 2012 Our Rating: :: PLENTIFULiPhone App - Designed for the iPhone, compatible with the iPad A photography app that wants to be able to do everything that could ever be asked... | Read more »
playGO AP1 is the Next Generation of Aud...
With all of Apple’s relatively recent success in the smartphone and tablet market, we can forget sometimes that what kicked off their modern dominance was a device that simply played music. BICOM, Inc. has been recognizing how important music is to... | Read more »
Monkey Pong Review
Monkey Pong Review By Angela LaFollette on May 23rd, 2012 Our Rating: :: BALL BUSTING ACTIONiPhone App - Designed for the iPhone, compatible with the iPad Help the hungry monkey reach all the fruit by bouncing a ball in this family... | Read more »
Heroes & Generals Enters Closed Beta
Creators of Hitman, Roto-Moto, has launched a closed beta of their game, Heroes & Generals. The game is a massively multiplayer first-person shooter involving online fighting between the Axis and Allied forces in Europe. | Read more »
FeedFriendly Review
FeedFriendly Review By Angela LaFollette on May 23rd, 2012 Our Rating: :: EASY TO USEUniversal App - Designed for iPhone and iPad Combine the top three social network newsfeed updates into one location with the help of FeedFriendly... | Read more »
Favorite 4: Euro 2012 Apps
In a matter of weeks, one of the biggest soccer tournaments out there begins: Euro 2012. Qualification is over and 16 European teams are all lined up to prove which one is the best of the bunch. As a Brit, I’m ever hopeful that England will achieve... | Read more »

Price Scanner via MacPrices.net

Are You Sure You Really Want A Retina Display MacB...
Apple didn’t invent the laptop computer, but over the past 21 years they’ve continuously set and reset the bar for laptop innovation and engineering advances, with PC competitors mostly playing catch... Read more
Two PC Pundits Weigh In On PC To Mac Switching (Or...
ZNet’s Stephen Chapman and Forbes’ Brian Caulfield have posted recent blogs on the topic of their personally switching from Windows PCs to Macs. From PC to Mac 10-Months Later ZNet blogger Stephen... Read more
Apple Maintains Top Mobile PC Share in Q112 on Str...
Apple shipped nearly 17.2 million mobile PCs in Q112, accounting for 118% year-over-year shipment growth, according to preliminary results from the latest NPD DisplaySearch Quarterly Mobile PC... Read more
Apple offering refurbished 17″ MacBook Pros for $3...
 The Apple Store has Apple Certified Refurbished 17″ 2.4GHz MacBook Pros available for $2119 including free shipping. That’s $380 off the price of new models. Apple’s one-year warranty is standard. Read more
Week’s Best MacBook Deals
We’ve posted the Week’s Best Deals on MacBook Airs and MacBook Pros for Wednesday, the 23rd of May. Find the lowest price or the best set of bundles from Apple’s Authorized Resellers with these deals... Read more
MacBook Airs on sale for up to $101 off MSRP, free...
 Adorama has MacBook Airs on sale today for up to $101 off MSRP including free shipping. NY and NJ sales tax only. Their prices are among the lowest available for these models from any Apple... Read more
Open-box special: 2.3GHz Mac mini for $493
MacMall has open-box return 2.3GHz Mac minis available for $493 including free shipping. That’s $106 off MSRP. Apple’s one-year warranty and all materials are included. Act now if you’re interested,... Read more
Apple iPhone Charger’s Secrets And Engineering Sup...
Blogger Ken Shirriff’s has posted a thoroughgoing Apple iPhone charger teardown and analysis, the one-line takeaway being: “quality in a tiny expensive package.” Shirriff says that disassembling... Read more

Jobs Board

*Apple* Solutions Consultant-Retail Sal...
Requisition Number 15545402 Job title Apple Solutions Consultant-Retail Sales Location Mobile Country United States City Mobile State Alabama Job type Job description Read more
iPhone Developer at Mastech (Los Angeles...
We are currently seeking an Android/ iPhone Developer for our client in the Insurance domain. We value our professionals, providing comprehensive benefits, exciting challenges, and the opportunity... Read more
24 funny 2d Charaters for iPhone game. a...
We are developing an iPhone game and desire to have 24 characters drawn to our specification. Attached is the detailed spec. Desired Skills: Cartoon, Illustration Read more
*Apple* Solutions Consultant-Retail Sal...
Requisition Number 15545261 Job title Apple Solutions Consultant-Retail Sales Location Spanish Fort Country United States City Spanish Fort State Alabama Job type Job Read more
Android and Iphone Application at Elance...
I need an interval timer application to be created for iphone and android platforms... I am on a tight budget but this ... & IPHONES) not just one so if you can only do one don't waste your time... Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.