Source Code (Use browser search to find items of interest.)

Class Index

ktuberling'TopLevel (./kdegames/ktuberling/toplevel.h:23)

class TopLevel : public KTMainWindow
{
  Q_OBJECT
  
public:

  TopLevel();
  ~TopLevel();

protected:

  void readOptions();
  void writeOptions();
  void setupMenuBar();
  void setupToolBar();
  bool loadBitmaps();
  void setupGeometry();

  virtual void paintEvent(QPaintEvent *);
  virtual void closeEvent(QCloseEvent *);
  virtual void mousePressEvent(QMouseEvent *);
  virtual void mouseReleaseEvent(QMouseEvent *);

private:

  bool zone(QPoint &);
  bool loadFrom(const char *);
  bool saveAs(const char *);
  bool printPicture(QPrinter &) const;
  void drawText(QPainter &, QRect &, int) const;
  void playSound(int) const;
  void repaintAll();
  void enableUndo(bool) const;
  void enableRedo(bool) const;

private slots:

  void fileNew();
  void fileOpen();
  void fileSave();
  void filePicture();
  void filePrint();
  void editCopy();
  void editUndo();
  void editRedo();
  void optionsSound();
//  void aboutApp();

private:

  KMenuBar *menubar;		// Task window's menubar
  KToolBar *toolbar;		// Task window's tool bar
  QPopupMenu *fileMenu,		// Various menus
	     *editMenu,
	     *optionsMenu;
  int				// Menu items identificators
      newID, openID, saveID, pictureID, printID, quitID,
      copyID, undoID, redoID,
      soundID;
  int				// Tool bar buttons identificators
      ID_NEW, ID_OPEN, ID_SAVE, ID_PRINT,
      ID_UNDO, ID_REDO,
      ID_HELP;

  bool soundEnabled;		// true if the sound is enabled by user, even if there is no audio server

  QBitmap gameboard, masks;	// Bitmaps of the game board and the objects' shapes
  QRect editableArea;		// Part of the gameboard where the player can lay down objects
  int editableSound;		// Sound associated with this area
  int texts,			// Number of categories of objects names
      decorations;		// Number of draggable objects on the right side of the gameboard
  QRect *textsLayout,		// Positions of the categories names
	*objectsLayout,		// Position of the draggable objects on right side of the gameboard
	*shapesLayout;		// Position of the shapes of these objects in the masks file
  int *textsList,		// List of the message numbers associated with categories
      *soundsList;		// List of sounds associated with each object

  QCursor *draggedCursor;	// Cursor's shape for currently dragged object
  ToDraw draggedObject;		// Object currently dragged
  int draggedZOrder;		// Z-order (in 'toDraw buffer) of this object

  QList<ToDraw> toDraw;		// List of objects in z-order
  QList<Action> history;	// List of actions in chronological order
  unsigned int currentAction;	// Number of current action (not the last one if used "undo" button!)

};

ktuberling'TopLevel::TopLevel() (./kdegames/ktuberling/toplevel.cpp:39)

TopLevel::TopLevel()
  : KTMainWindow()
{
  setBackgroundColor(white);
  readOptions();
  objectsLayout = shapesLayout = 0;
  textsList = soundsList = 0;

  toDraw.setAutoDelete(true);
  history.setAutoDelete(true);

  if (!loadBitmaps())
  {
    QMessageBox::critical
	(this,
         i18n("Tuberling"),
	 i18n("Fatal error :\n" 
              "I could not load the pictures. I'll quit"),
	 i18n("OK")); 
    exit(-1);
  }

  draggedCursor = 0;
  currentAction = 0;

  setupGeometry();
  setupMenuBar();
  setupToolBar();
}

// Destructor

ktuberling'TopLevel::~TopLevel() (./kdegames/ktuberling/toplevel.cpp:70)

TopLevel::~TopLevel()
{
  if (soundsList) delete soundsList;
  if (textsList) delete textsList;
  if (objectsLayout) delete objectsLayout;
  if (shapesLayout) delete shapesLayout;
  if (draggedCursor) delete draggedCursor;
}

// Read options from preferences file

ktuberling'TopLevel::readOptions() (./kdegames/ktuberling/toplevel.cpp:80)

