Česky
Kamil Dudka

GED 2006 (C++)

File detail

Name:Downloaddocwnd.cc [Download]
Location: ged2006 > src
Size:16.4 KB
Last modification:2007-08-29 02:16

Source code

/*
 * File: docwnd.cc
 * Project: GED - bitmap editor (ICP)
 * Author: name, login
 * Team: xdudka00, xfilak01, xhefka00, xhradi08
 * Created: YYYY-MM-DD
 */
 
#include <iostream>
#include <sstream>
#include <fstream>
 
#include <FL/Fl.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_File_Chooser.H>
 
#include "global.h"
#include "appwnd.h"
#include "menu.h"
#include "pluginsupport.h"
#include "gedconf.h"
#include "docwnd.h"
 
using std::string;
 
using GlobalH::minDocWndWidth;
using GlobalH::minDocWndHeight;
using GlobalH::defDocWndWidth;
using GlobalH::defDocWndHeight;
using GlobalH::menuBarHeight;
 
using GlobalH::min;
using GlobalH::max;
 
/*
 * CallBacks
 */
static void cb_wndProc (Fl_Widget *pDocWnd, void *) {static_cast<DocWnd *>(pDocWnd)->tryClose (); }
 
/*
 * Create document window
 */
DocWnd::DocWnd (Image *img)
	: Fl_Window (defDocWndWidth-1, defDocWndHeight-1, (img->name()).c_str()), _img (img)
{
	// Select whole image
	_img->unSelect();
 
	// Wnd properties
	this->size_range (minDocWndWidth, minDocWndHeight);
	this->callback (cb_wndProc);
	this->begin();
 
	// Create menu
	_menu = new DocMenu (this, Point(0, 0), BoxSize(defDocWndWidth, menuBarHeight));
	_menu -> menuInit (*_img);
 
	// Create scroll area
	_scroll = new MyFl_Scroll (0, menuBarHeight, defDocWndWidth, defDocWndHeight - 2*menuBarHeight);
	_scroll->begin();
 
	// Create paint area and load image
	_paint = new PaintArea (Point (0, menuBarHeight), _img);
 
	_scroll->end();
 
	// Initial status bar message
	std::ostringstream initStream;
	initStream << "Image size: " << _img->size().width << "x" << _img->size().height;
	_statusMsg = initStream.str();
 
	// Create status bar
	_statusBar = new StatusBar (
			0, defDocWndHeight-menuBarHeight,
			defDocWndWidth, menuBarHeight,
			_statusMsg.c_str());
 
	this->resizable (_scroll);
	this->end ();
}
 
/*
 * Unused destructor
 */
DocWnd::~DocWnd () {
}
 
/*
 * Save image
 * return: true if saved
 */
bool DocWnd::save () {
	if (_img->bNew ())
		return saveAs();
 
	_img->save (_img->name());
	_img->setSaved ();
	setStatusMsg ("Image saved..");
	this->redraw ();
 
	return true;
}
 
/*
 * Save as ...
 * return: true if saved
 */
bool DocWnd::saveAs ()
{
	char *szFileName;
 
	for (;;) {
		// Ask for name
		szFileName = fl_file_chooser ("Save image", _img->filePattern().c_str(), 0);
 
		if (NULL==szFileName) {
			setStatusMsg ("Saving canceled by user..");
			this->redraw ();
			return false;
		}
 
		std::fstream f (szFileName, std::ios::in);
		if (!f)
			break;
		f.close ();
 
		std::string msg ("Overwrite exitsting file ");
		msg.append (szFileName);
		msg.append ("?");
		if (1==fl_choice (msg.c_str(), "No", "Yes", 0))
			break;
	}
 
	// Save with fileName
	std::string fileName (szFileName);
	_img -> save (fileName);
 
	// Update some visible stuff
	_img -> setSaved ();
	this->setStatusMsg ("Image saved..");
	this->label (szFileName);
	this->redraw ();
 
	return true;
}
 
/*
 * Close window - return false if canceled
 */
bool DocWnd::tryClose () {
	if (_img->needSave ()) {
		// File changed
		std::string msg ("Save changes in ");
		msg.append (_img->name () + "?");
 
		// Ask for save
		switch (fl_choice (msg.c_str(), "Cancel", "Yes", "No")) {
			case 0:
				// Cancel
				setStatusMsg ("Document closing canceled by user..");
				this->redraw ();
				return false;
 
			case 1:
				// Save
				try {
					if (!this->save ())
						return false;
				}
				catch (Image::ErrSave ()) {
					return false;
				}
 
			case 2:
			default:
				break;
		}
 
	}
 
	// Destroy document window
	this->hide ();
	this->clear ();
	Fl::delete_widget (this);
 
	return true;
}
 