void TopLevel::readOptions()
{
  KConfig *config;
  QString option;

  config = KApplication::kApplication()->config();

  config->setGroup("General");
  option = config->readEntry("Sound", "on");
  soundEnabled = option.find("on") == 0;
}

// Write options to preferences file

ktuberling'TopLevel::writeOptions() (./kdegames/ktuberling/toplevel.cpp:93)

void TopLevel::writeOptions()
{
  KConfig *config;

  config = KApplication::kApplication()->config();

  config->setGroup("General");
  config->writeEntry("Sound", soundEnabled? "on": "off");
  config->sync();
}

// Menubar initialization

ktuberling'TopLevel::setupMenuBar() (./kdegames/ktuberling/toplevel.cpp:105)

void TopLevel::setupMenuBar()
{
  menubar = menuBar();
  menubar->setGeometry(0, 0, width(), 24);

  fileMenu = new QPopupMenu();
  editMenu = new QPopupMenu();
  optionsMenu = new QPopupMenu();

  newID = fileMenu->insertItem(i18n("&New"));
  fileMenu->connectItem(newID, this, SLOT(fileNew()));
  fileMenu->setAccel(KStdAccel::key(KStdAccel::New), newID);

  openID = fileMenu->insertItem(i18n("&Open..."));
  fileMenu->connectItem(openID, this, SLOT(fileOpen()));
  fileMenu->setAccel(KStdAccel::key(KStdAccel::Open), openID);

  saveID = fileMenu->insertItem(i18n("&Save..."));
  fileMenu->connectItem(saveID, this, SLOT(fileSave()));
  fileMenu->setAccel(KStdAccel::key(KStdAccel::Save), saveID);

  pictureID = fileMenu->insertItem(i18n("Save &as picture..."));
  fileMenu->connectItem(pictureID, this, SLOT(filePicture()));
  fileMenu->insertSeparator();

  printID = fileMenu->insertItem(i18n("&Print"));
  fileMenu->connectItem(printID, this, SLOT(filePrint()));
  fileMenu->setAccel(KStdAccel::key(KStdAccel::Print), printID);
  fileMenu->insertSeparator();

  quitID = fileMenu->insertItem(i18n("&Quit"));
  fileMenu->connectItem(quitID, kapp, SLOT(quit()));
  fileMenu->setAccel(KStdAccel::key(KStdAccel::Quit), quitID);

  copyID = editMenu->insertItem(i18n("&Copy"));
  editMenu->connectItem(copyID, this, SLOT(editCopy()));
  editMenu->setAccel(KStdAccel::key(KStdAccel::Copy), copyID);
  editMenu->insertSeparator();

  undoID = editMenu->insertItem(i18n("&Undo"));
  editMenu->setItemEnabled(undoID, false);
  editMenu->connectItem(undoID, this, SLOT(editUndo()));
  editMenu->setAccel(KStdAccel::key(KStdAccel::Undo), undoID);


  redoID = editMenu->insertItem(i18n("&Redo"));
  editMenu->setItemEnabled(redoID, false);
  editMenu->connectItem(redoID, this, SLOT(editRedo()));
  editMenu->setAccel(KStdAccel::key(KStdAccel::Redo), redoID);

  soundID = optionsMenu->insertItem(i18n("&Sound"));
  optionsMenu->setItemChecked(soundID, soundEnabled);
  optionsMenu->connectItem(soundID, this, SLOT(optionsSound()));

  QString about = i18n("A program by Eric Bischoff (ebisch@cybercable.tm.fr)\n"
	               "and John Calhoun.\n\n"
	               "This program is dedicated to my daughter Sunniva.");

  menubar->insertItem(i18n("&File"), fileMenu);
  menubar->insertItem(i18n("&Edit"), editMenu);
  menubar->insertItem(i18n("&Options"), optionsMenu);
  menubar->insertSeparator(-1);
  menubar->insertItem(i18n("&Help"), helpMenu(about));

}

// Toolbar initialization

ktuberling'TopLevel::setupToolBar() (./kdegames/ktuberling/toplevel.cpp:172)

void TopLevel::setupToolBar()
{
  KIconLoader iconLoader;

  toolbar = toolBar();
  toolbar->setGeometry(0, 24, width(), 24);

  toolbar->insertButton(iconLoader.loadIcon("filenew",KIcon::Toolbar), ID_NEW, SIGNAL(pressed()), this, SLOT(fileNew()), true, i18n("New"));
  toolbar->insertButton(iconLoader.loadIcon("fileopen",KIcon::Toolbar), ID_OPEN, SIGNAL(pressed()), this, SLOT(fileOpen()), true, i18n("Open"));
  toolbar->insertButton(iconLoader.loadIcon("filefloppy",KIcon::Toolbar), ID_SAVE, SIGNAL(pressed()), this, SLOT(fileSave()), true, i18n("Save"));
  toolbar->insertButton(iconLoader.loadIcon("fileprint",KIcon::Toolbar), ID_PRINT, SIGNAL(pressed()), this, SLOT(filePrint()), true, i18n("Print"));
  toolbar->insertSeparator();

  toolbar->insertButton(iconLoader.loadIcon("undo",KIcon::Toolbar), ID_UNDO, SIGNAL(pressed()), this, SLOT(editUndo()), false, i18n("Undo"));
  toolbar->insertButton(iconLoader.loadIcon("redo",KIcon::Toolbar), ID_REDO, SIGNAL(pressed()), this, SLOT(editRedo()), false, i18n("Redo"));
}

// Load background and draggable objects masks

ktuberling'TopLevel::loadBitmaps() (./kdegames/ktuberling/toplevel.cpp:190)

bool TopLevel::loadBitmaps()
{
  FILE *layoutFile;
  char comment[512];
  int editableLeft, editableTop, editableRight, editableBottom;
  int textLeft, textTop, textRight, textBottom;
  int objectLeft, objectTop, objectRight, objectBottom;
  int shapeLeft, shapeTop, shapeRight, shapeBottom;
  QRect *text, *object, *shape;
  int *label, *sound;

  if (!gameboard.load((const char *) locate("data", "ktuberling/pics/gameboard.xpm")))
	return false;

  if (!masks.load((const char *) locate("data", "ktuberling/pics/masks.xpm")))
	return false;

  if (!(layoutFile = fopen((const char *) locate("data", "ktuberling/pics/layout.txt"), "r")))
	return false;

  if (fscanf(layoutFile, "%d %d %d %d %d",
	     &editableLeft, &editableTop, &editableRight, &editableBottom,
             &editableSound) != 5) { fclose(layoutFile); return false; }
  if (!fgets(comment, sizeof(comment), layoutFile))
	{ fclose(layoutFile); return false; }
  editableArea.setCoords
	(XMARGIN + XORIGIN + editableLeft,
         YMARGIN + YORIGIN + editableTop,
         XMARGIN + XORIGIN + editableRight,
         YMARGIN + YORIGIN + editableBottom);

  if (fscanf(layoutFile, "%d", &texts) != 1)
	{ fclose(layoutFile); return false; }
  if (!fgets(comment, sizeof(comment), layoutFile))
	{ fclose(layoutFile); return false; }
  if (texts < 1)
	{ fclose(layoutFile); return false; }

  if (!(textsLayout = new QRect[texts])) { fclose(layoutFile); return false; }
  if (!(textsList = new int[texts])) { fclose(layoutFile); return false; }
  for (text = textsLayout, label = textsList;
       text < textsLayout + texts;
       text++, label++)
  {
    if (fscanf(layoutFile,
               "%d %d %d %d %d",
               &textLeft, &textTop, &textRight, &textBottom,
	       label) != 5) { fclose(layoutFile); return false; }
    if (!fgets(comment, sizeof(comment), layoutFile))
		 { fclose(layoutFile); return false; }
    text->setCoords(textLeft, textTop, textRight, textBottom);
  }
 
  if (fscanf(layoutFile, "%d", &decorations) != 1)
	{ fclose(layoutFile); return false; }
  if (!fgets(comment, sizeof(comment), layoutFile))
	{ fclose(layoutFile); return false; }
  if (decorations < 1)
	{ fclose(layoutFile); return false; }

  if (!(objectsLayout = new QRect[decorations])) { fclose(layoutFile); return false; }
  if (!(shapesLayout = new QRect[decorations])) { fclose(layoutFile); return false; }
  if (!(soundsList = new int[decorations])) { fclose(layoutFile); return false; }
  for (object = objectsLayout, shape = shapesLayout, sound = soundsList;
       object < objectsLayout + decorations;
       object++, shape++, sound++)
  {
    if (fscanf(layoutFile,
               "%d %d %d %d  %d %d %d %d  %d",
               &objectLeft, &objectTop, &objectRight, &objectBottom,
               &shapeLeft, &shapeTop, &shapeRight, &shapeBottom,
	       sound) != 9)
		{ fclose(layoutFile); return false; }
    if (!fgets(comment, sizeof(comment), layoutFile))
		{ fclose(layoutFile); return false; }
    object->setCoords(objectLeft, objectTop, objectRight, objectBottom);
    shape->setCoords(shapeLeft, shapeTop, shapeRight, shapeBottom);
  }
  if (fclose(layoutFile)) return false;

  return true;
}