/*
 * Undo
 */
void DocWnd::undo () {
	_img->undo ();
	setStatusMsg ("Last operation reverted..");
	this->redraw();
}
 
/*
 * Redo
 */
void DocWnd::redo () {
	_img->redo ();
	setStatusMsg ("Last undo reverted..");
	this->redraw();
}
 
/*
 * Select whole image
 */
void DocWnd::unSelect ()
{
	*_img << new SelectCmd (_img);
	this->setStatusMsg ("Selected all..");
	this->redraw ();
}
 
/*
 * Apply filter "grayscale"
 */
void DocWnd::filterGray ()
{
	*_img << new GrayScaleCmd (_img);
	setStatusMsg ("Built-in filter \'Grayscale\' called successfully..");
	redraw ();
}
 
/*
 * Apply filter "invert"
 */
void DocWnd::filterInvert ()
{
	*_img << new InvertCmd (_img);
	setStatusMsg ("Built-in filter \'Invert\' called successfully..");
	redraw ();
}
 
/*
 * Set status bar message text
 */
void DocWnd::setStatusMsg (const char *szMsg)
{
	_statusMsg = szMsg;
	_statusBar -> label (_statusMsg.c_str ());
}
 
/*
 * Redraw image, init menu items and dispaly status message
 */
void DocWnd::redraw ()
{
	_paint->resize (
			- _scroll->xposition(), menuBarHeight - _scroll->yposition(),
			_img->size().width, _img->size().height);
	_paint->redraw ();
	_menu->menuInit (*_img);
	Fl_Window::redraw();
}
 
/*
 * Draw status bar at low level
 */
void DocWnd::drawStatus ()
{
	_statusBar -> draw();
}
 
/*
 * Apply plugin "pluginName" with arguments "szArgs"
 */
void DocWnd::applyPlugin (std::string pluginName, const char *szArgs)
{
	std::string args;
	if (0!= szArgs)
		args = szArgs;
 
	std::ostringstream msgStream;
	try {
		*_img << new PluginCmd (_img, pluginName, args);
		msgStream << "Plugin \'" << pluginName << " (" << args << ")\' called successfully..";
	}
	catch (PluginList::ErrPluginNotFound) {
		msgStream << "Error: plugin \'" << pluginName << "\' not found!";
	}
	catch (PluginList::ErrPluginEC err) {
		_img->rollBack ();
		msgStream << "Error: plugin \'" << pluginName << "\' returned error code " << err.iErrCode;
	}
 
	setStatusMsg (msgStream.str ());
	this->redraw ();
}
 
/*
 * Start macro loading
 */
void DocWnd::macLoad ()
{
	_img -> startMacro ();
	_menu -> menuInit (*_img);
	setStatusMsg ("Loading macro..");
	this->redraw ();
}
 
/*
 * Stop macro loading and create macro
 */
void DocWnd::macCreate (const char *szName)
{
	const std::string macName (szName);
	try {
		GedConf *conf= GedConf::ptr ();
 
		// Delete macro if redefined
		MacroList macList= conf->getMacroList ();
		if (macList.end() != macList.find (macName))
			conf->removeMacro (macName);
 
		// Save sequence as macro
		MacroCmd *mac = _img->createMacroCmd ();
		mac -> name (macName);
		mac -> save ();
		delete mac;
 
		// Save configuration to disk
		conf->saveConfig ();
 
		// Update some visible stuff
		string msg ("Added macro \'");
		msg.append (macName);
		msg.append ("\'");
		setStatusMsg (msg);
		this->redraw ();
	}
	catch (CmdHistory::ErrUnderflow) {
		std::cerr << "GED2006: error: Macro underflow! (possible data lost)\n";
	}
}
 
/*
 * Apply macro "macName"
 */
void DocWnd::applyMacro (std::string macName)
{
	MacroCmd *macro;
	try {
		// Load macro
		macro = new MacroCmd (_img, macName);
	}
	catch (...) {
		setStatusMsg ("Error occur during macro loading!");
		redraw ();
		return;
	}
 
	try {
		// Execute macro
		*_img << macro;
	}
	catch (...) {
		// Roll-back
		macro->unExec ();
		_img->rollBack ();
 
		setStatusMsg ("Error occur during macro execution!");
		redraw ();
		return;
	}
 
	// Update status bar
	setStatusMsg ("Macro \'" + macName + "\'");
	redraw ();
}
 
/*
 * Status bar initialization
 */
StatusBar::StatusBar (int x, int y, int width, int height, const char *szText):
	Fl_Box (FL_UP_BOX, x, y, width, height, szText)
{
	align (FL_ALIGN_INSIDE | FL_ALIGN_LEFT);
}
 
/*
 * Paint area initialization
 */
PaintArea::PaintArea (Point at, Image *img):
	Fl_Box (at.x, at.y, img->size().width, img->size().height),
	_img (img),
	_dc (TOOL_NOTOOL, 0, 0),
	_currentCmd (0)
{
	_flImg = new MyFl_Image (*_img);
	this->image (_flImg);
}
 
/*
 * Paint area destructor
 */
PaintArea::~PaintArea ()
{
	delete _flImg;
	delete _img;
}
 
/*
 * Draw paint area (need for selection)
 */
void PaintArea::draw ()
{
	// Get scroller position
	MyFl_Scroll *scroll = static_cast<MyFl_Scroll *>(parent ());
	const int xPos = scroll->xposition ();
	const int yPos = scroll->yposition ();
 
	// Selected area metrics
	Point at (_img->select().at);
	at.x -= xPos;
	at.y -= yPos;
	at.y += menuBarHeight;
	BoxSize size (_img->select().size);
 
	// Draw selected area border
	Fl_Box::draw ();
	fl_line_style (FL_DOT, 1);
	fl_draw_box (FL_BORDER_FRAME,
			at.x, at.y,
			size.width, size.height,
			FL_BLACK);
	fl_line_style (FL_SOLID, 1);
}
 
/*
 * Redraw paint area
 */
void PaintArea::redraw ()
{
	_flImg->uncache();
	Fl_Box::redraw();
}
 
/*
 * Paint area event handling
 */
int PaintArea::handle (int event)
{
	// Event filter
	switch (event) {
		case FL_PUSH:
		case FL_DRAG:
		case FL_RELEASE:
			if (1==Fl::event_button ())
				break;
 
		default:
			return Fl_Widget::handle (event);
	}
 
	// Get pointers to parents
	DocWnd *doc = static_cast<DocWnd *>(parent()->parent());
	MyFl_Scroll *scroll = static_cast<MyFl_Scroll *>(parent ());
 
	// Get scroller position
	const int xPos = scroll->xposition ();
	const int yPos = scroll->yposition ();
 
	// Get mouse position
	int x = Fl::event_x ();
	int y = Fl::event_y ();
 
	// Event handle
	if (event == FL_PUSH) {
		// Save context
		_dc = AppWnd::ptr()->drawContext();
 
		// Compute brush width
		switch (_dc.tool) {
			case TOOL_BRUSH:
			case TOOL_LINE:
			case TOOL_RECT:
				_iBrushWidth = _dc.fgBrushWidth;
				break;
 
			case TOOL_RUBBER:
				_iBrushWidth = _dc.bgBrushWidth;
				break;
 
			default:
				_iBrushWidth = 1;
		}
 
		// Check fb selection
		if (_dc.tool != TOOL_SELECT) {
			_selX1 = _img->select().at.x;
			_selY1 = _img->select().at.y;
			_selX2 = _selX1 + _img->select().size.width;
			_selY2 = _selY1 + _img->select().size.height;
		} else {
			_selX1 = 0;
			_selY1 = 0;
			_selX2 = _img->size().width;
			_selY2 = _img->size().height;
		}
		_selX1 -= _iBrushWidth-1;
		_selY1 -= _iBrushWidth-1;
		_selX2 --;
		_selY2 --;
 
		// Absolute position
		_absX1 = _absX2 = min (min (max (x, 0),
				doc->w() - _iBrushWidth),
				_img->size().width - xPos - _iBrushWidth);
		_absY1 = _absY2 = min (min (max (y, int (menuBarHeight)),
				doc->h() - _iBrushWidth - menuBarHeight+1),
				_img->size().height - yPos - _iBrushWidth + menuBarHeight);
 
		// Relative position
		_relX1 = _relX2 = _absX1 + xPos;
		_relY1 = _relY2 = _absY1 + yPos - menuBarHeight;
 
		// Check position against selected area
		_bInRange =
			_relX2 >= _selX1 && _relX2 <= _selX2 &&
			_relY2 >= _selY1 && _relY2 <= _relY2;
 
		// Create draw command (if needed)
		drawStart ();
		drawData ();
 
		// Start feedback
		feedBackInit ();
		feedBack ();
		return 1;
 
	} // (FL_PUSH)
 
	if (_dc.tool == TOOL_CIRCLE) {
		// Circle must be in aspect ratio 1:1
 
		const int xMin = min (x, _absX1);
		const int xMax = max (x, _absX1);
		const int yMin = min (y, _absY1);
		const int yMax = max (y, _absY1);
		const int dx = xMax - xMin;
		const int dy = yMax - yMin;
 
		// Aspect ratio correction
		if (dx > dy) {
			if (x == xMin)
				x = xMax - dy;
			else
				x = xMin + dy;
		} else {
			if (y == yMin)
				y = yMax - dx;
			else
				y = yMin + dx;
		}
	}
 
	// Absolute position
	_absX2 = min (min (max (x, 0),
			doc->w() - _iBrushWidth),
			_img->size().width - xPos - _iBrushWidth);
	_absY2 = min (min (max (y, int (menuBarHeight)),
			doc->h() - _iBrushWidth - menuBarHeight+1),
			_img->size().height - yPos - _iBrushWidth + menuBarHeight);
 
	// Relative position
	_relX2 = _absX2 + xPos;
	_relY2 = _absY2 + yPos - menuBarHeight;
 
	// Check position against selected area
	_bInRange =
		_relX2 >= _selX1 && _relX2 <= _selX2 &&
		_relY2 >= _selY1 && _relY2 <= _relY2;
 
	if (event == FL_DRAG) {
		// FL_DRAG
 
		drawData ();
		feedBack ();
		return 1;
	}
 
	// FL_RELEASE
	doc -> setStatusMsg ("");
	drawEnd ();
	doc -> redraw();
	return 1;
 
}
 