// Set up top level window's geometry

ktuberling'TopLevel::setupGeometry() (./kdegames/ktuberling/toplevel.cpp:274)

void TopLevel::setupGeometry()
{
  int width = gameboard.width() + XORIGIN + 2 * XMARGIN,
      height = gameboard.height() + YORIGIN + 2 * YMARGIN;
  setMinimumWidth(width);
  setMaximumWidth(width);
  setMinimumHeight(height);
  setMaximumHeight(height);
}

// Reset gameboard

ktuberling'TopLevel::fileNew() (./kdegames/ktuberling/toplevel.cpp:285)

void TopLevel::fileNew()
{
  toDraw.clear();
  history.clear();
  currentAction = 0;

  enableUndo(false);
  enableRedo(false);
  repaintAll();
}

// Load gameboard

ktuberling'TopLevel::fileOpen() (./kdegames/ktuberling/toplevel.cpp:297)

void TopLevel::fileOpen()
{
  QString name;
  char dir[512], *p;

  sprintf(dir, "%.512s", (const char *) locate("data", "ktuberling/museum/miss.tuberling"));
  for (p = dir + strlen(dir) - 1; p >= dir; p--)
    if (*p == '/') { *p = '\0'; break; }

  KURL url = KFileDialog::getOpenURL(dir, "*.tuberling");
  
  toolbar->getButton(ID_OPEN)->setDown(false);

  if (url.isEmpty())
    return;
  
  KIO::NetAccess::download( url, name );
  
  toDraw.clear();
  history.clear();
  currentAction = 0;

  if (!loadFrom(name))
    QMessageBox::warning(this,i18n("Tuberling"),i18n("Could not load file"),i18n("OK"));

  enableUndo(currentAction != 0);
  enableRedo(false);
  repaintAll();

  KIO::NetAccess::removeTempFile( name );
}

// Save gameboard

ktuberling'TopLevel::fileSave() (./kdegames/ktuberling/toplevel.cpp:330)

void TopLevel::fileSave()
{
  QString name;
  char dir[512], *p;

  sprintf(dir, "%.512s", (const char *) locate("data", "ktuberling/museum/miss.tuberling"));
  for (p = dir + strlen(dir) - 1; p >= dir; p--)
    if (*p == '/') { *p = '\0'; break; }

  KURL url = KFileDialog::getSaveURL(dir, "*.tuberling");
  
  toolbar->getButton(ID_SAVE)->setDown(false);
  
  if (url.isEmpty())
    return;
  
 
  if( !url.isLocalFile() )
  {
    KMessageBox::sorry( 0L, i18n( "Only saving to local files currently supported." ) );
    return;
  }
 
  if( !saveAs( url.path() ) )
    QMessageBox::warning(this,i18n("Tuberling"),i18n("Could not save file"),i18n("OK"));
}

// Save gameboard as picture

ktuberling'TopLevel::filePicture() (./kdegames/ktuberling/toplevel.cpp:358)

void TopLevel::filePicture()
{
  QPixmap picture(QPixmap::grabWindow
	(winId(),
         editableArea.left(),
         editableArea.top(),
         editableArea.width(),
         editableArea.height()));

  KURL url = KFileDialog::getSaveURL
		(getenv("HOME"),
		 i18n(	"*.xpm|Unix pixmaps (*.xpm)\n"
			"*.jpg|JPEG compressed files (*.jpg)\n"
			"*.png|Next generation pictures (*.png)\n"
			"*.bmp|Windows bitmaps (*.bmp)\n"
		 	"*|All picture formats"));
  if( url.isEmpty() )
    return;
  
  if( !url.isLocalFile() )
  {
    KMessageBox::sorry( 0L, i18n( "Only saving to local files supported yet." ) );
    return;
  }
  
  QString name = url.path();
  const char *format;
  int suffix;
  QString end;

  suffix = name.findRev('.');
  if (suffix == -1)
  {
    name += ".xpm";
    end = "xpm";
  }
  else end = name.mid(suffix + 1, name.length());

  if (end == "xpm") format = "XPM";
  else if (end == "jpg") format = "JPEG";
  else if (end == "png") format = "PNG";
  else if (end == "bmp") format = "BMP";
  else
  {
    QMessageBox::warning
      (this,
       i18n("Tuberling"),
       i18n("Unknown picture format"),
       i18n("OK"));
    return;
  }

  if (!picture.save(name, format))
    QMessageBox::warning
      (this,
       i18n("Tuberling"),
       i18n("Could not save file"),
       i18n("OK"));
}

// Save gameboard as picture

ktuberling'TopLevel::filePrint() (./kdegames/ktuberling/toplevel.cpp:419)

void TopLevel::filePrint()
{
  QPrinter printer;
  bool ok;

  ok = QPrintDialog::getPrinterSetup(&printer);
  toolbar->getButton(ID_PRINT)->setDown(false);
  if (!ok) return;
  repaint(true);
  if (!printPicture(printer))
    QMessageBox::warning
        (this,
         i18n("Tuberling"),
         i18n("Could not print picture"),
	 i18n("OK"));
  else
    QMessageBox::information
        (this,
         i18n("Tuberling"),
         i18n("Picture successfully printed"),
	 i18n("OK"));
}

// Copy modified area to clipboard

ktuberling'TopLevel::editCopy() (./kdegames/ktuberling/toplevel.cpp:443)

void TopLevel::editCopy()
{
  QClipboard *clipboard = QApplication::clipboard();
  QPixmap picture(QPixmap::grabWindow
	(winId(),
         editableArea.left(),
         editableArea.top(),
         editableArea.width(),
         editableArea.height()));

  clipboard->setPixmap(picture);
}

// Undo last action

ktuberling'TopLevel::editUndo() (./kdegames/ktuberling/toplevel.cpp:457)

void TopLevel::editUndo()
{
  ToDraw *newObject;
  Action *undone;
  int zOrder;

  if (!currentAction) return;
  toolbar->getButton(ID_UNDO)->setDown(false);
  if (!(undone = history.at(--currentAction)))
    return;

  zOrder = undone->ZOrderAfter();
  if (zOrder != -1)
  {
    // Undo an "add" or a "move" action
    if (!toDraw.remove(zOrder)) return;
  }

  zOrder = undone->ZOrderBefore();
  if (zOrder != -1)
  {
    // Undo a "delete" or a "move" action
    if (!(newObject = new ToDraw(undone->DrawnBefore())))
      return;
    if (!toDraw.insert(zOrder, newObject))
      return;
  }

  if (!currentAction) enableUndo(false);
  enableRedo(true);
  repaintAll();
}

// Redo last action

ktuberling'TopLevel::editRedo() (./kdegames/ktuberling/toplevel.cpp:491)

void TopLevel::editRedo()
{
  ToDraw *newObject;
  Action *undone;
  int zOrder;

  if (currentAction >= history.count()) return;
  toolbar->getButton(ID_REDO)->setDown(false);
  if (!(undone = history.at(currentAction++)))
    return;

  zOrder = undone->ZOrderBefore();
  if (zOrder != -1)
  {
    // Redo a "delete" or a "move" action
    if (!toDraw.remove(zOrder)) return;
  }

  zOrder = undone->ZOrderAfter();
  if (zOrder != -1)
  {
    // Redo an "add" or a "move" action
    if (!(newObject = new ToDraw(undone->DrawnAfter())))
      return;
    if (!toDraw.insert(zOrder, newObject))
      return;
  }

  if (currentAction >= history.count()) enableRedo(false);
  enableUndo(true);
  repaintAll();
}