/*
 * Called on mouse down event
 */
void PaintArea::drawStart ()
{
	// Delete last command
	delete _currentCmd;
 
	// Create new command (if needed)
	switch (_dc.tool) {
		case TOOL_BRUSH:
			_currentCmd = new BrushCmd (_img, _dc);
			break;
 
		case TOOL_RUBBER:
			_currentCmd = new BrushCmd (_img, _dc, true);
			break;
 
		default:
			_currentCmd = 0;
	}
}
 
/*
 * Called on mouse drag event
 */
void PaintArea::drawData ()
{
	if (0== _currentCmd)
		return;
 
	switch (_dc.tool) {
		case TOOL_BRUSH:
		case TOOL_RUBBER:
			if (_bInRange)
				break;
		default:
			return;
	}
 
	// Add current point to pixmap
	static_cast<BrushCmd *>(_currentCmd) -> add (Point (_relX2, _relY2));
}
 
/*
 * Called on mouse release event
 */
void PaintArea::drawEnd ()
{
	// Left-up corner (xMin,yMin) and right-bott corner (xMax, yMax)
	const int xMin = min (_relX1, _relX2);
	const int xMax = max (_relX1, _relX2);
	const int yMin = min (_relY1, _relY2);
	const int yMax = max (_relY1, _relY2);
 
	// Drawn object placement
	Point at;
	BoxSize size;
	if (_dc.tool == TOOL_LINE) {
		at.x = _relX1;
		at.y = _relY1;
		size.width = _relX2 - _relX1;
		size.height = _relY2 - _relY1;
	} else {
		at.x = xMin;
		at.y = yMin;
		size.width = xMax - xMin;
		size.height = yMax - yMin;
	}
	const Rect placement (at, size);
 
	switch (_dc.tool) {
		case TOOL_BRUSH:
		case TOOL_RUBBER:
			if (0== _currentCmd)
				return;
 
			*_img << _currentCmd;
			_currentCmd = 0;
			break;
 
		case TOOL_LINE:
			*_img << new LineCmd (_img, placement, _dc);
			break;
 
		case TOOL_RECT:
			*_img << new RectCmd (_img, placement, _dc);
			break;
 
		case TOOL_CIRCLE:
			*_img << new CircleCmd (_img, placement, _dc);
			break;
 
		case TOOL_ELLIPSE:
			*_img << new EllipseCmd (_img, placement, _dc);
			break;
 
		case TOOL_TEXT:
		{
			const char *szText= fl_input ("Draw text:");
			if (0== szText || '\0'== *szText)
				return;
 
			try {
				*_img << new TextCmd (_img, _dc, Point (_relX2, _relY2), szText);
			}
			catch (TextCmd::ErrFreeType) {
				_img -> rollBack ();
				DocWnd *const doc = static_cast<DocWnd *>(parent()->parent());
				doc->setStatusMsg ("Error occur during text draw!");
			}
			break;
		}
 
		case TOOL_SELECT:
			*_img << new SelectCmd (_img, placement);
			break;
 
		default:
			return;
	}
}
 