// Toggle sound on/off

ktuberling'TopLevel::optionsSound() (./kdegames/ktuberling/toplevel.cpp:525)

void TopLevel::optionsSound()
{
  soundEnabled = !soundEnabled;
  optionsMenu->setItemChecked( soundID, soundEnabled);
  writeOptions();
}

// Repaint event handling

ktuberling'TopLevel::paintEvent() (./kdegames/ktuberling/toplevel.cpp:533)

void TopLevel::paintEvent(QPaintEvent *event)
{
  QPoint destination(event->rect().topLeft()),
         position(destination.x() - XMARGIN - XORIGIN,
                  destination.y() - YMARGIN - YORIGIN);
  QRect area(position, QSize(event->rect().size()));
  QBitmap cache(gameboard.size());
  QPainter artist(&cache);
  const ToDraw *currentObject;
  int text;

  if (destination.x() < XMARGIN + XORIGIN) destination.setX(XMARGIN + XORIGIN);
  if (destination.y() < YMARGIN + YORIGIN) destination.setY(YMARGIN + YORIGIN);
  area = QRect(0, 0, gameboard.width(), gameboard.height()).intersect(area);
  if (area.isEmpty()) return;

  artist.drawPixmap(area.topLeft(), gameboard, area);
  artist.setPen(color0);
  for (text = 0; text < texts; text++)
    drawText(artist, textsLayout[text], textsList[text]);

  artist.setPen(color1);

  for (currentObject = toDraw.first(); currentObject; currentObject = toDraw.next())
    currentObject->draw(artist, area, objectsLayout, shapesLayout, &gameboard, &masks);
  bitBlt(this, destination, &cache, area, Qt::CopyROP);
}

// Top level window close box interaction handling

ktuberling'TopLevel::closeEvent() (./kdegames/ktuberling/toplevel.cpp:562)

void TopLevel::closeEvent(QCloseEvent *)
{
  kapp->quit();
}

// Mouse button down event

ktuberling'TopLevel::mousePressEvent() (./kdegames/ktuberling/toplevel.cpp:568)

void TopLevel::mousePressEvent(QMouseEvent *event)
{
  if (draggedCursor) return;

  QPoint position(event->x() - XMARGIN - XORIGIN,
                  event->y() - YMARGIN - YORIGIN);
  if (!zone(position)) return;

  int draggedNumber = draggedObject.getNumber();
  QBitmap object(objectsLayout[draggedNumber].size()),
          shape(shapesLayout[draggedNumber].size());
  bitBlt(&object, QPoint(0, 0), &gameboard, objectsLayout[draggedNumber], Qt::CopyROP);
  bitBlt(&shape, QPoint(0, 0), &masks, shapesLayout[draggedNumber], Qt::CopyROP);

  if (!(draggedCursor = new QCursor
       (object, shape, position.x(), position.y()))) return;
  setCursor(*draggedCursor);

  playSound(soundsList[draggedNumber]);
}

// Mouse button up event

ktuberling'TopLevel::mouseReleaseEvent() (./kdegames/ktuberling/toplevel.cpp:590)

void TopLevel::mouseReleaseEvent(QMouseEvent *event)
{
  // If we are not dragging an object, ignore the event
  if (!draggedCursor) return;

  QCursor arrow;
  int draggedNumber = draggedObject.getNumber();
  QRect position(
    event->x() - XMARGIN - XORIGIN - draggedCursor->hotSpot().x(),
    event->y() - YMARGIN - YORIGIN - draggedCursor->hotSpot().y(),
    objectsLayout[draggedNumber].width(),
    objectsLayout[draggedNumber].height()); 
  QRect dirtyArea
	(editableArea.left() - 10,
         editableArea.top() - 10,
         editableArea.width() + 20,
         editableArea.height() + 20);
  ToDraw *newObject;
  Action *newAction;

  // We are not anymore dragging an object
  delete draggedCursor;
  draggedCursor = 0;
  setCursor(arrow);

  // If we are not moving the object to the editable area
  if (!dirtyArea.contains(event->pos()))
  {
    // ... then register its deletion (if coming from the editable area), and return
    if (draggedZOrder == -1) return;

    while (history.count() > currentAction) history.removeLast();
    if (!(newAction = new Action(&draggedObject, draggedZOrder, 0, -1))) return;
    history.append(newAction);
    currentAction++;
    enableUndo(true);

    return; 
  }

  // Register that we have one more object to draw
  if (!(newObject = new ToDraw(draggedNumber, position))) return;
  toDraw.append(newObject);

  // Forget all subsequent actions in the undo buffer, and register object's addition (or its move)
  while (history.count() > currentAction) history.removeLast();
  if (!(newAction = new Action(&draggedObject, draggedZOrder, newObject, toDraw.count()-1))) return;
  history.append(newAction);
  currentAction++;
  enableUndo(true);

  // Repaint the editable area
  position.moveBy(XMARGIN + XORIGIN, YMARGIN + YORIGIN);
  repaint(position, false);

}

// In which decorative object are we?
// On return, the position is the location of the cursor's hot spot
// Returns false if we aren't in any zone

ktuberling'TopLevel::zone() (./kdegames/ktuberling/toplevel.cpp:650)

bool TopLevel::zone(QPoint &position)
{
  // Scan all available decorative objects on right side because we may be adding one
  int draggedNumber;
  for (draggedNumber = 0;
       draggedNumber < decorations;
       draggedNumber++) if (objectsLayout[draggedNumber].contains(position))
  {
     position.setX(position.x() - objectsLayout[draggedNumber].x());
     position.setY(position.y() - objectsLayout[draggedNumber].y());

     draggedObject.setNumber(draggedNumber);
     draggedZOrder = -1;

     return true;
  }

  // Scan all decorative objects already layed down on editable are because we may be moving or removing one
  const ToDraw *currentObject;

  for (draggedZOrder = toDraw.count()-1; draggedZOrder >= 0; draggedZOrder--)
  {
    currentObject = toDraw.at(draggedZOrder);
    if (!currentObject->getPosition().contains(position)) continue;

    QRect toUpdate(currentObject->getPosition());
    draggedObject = *currentObject;
    draggedNumber = draggedObject.getNumber();

    QBitmap shape(shapesLayout[draggedNumber].size());
    QPoint relative(position.x() - toUpdate.x(),
                    position.y() - toUpdate.y());
    bitBlt(&shape, QPoint(0, 0), &masks, shapesLayout[draggedNumber], Qt::CopyROP);
    if (!shape.convertToImage().pixelIndex(relative.x(), relative.y())) continue;

    toDraw.remove(draggedZOrder); 
    toUpdate.moveBy(XMARGIN + XORIGIN, YMARGIN + YORIGIN);
    repaint(toUpdate, false);

    position = relative;

    return true;
  }

  // If we are on the gameboard itself, then play "tuberling" sound 
  if (editableArea.contains(position))
	playSound(editableSound);

  return false;
}

// Load objects and lay them down on the editable area

ktuberling'TopLevel::loadFrom() (./kdegames/ktuberling/toplevel.cpp:702)

bool TopLevel::loadFrom(const char *name)
{
  FILE *fp;
  bool eof = false;
  ToDraw readObject, *newObject;
  Action *newAction;

  if (!(fp = fopen(name, "r"))) return false;
  for (;;)
  {
    if (!readObject.load(fp, decorations, eof))
    {
      fclose(fp);
      return false;
    }
    if (eof)
    {
      return !fclose(fp);
    }
    if (!(newObject = new ToDraw(readObject))) return false;
    toDraw.append(newObject);
    if (!(newAction = new Action(0, -1, newObject, toDraw.count()-1))) return false;
    history.append(newAction);
    currentAction++;
    
  } 
}

// Save objects laid down on the editable area

ktuberling'TopLevel::saveAs() (./kdegames/ktuberling/toplevel.cpp:731)

bool TopLevel::saveAs(const char *name)
{
  FILE *fp;
  const ToDraw *currentObject;

  if (!(fp = fopen(name, "w"))) return false;
  for (currentObject = toDraw.first(); currentObject; currentObject = toDraw.next())
    if (!currentObject->save(fp))
    {
      fclose(fp);
      return false;
    }
  return !fclose(fp);
}

// Print gameboard's picture