/*
 * Prepare document window for feedback during draw
 */
void PaintArea::feedBackInit ()
{
	// Save current context
	Fl_Window *last = Fl_Window::current();
	DocWnd *doc = static_cast<DocWnd *>(parent()->parent());
 
	// Redraw paint area, hide scrolling
	doc -> make_current ();
	fl_push_clip (
			0, menuBarHeight,
			min (doc->w(), _img->size().width),
			min (doc->h()-2*menuBarHeight+1, _img->size().height));
	this->draw ();
	fl_pop_clip ();
 
	// Restore context
	if (
			last== AppWnd::ptr() ||
			AppWnd::ptr()->isDocument (last))
		last -> make_current();
}
 
/*
 * Feedback during draw
 */
void PaintArea::feedBack ()
{
	// Save context
	Fl_Window *last = Fl_Window::current();
	DocWnd *doc = static_cast<DocWnd *>(parent()->parent());
 
	// Left-up corner (xMin,yMin) and right-bott corner (xMax, yMax)
	const int xMin = min (_absX1, _absX2);
	const int xMax = max (_absX1, _absX2);
	const int yMin = min (_absY1, _absY2);
	const int yMax = max (_absY1, _absY2);
	const int dx = xMax - xMin;
	const int dy = yMax - yMin;
 
	// Draw feedback
	std::ostringstream msg;
	doc->make_current ();
 
	switch (_dc.tool) {
		case TOOL_NOTOOL:
			// no feedback
			msg << "Scrollbars hidden";
			break;
 
		case TOOL_TEXT:
			msg << "Text placement";
			break;
 
		case TOOL_BRUSH:
			msg << "Brush";
			fl_color (_dc.fgColor.red, _dc.fgColor.green, _dc.fgColor.blue);
			fl_line_style (FL_SOLID, _iBrushWidth);
			fl_line (
					_absX2 + _iBrushWidth/2, _absY2,
					_absX2 + _iBrushWidth/2, _absY2 + _iBrushWidth);
			break;
 
		case TOOL_RUBBER:
			msg << "Rubber";
			fl_color (_dc.bgColor.red, _dc.bgColor.green, _dc.bgColor.blue);
			fl_line_style (FL_SOLID, _iBrushWidth);
			fl_line (
					_absX2 + _iBrushWidth/2, _absY2,
					_absX2 + _iBrushWidth/2, _absY2 + _iBrushWidth);
			break;
 
		case TOOL_LINE:
			msg << "Drawing line";
			feedBackInit ();
			fl_color (_dc.fgColor.red, _dc.fgColor.green, _dc.fgColor.blue);
			fl_line_style (FL_SOLID, _dc.fgBrushWidth);
			fl_line (
					_absX1 + _iBrushWidth/2, _absY1 + _iBrushWidth/2,
					_absX2 + _iBrushWidth/2, _absY2 + _iBrushWidth/2);
			break;
 
		case TOOL_RECT:
			msg << "Drawing rectangle";
			feedBackInit ();
			fl_line_style (FL_SOLID, _dc.fgBrushWidth);
			fl_draw_box (FL_BORDER_FRAME, xMin + _iBrushWidth/2, yMin + _iBrushWidth/2, dx, dy,
					fl_rgb_color (_dc.fgColor.red, _dc.fgColor.green, _dc.fgColor.blue));
			break;
 
		case TOOL_CIRCLE:
		case TOOL_ELLIPSE:
		case TOOL_SELECT:
			msg << "Select area";
			feedBackInit ();
			fl_line_style (FL_DOT,1);
			fl_draw_box (FL_BORDER_FRAME, xMin, yMin, dx, dy,
					fl_rgb_color (_dc.fgColor.red, _dc.fgColor.green, _dc.fgColor.blue));
			break;
	}
	fl_color (FL_BLACK);
	fl_line_style (FL_SOLID,1);
 
	msg << ", effective mouse position: (" << (_relX2) << ", " << (_relY2) << ")";
	doc->setStatusMsg (msg.str ());
	doc->drawStatus ();
 
	// Restore context
	if (
			last== AppWnd::ptr() ||
			AppWnd::ptr()->isDocument (last))
		last -> make_current();
}