ktuberling'TopLevel::printPicture() (./kdegames/ktuberling/toplevel.cpp:747)

bool TopLevel::printPicture(QPrinter &printer) const
{
  QPainter artist;
  QPixmap picture(QPixmap::grabWindow
	(winId(),
         editableArea.left(),
         editableArea.top(),
         editableArea.width(),
         editableArea.height()));

  if (!artist.begin(&printer)) return false;
  artist.drawPixmap(QPoint(32, 32), picture);
  if (!artist.end()) return false;
  return true;
}

// Draw some text

ktuberling'TopLevel::drawText() (./kdegames/ktuberling/toplevel.cpp:764)

void TopLevel::drawText(QPainter &artist, QRect &area, int textNumber) const
{
  QString label;

  switch (textNumber)
  {
      case TEXT_EYES:
	label = i18n("Eyes");
	break;
      case TEXT_EYEBROWS:
	label = i18n("Eyesbrows");
	break;
      case TEXT_NOSES:
	label = i18n("Noses");
	break;
      case TEXT_EARS:
	label = i18n("Ears");
	break;
      case TEXT_MOUTHS:
	label = i18n("Mouths");
	break;
      case TEXT_GOODIES:
	label = i18n("Goodies");
	break;
      default:
	return;
  }
  artist.drawText(area, AlignCenter, label);
}

// Play some sound

ktuberling'TopLevel::playSound() (./kdegames/ktuberling/toplevel.cpp:795)

void TopLevel::playSound(int soundNumber) const
{
  QString soundName;

  if (!soundEnabled) return;
  switch (soundNumber)
  {
    case SOUND_TUBERLING:
	soundName = i18n("en/tuberling.wav");
        break;
    case SOUND_EYE:
	soundName = i18n("en/eye.wav");
        break;
    case SOUND_EYEBROW:
	soundName = i18n("en/eyebrow.wav");
        break;
    case SOUND_NOSE:
	soundName = i18n("en/nose.wav");
        break;
    case SOUND_EAR:
	soundName = i18n("en/ear.wav");
        break;
    case SOUND_MOUTH:
	soundName = i18n("en/mouth.wav");
        break;
    case SOUND_HAT:
	soundName = i18n("en/hat.wav");
        break;
    case SOUND_MOUSTACHE:
	soundName = i18n("en/moustache.wav");
        break;
    case SOUND_CIGAR:
	soundName = i18n("en/cigar.wav");
        break;
    case SOUND_TIE:
	soundName = i18n("en/tie.wav");
        break;
    case SOUND_SUNGLASSES:
	soundName = i18n("en/sunglasses.wav");
        break;
    case SOUND_SPECTACLES:
	soundName = i18n("en/spectacles.wav");
        break;
    case SOUND_BOW:
	soundName = i18n("en/bow.wav");
        break;
    case SOUND_EARRING:
	soundName = i18n("en/earring.wav");
        break;
    case SOUND_BADGE:
	soundName = i18n("en/badge.wav");
        break;
    case SOUND_WATCH:
	soundName = i18n("en/watch.wav");
        break;
    default:
	return;
  }

  KAudioPlayer::play(locate("data", "ktuberling/sounds/" + soundName));
}

// Repaint all the editable area

ktuberling'TopLevel::repaintAll() (./kdegames/ktuberling/toplevel.cpp:858)

void TopLevel::repaintAll()
{
  QRect dirtyArea
	(editableArea.left() - 10,
         editableArea.top() - 10,
         editableArea.width() + 20,
         editableArea.height() + 20);

  repaint(dirtyArea, false);
}

// Enable or disable "undo" button and menu item

ktuberling'TopLevel::enableUndo() (./kdegames/ktuberling/toplevel.cpp:870)

void TopLevel::enableUndo(bool enable) const
{
  editMenu->setItemEnabled(undoID, enable);
  toolbar->setItemEnabled(ID_UNDO, enable);
}

// Enable or disable "redo" button and menu item

ktuberling'TopLevel::enableRedo() (./kdegames/ktuberling/toplevel.cpp:877)

void TopLevel::enableRedo(bool enable) const
{
  editMenu->setItemEnabled(redoID, enable);
  toolbar->setItemEnabled(ID_REDO, enable);
}