Replace modules/editors.cpp with widgets::Workspace class

Now we have one editor for each Document we want to edit. Before we were
creating one editor and switching the document on it, but now we have
several Editors (one for each opened Document). This simplifies the
Document implementation, because it doesn't need to know temporal
Editor information (like "preferred editor settings").

- Removed all code from modules/editors.cpp (legacy from C code).
- Removed PreferredEditorSettings class and
  Document::getPreferredEditorSettings().
- Added Workspace/WorkspaceView classes.
- Added DocumentView (a DocumentObserver).
- Added MiniEditorWindow class.
- Removed SelectFileCommand & CloseEditorCommand.
- Added TabView interface instead of using a raw void* in
  Tabs/TabsDelegate classes.
- Modified editors_draw_sprite() calls to Document::notifySpritePixelsModified
  notifications.
- The "current_editor" global variable wasn't removed because it's
  used in several places yet, but it will be removed in the near future.
  (Also this variable now can be NULL when we don't have an opened
  document.)
This commit is contained in:
David Capello 2013-01-20 18:40:37 -03:00
parent 8cc854aef6
commit ed90055bf7
76 changed files with 1240 additions and 1418 deletions

View File

@ -71,7 +71,6 @@
<key command="MakeUniqueEditor" shortcut="Ctrl+1" />
<key command="SplitEditorVertically" shortcut="Ctrl+2" />
<key command="SplitEditorHorizontally" shortcut="Ctrl+3" />
<key command="CloseEditor" shortcut="Ctrl+4" />
<key command="Preview" shortcut="F8" />
<key command="ShowGrid" shortcut="Shift+G" />
<key command="SnapToGrid" shortcut="Shift+S" />
@ -355,14 +354,9 @@
<item command="SaveMask" text="&amp;Save to MSK file" />
</menu>
<menu text="&amp;View">
<menu text="Sprite &amp;Editor">
<item command="MakeUniqueEditor" text="Make &amp;Unique" />
<separator />
<item command="SplitEditorVertically" text="Split &amp;Vertically" />
<item command="SplitEditorHorizontally" text="Split &amp;Horizontally" />
<separator />
<item command="CloseEditor" text="&amp;Close" />
</menu>
<item command="MakeUniqueEditor" text="Make &amp;Unique" />
<item command="SplitEditorVertically" text="Split &amp;Vertically" />
<item command="SplitEditorHorizontally" text="Split &amp;Horizontally" />
<separator />
<item command="ShowGrid" text="Show &amp;Grid" />
<item command="SnapToGrid" text="&amp;Snap to Grid" />

View File

@ -61,6 +61,7 @@
<color id="filelist_selected_row_text" value="#ffffff" />
<color id="filelist_selected_row_face" value="#2c4c91" />
<color id="filelist_disabled_row_text" value="#ffc8c8" />
<color id="workspace" value="#7d929e" />
</colors>
<cursors>

View File

@ -9,7 +9,7 @@
horizontal="true" expansive="true"
by="pixel" position="52">
<box noborders="true" vertical="true" id="colorbar" />
<box noborders="true" horizontal="true" id="editor" expansive="true" />
<box noborders="true" horizontal="true" id="workspace" expansive="true" />
</splitter>
<box noborders="true" vertical="true" id="toolbar" />
</box>

View File

@ -255,7 +255,6 @@ add_library(aseprite-library
commands/cmd_save_file.cpp
commands/cmd_save_mask.cpp
commands/cmd_scroll.cpp
commands/cmd_select_file.cpp
commands/cmd_sprite_editor.cpp
commands/cmd_sprite_properties.cpp
commands/cmd_sprite_size.cpp
@ -377,6 +376,7 @@ add_library(aseprite-library
widgets/color_button.cpp
widgets/color_selector.cpp
widgets/color_sliders.cpp
widgets/document_view.cpp
widgets/drop_down_button.cpp
widgets/editor/cursor.cpp
widgets/editor/drawing_state.cpp
@ -399,11 +399,13 @@ add_library(aseprite-library
widgets/main_menu_bar.cpp
widgets/main_window.cpp
widgets/menuitem2.cpp
widgets/mini_editor.cpp
widgets/palette_view.cpp
widgets/popup_window_pin.cpp
widgets/status_bar.cpp
widgets/tabs.cpp
widgets/toolbar.cpp)
widgets/toolbar.cpp
widgets/workspace.cpp)
######################################################################
# ASEPRITE application

View File

@ -40,7 +40,6 @@
#include "ini_file.h"
#include "log.h"
#include "modules.h"
#include "modules/editors.h"
#include "modules/gfx.h"
#include "modules/gui.h"
#include "modules/palettes.h"
@ -158,7 +157,10 @@ int App::run()
// Create the main window and show it.
m_mainWindow.reset(new MainWindow);
m_mainWindow->createFirstEditor();
// Create the list of tabs
app_rebuild_documents_tabs();
m_mainWindow->openWindow();
// Redraw the whole screen.
@ -190,9 +192,6 @@ int App::run()
context->setActiveDocument(document);
if (isGui()) {
// Show it
set_document_in_more_reliable_editor(context->getFirstDocument());
// Recent file
getRecentFiles()->addRecentFile(it->c_str());
}
@ -217,6 +216,11 @@ int App::run()
// Uninstall support to drop files
uninstall_drop_files();
// Destroy all documents in the UIContext.
const Documents& docs = m_modules->m_ui_context.getDocuments();
while (!docs.empty())
m_modules->m_ui_context.removeDocument(docs.back());
// Destroy the window.
m_mainWindow.reset(NULL);
}
@ -295,32 +299,9 @@ void app_refresh_screen(const Document* document)
ui::Manager::getDefault()->invalidate();
}
/**
* Regenerates the label for each tab in the @em tabsbar.
*/
void app_rebuild_documents_tabs()
{
UIContext* context = UIContext::instance();
const Documents& docs = context->getDocuments();
// Insert all other sprites
for (Documents::const_iterator
it = docs.begin(), end = docs.end(); it != end; ++it) {
const Document* document = *it;
app_update_document_tab(document);
}
}
void app_update_document_tab(const Document* document)
{
std::string str = get_filename(document->getFilename());
// Add an asterisk if the document is modified.
if (document->isModified())
str += "*";
App::instance()->getMainWindow()->getTabsBar()
->setTabText(str.c_str(), const_cast<Document*>(document));
App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
}
PixelFormat app_get_current_pixel_format()

View File

@ -86,7 +86,6 @@ private:
void app_refresh_screen(const Document* document);
void app_rebuild_documents_tabs();
void app_update_document_tab(const Document* document);
PixelFormat app_get_current_pixel_format();

View File

@ -213,9 +213,8 @@ void CanvasSizeCommand::onExecute(Context* context)
undoTransaction.commit();
documentWriter->generateMaskBoundaries();
update_screen_for_document(documentWriter);
}
update_screen_for_document(document);
}
//////////////////////////////////////////////////////////////////////

View File

@ -146,7 +146,7 @@ void CelPropertiesCommand::onExecute(Context* context)
// Change cel opacity.
cel_writer->setOpacity(new_opacity);
update_screen_for_document(document);
update_screen_for_document(document_writer);
}
}
}

View File

@ -27,11 +27,15 @@
#include "raster/sprite.h"
#include "ui/gui.h"
#include "ui_context.h"
#include "widgets/document_view.h"
#include "widgets/main_window.h"
#include "widgets/status_bar.h"
#include "widgets/workspace.h"
#include <memory>
using namespace ui;
using namespace widgets;
static bool close_active_document(Context* context);
@ -61,7 +65,23 @@ protected:
void onExecute(Context* context)
{
close_active_document(context);
Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
if (workspace->getActiveView() == NULL)
return;
if (DocumentView* docView =
dynamic_cast<DocumentView*>(workspace->getActiveView())) {
Document* document = docView->getDocument();
if (static_cast<UIContext*>(context)->countViewsOf(document) == 1) {
// If we have only one view for this document, close the file.
close_active_document(context);
return;
}
}
// Close the active view.
workspace->closeView(workspace->getActiveView());
}
private:
@ -92,14 +112,14 @@ protected:
void onExecute(Context* context)
{
if (!context->getActiveDocument())
set_document_in_more_reliable_editor(context->getFirstDocument());
while (true) {
if (context->getActiveDocument() != NULL) {
if (!close_active_document(context))
break;
}
else if (context->getFirstDocument() != NULL) {
context->setActiveDocument(context->getFirstDocument());
}
else
break;
}
@ -115,6 +135,7 @@ protected:
*/
static bool close_active_document(Context* context)
{
Document* nextDocument = NULL;
bool save_it;
bool try_again = true;
@ -124,6 +145,7 @@ static bool close_active_document(Context* context)
{
// The sprite is locked as reader temporaly
ActiveDocumentReader document(context);
nextDocument = context->getNextDocument(document);
// see if the sprite has changes
while (document->isModified()) {
@ -167,6 +189,10 @@ static bool close_active_document(Context* context)
base::get_file_name(document->getFilename()).c_str());
document.deleteDocument();
}
// Select next document in the context
context->setActiveDocument(nextDocument);
return true;
}

View File

@ -498,13 +498,11 @@ void ConfigureTools::onSnapToGridClick()
void ConfigureTools::onViewGridClick()
{
m_docSettings->setGridVisible(m_viewGrid->isSelected());
refresh_all_editors();
}
void ConfigureTools::onPixelGridClick()
{
m_docSettings->setPixelGridVisible(m_pixelGrid->isSelected());
refresh_all_editors();
}
void ConfigureTools::onSetGridClick()
@ -517,9 +515,6 @@ void ConfigureTools::onSetGridClick()
const Mask* mask(document->getMask());
m_docSettings->setGridBounds(mask->getBounds());
if (m_docSettings->getGridVisible())
refresh_all_editors();
}
else {
Command* grid_settings_cmd =
@ -536,7 +531,6 @@ void ConfigureTools::onSetGridClick()
void ConfigureTools::onOnionSkinClick()
{
m_docSettings->setUseOnionskin(m_onionSkin->isSelected());
refresh_all_editors();
}
//////////////////////////////////////////////////////////////////////

View File

@ -97,7 +97,6 @@ void DuplicateSpriteCommand::onExecute(Context* context)
docCopy->setFilename(dst_name->getText());
context->addDocument(docCopy);
set_document_in_more_reliable_editor(docCopy);
}
}

View File

@ -18,15 +18,15 @@
#include "config.h"
#include "commands/command.h"
#include "app.h"
#include "commands/command.h"
#include "document_wrappers.h"
#include "modules/gui.h"
#include "modules/editors.h"
#include "raster/layer.h"
#include "raster/sprite.h"
#include "widgets/editor/editor.h"
#include "widgets/status_bar.h"
#include "document_wrappers.h"
//////////////////////////////////////////////////////////////////////
// goto_previous_layer

View File

@ -63,8 +63,6 @@ protected:
IDocumentSettings* docSettings = context->getSettings()->getDocumentSettings(context->getActiveDocument());
docSettings->setGridVisible(docSettings->getGridVisible() ? false: true);
refresh_all_editors(); // TODO this should be done by "setGridVisible" impl
}
};
@ -159,9 +157,6 @@ void GridSettingsCommand::onExecute(Context* context)
bounds.h = MAX(bounds.h, 1);
docSettings->setGridBounds(bounds);
if (docSettings->getGridVisible())
refresh_all_editors(); // TODO this should be done by "setGridBounds" impl
}
}

View File

@ -40,6 +40,7 @@
#include "widgets/editor/editor_decorator.h"
#include "widgets/editor/select_box_state.h"
#include "widgets/editor/standby_state.h"
#include "widgets/workspace.h"
#include <allegro/color.h>
@ -129,7 +130,7 @@ protected:
MenuItem* item = new MenuItem("Use Current Sprite");
item->Click.connect(&ImportSpriteSheetWindow::onUseCurrentSprite, this);
if (m_editor || current_editor->getDocument() == NULL)
if (m_editor || !current_editor || current_editor->getDocument() == NULL)
item->setEnabled(false);
menu->addChild(item);

View File

@ -97,7 +97,7 @@ void LayerPropertiesCommand::onExecute(Context* context)
const_cast<Layer*>(layer)->setName(entry_name->getText());
update_screen_for_document(document);
update_screen_for_document(documentWriter);
}
}

View File

@ -94,9 +94,8 @@ void LoadMaskCommand::onExecute(Context* context)
undo.commit();
documentWriter->generateMaskBoundaries();
update_screen_for_document(documentWriter);
}
update_screen_for_document(document);
}
//////////////////////////////////////////////////////////////////////

View File

@ -38,6 +38,7 @@
#include "ui_context.h"
#include "util/clipboard.h"
#include "widgets/color_bar.h"
#include "widgets/workspace.h"
#include <allegro/config.h>
#include <allegro/unicode.h>
@ -187,11 +188,7 @@ void NewFileCommand::onExecute(Context* context)
}
// Show the sprite to the user
context->addDocument(document);
// Release the document as it is already owned by the context.
// And put the document in a reliable editor.
set_document_in_more_reliable_editor(document.release());
context->addDocument(document.release());
}
}
}

View File

@ -148,8 +148,6 @@ void OpenFileCommand::onExecute(Context* context)
App::instance()->getRecentFiles()->addRecentFile(fop->filename.c_str());
context->addDocument(document);
set_document_in_more_reliable_editor(document);
}
else if (!fop_is_stop(fop))
unrecent = true;

View File

@ -166,9 +166,6 @@ void OptionsCommand::onExecute(Context* context)
// Save configuration
flush_config_file();
// Refresh all editors
refresh_all_editors();
}
}

View File

@ -413,8 +413,9 @@ bool PaletteEntryEditor::onProcessMessage(Message* msg)
// Redraw all editors
try {
const ActiveDocumentReader document(UIContext::instance());
update_editors_with_document(document);
ActiveDocumentWriter document(UIContext::instance());
if (document != NULL)
document->notifyGeneralUpdate();
}
catch (...) {
// Do nothing
@ -423,7 +424,8 @@ bool PaletteEntryEditor::onProcessMessage(Message* msg)
// Redraw just the current editor
else {
m_redrawAll = true;
current_editor->updateEditor();
if (current_editor != NULL)
current_editor->updateEditor();
}
}
return Window::onProcessMessage(msg);

View File

@ -115,7 +115,8 @@ void PlayAnimationCommand::onExecute(Context* context)
oldpal = newpal;
}
current_editor->drawSpriteSafe(0, 0, sprite->getWidth(), sprite->getHeight());
current_editor->drawSpriteClipped
(gfx::Region(gfx::Rect(0, 0, sprite->getWidth(), sprite->getHeight())));
ui::dirty_display_flag = true;

View File

@ -142,6 +142,8 @@ static void save_as_dialog(const DocumentReader& document, const char* dlg_title
// Save the document
save_document_in_background(documentWriter, mark_as_saved);
update_screen_for_document(documentWriter);
}
}
@ -185,6 +187,7 @@ void SaveFileCommand::onExecute(Context* context)
DocumentWriter documentWriter(document);
save_document_in_background(documentWriter, true);
update_screen_for_document(documentWriter);
}
// If the document isn't associated to a file, we must to show the
// save-as dialog to the user to select for first time the file-name
@ -192,8 +195,6 @@ void SaveFileCommand::onExecute(Context* context)
else {
save_as_dialog(document, "Save File", true);
}
update_screen_for_document(document);
}
//////////////////////////////////////////////////////////////////////
@ -226,7 +227,6 @@ void SaveFileAsCommand::onExecute(Context* context)
{
const ActiveDocumentReader document(context);
save_as_dialog(document, "Save As", true);
update_screen_for_document(document);
}
//////////////////////////////////////////////////////////////////////
@ -267,9 +267,8 @@ void SaveFileCopyAsCommand::onExecute(Context* context)
{
DocumentWriter documentWriter(document);
documentWriter->setFilename(old_filename.c_str());
update_screen_for_document(documentWriter);
}
update_screen_for_document(document);
}
//////////////////////////////////////////////////////////////////////

View File

@ -1,107 +0,0 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "app.h"
#include "commands/command.h"
#include "commands/params.h"
#include "document_wrappers.h"
#include "modules/editors.h"
#include "ui/gui.h"
#include "ui_context.h"
#include <allegro/debug.h>
#include <allegro/unicode.h>
//////////////////////////////////////////////////////////////////////
// select_file
class SelectFileCommand : public Command
{
public:
SelectFileCommand();
Command* clone() { return new SelectFileCommand(*this); }
protected:
void onLoadParams(Params* params);
bool onEnabled(Context* context);
bool onChecked(Context* context);
void onExecute(Context* context);
private:
DocumentId m_documentId;
};
SelectFileCommand::SelectFileCommand()
: Command("SelectFile",
"Select File",
CmdUIOnlyFlag)
{
m_documentId = WithoutDocumentId;
}
void SelectFileCommand::onLoadParams(Params* params)
{
if (params->has_param("document_id")) {
m_documentId = (DocumentId)ustrtol(params->get("document_id").c_str(), NULL, 10);
}
}
bool SelectFileCommand::onEnabled(Context* context)
{
// m_documentId != 0, the ID specifies a Document
if (m_documentId != WithoutDocumentId) {
Document* document = context->getDocuments().getById(m_documentId);
return document != NULL;
}
// m_documentId=0, means the select "Nothing" option
else
return true;
}
bool SelectFileCommand::onChecked(Context* context)
{
const ActiveDocumentReader document(context);
if (m_documentId != WithoutDocumentId)
return document == context->getDocuments().getById(m_documentId);
else
return document == NULL;
}
void SelectFileCommand::onExecute(Context* context)
{
if (m_documentId != WithoutDocumentId) {
Document* document = context->getDocuments().getById(m_documentId);
ASSERT(document != NULL);
set_document_in_more_reliable_editor(document);
}
else {
set_document_in_more_reliable_editor(NULL);
}
}
//////////////////////////////////////////////////////////////////////
// CommandFactory
Command* CommandFactory::createSelectFileCommand()
{
return new SelectFileCommand;
}

View File

@ -18,34 +18,11 @@
#include "config.h"
#include "app.h"
#include "commands/command.h"
#include "modules/editors.h"
#include "ui/base.h"
//////////////////////////////////////////////////////////////////////
// close_editor
class CloseEditorCommand : public Command
{
public:
CloseEditorCommand();
Command* clone() { return new CloseEditorCommand(*this); }
protected:
void onExecute(Context* context);
};
CloseEditorCommand::CloseEditorCommand()
: Command("CloseEditor",
"Close Editor",
CmdUIOnlyFlag)
{
}
void CloseEditorCommand::onExecute(Context* context)
{
close_editor(current_editor);
}
#include "widgets/main_window.h"
#include "widgets/workspace.h"
//////////////////////////////////////////////////////////////////////
// make_unique_editor
@ -69,7 +46,9 @@ MakeUniqueEditorCommand::MakeUniqueEditorCommand()
void MakeUniqueEditorCommand::onExecute(Context* context)
{
make_unique_editor(current_editor);
widgets::Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
if (workspace->getActiveView() != NULL)
workspace->makeUnique(workspace->getActiveView());
}
//////////////////////////////////////////////////////////////////////
@ -94,7 +73,9 @@ SplitEditorHorizontallyCommand::SplitEditorHorizontallyCommand()
void SplitEditorHorizontallyCommand::onExecute(Context* context)
{
split_editor(current_editor, JI_HORIZONTAL);
widgets::Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
if (workspace->getActiveView() != NULL)
workspace->splitView(workspace->getActiveView(), JI_HORIZONTAL);
}
//////////////////////////////////////////////////////////////////////
@ -119,17 +100,14 @@ SplitEditorVerticallyCommand::SplitEditorVerticallyCommand()
void SplitEditorVerticallyCommand::onExecute(Context* context)
{
split_editor(current_editor, JI_VERTICAL);
widgets::Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
if (workspace->getActiveView() != NULL)
workspace->splitView(workspace->getActiveView(), JI_VERTICAL);
}
//////////////////////////////////////////////////////////////////////
// CommandFactory
Command* CommandFactory::createCloseEditorCommand()
{
return new CloseEditorCommand;
}
Command* CommandFactory::createMakeUniqueEditorCommand()
{
return new MakeUniqueEditorCommand;

View File

@ -240,7 +240,8 @@ void SpriteSizeCommand::onExecute(Context* context)
job.startJob();
}
update_screen_for_document(document);
DocumentWriter docWriter(document);
update_screen_for_document(docWriter);
}
}

View File

@ -81,7 +81,9 @@ void UndoCommand::onExecute(Context* context)
if (spritePosition != sprite->getCurrentPosition()) {
sprite->setCurrentPosition(spritePosition);
current_editor->drawSpriteSafe(0, 0, sprite->getWidth(), sprite->getHeight());
current_editor->drawSpriteClipped
(gfx::Region(gfx::Rect(0, 0, sprite->getWidth(), sprite->getHeight())));
update_screen_for_document(document);
ui::dirty_display_flag = true;

View File

@ -28,7 +28,6 @@ FOR_EACH_COMMAND(ChangePen)
FOR_EACH_COMMAND(ChangePixelFormat)
FOR_EACH_COMMAND(Clear)
FOR_EACH_COMMAND(CloseAllFiles)
FOR_EACH_COMMAND(CloseEditor)
FOR_EACH_COMMAND(CloseFile)
FOR_EACH_COMMAND(ColorCurve)
FOR_EACH_COMMAND(ConfigureTools)
@ -96,7 +95,6 @@ FOR_EACH_COMMAND(SaveFileAs)
FOR_EACH_COMMAND(SaveFileCopyAs)
FOR_EACH_COMMAND(SaveMask)
FOR_EACH_COMMAND(Scroll)
FOR_EACH_COMMAND(SelectFile)
FOR_EACH_COMMAND(ShowGrid)
FOR_EACH_COMMAND(SnapToGrid)
FOR_EACH_COMMAND(SplitEditorHorizontally)

View File

@ -34,6 +34,9 @@ Context::Context(ISettings* settings)
Context::~Context()
{
// The context must be empty at this point.
ASSERT(m_documents.empty());
delete m_settings;
}
@ -54,13 +57,23 @@ Document* Context::getNextDocument(Document* document) const
{
ASSERT(document != NULL);
Documents::const_iterator it = std::find(m_documents.begin(), m_documents.end(), document);
Documents::const_iterator begin = m_documents.begin();
Documents::const_iterator end = m_documents.end();
Documents::const_iterator it = std::find(begin, end, document);
ASSERT(it != end);
if (it != m_documents.end()) {
++it;
if (it != m_documents.end())
if (it != end) {
if (it != begin) {
--it;
return *it;
}
else {
++it;
if (it != end)
return *it;
}
}
return NULL;
}
@ -103,6 +116,9 @@ Document* Context::getActiveDocument() const
void Context::setActiveDocument(Document* document)
{
if (document == m_activeDocument)
return;
m_observers.notifyActiveDocumentBeforeChange(this);
m_activeDocument = document;

View File

@ -198,8 +198,6 @@ void switch_between_animation_and_sprite_editor()
// Destroy thumbnails
destroy_thumbnails();
update_screen_for_document(document);
}
//////////////////////////////////////////////////////////////////////

View File

@ -200,7 +200,7 @@ static void mask_preview(const DocumentReader& document)
{
DocumentWriter documentWriter(document);
documentWriter->generateMaskBoundaries(mask);
update_screen_for_document(documentWriter);
}
update_screen_for_document(document);
}
}

View File

@ -61,12 +61,6 @@ Document::Document(Sprite* sprite)
// Boundary stuff
m_bound.nseg = 0;
m_bound.seg = NULL;
// Preferred edition options
m_preferred.scroll_x = 0;
m_preferred.scroll_y = 0;
m_preferred.zoom = 0;
m_preferred.virgin = true;
}
Document::~Document()
@ -138,6 +132,18 @@ void Document::addSprite(Sprite* sprite)
notifyObservers<DocumentEvent&>(&DocumentObserver::onAddSprite, ev);
}
void Document::notifyGeneralUpdate()
{
DocumentEvent ev(this);
notifyObservers<DocumentEvent&>(&DocumentObserver::onGeneralUpdate, ev);
}
void Document::notifySpritePixelsModified(Sprite* sprite, const gfx::Region& region)
{
DocumentEvent ev(this, sprite, NULL, NULL, NULL, -1, FrameNumber(), region);
notifyObservers<DocumentEvent&>(&DocumentObserver::onSpritePixelsModified, ev);
}
const char* Document::getFilename() const
{
return m_filename.c_str();
@ -172,19 +178,6 @@ void Document::setFormatOptions(const SharedPtr<FormatOptions>& format_options)
m_format_options = format_options;
}
//////////////////////////////////////////////////////////////////////
// Preferred editor settings
PreferredEditorSettings Document::getPreferredEditorSettings() const
{
return m_preferred;
}
void Document::setPreferredEditorSettings(const PreferredEditorSettings& settings)
{
m_preferred = settings;
}
//////////////////////////////////////////////////////////////////////
// Boundaries
@ -473,7 +466,6 @@ Document* Document::duplicate(DuplicateType type) const
documentCopy->setMask(getMask());
documentCopy->m_maskVisible = m_maskVisible;
documentCopy->m_preferred = m_preferred;
documentCopy->generateMaskBoundaries();
return documentCopy.release();

View File

@ -40,13 +40,7 @@ class Mutex;
class Sprite;
struct _BoundSeg;
struct PreferredEditorSettings
{
int scroll_x;
int scroll_y;
int zoom;
bool virgin;
};
namespace gfx { class Region; }
enum DuplicateType
{
@ -85,6 +79,12 @@ public:
void addSprite(Sprite* sprite);
//////////////////////////////////////////////////////////////////////
// Notifications
void notifyGeneralUpdate();
void notifySpritePixelsModified(Sprite* sprite, const gfx::Region& region);
//////////////////////////////////////////////////////////////////////
// File related properties
@ -100,12 +100,6 @@ public:
void setFormatOptions(const SharedPtr<FormatOptions>& format_options);
//////////////////////////////////////////////////////////////////////
// Preferred editor settings
PreferredEditorSettings getPreferredEditorSettings() const;
void setPreferredEditorSettings(const PreferredEditorSettings& settings);
//////////////////////////////////////////////////////////////////////
// Boundaries
@ -208,9 +202,6 @@ private:
// Data to save the file in the same format that it was loaded
SharedPtr<FormatOptions> m_format_options;
// Preferred options in the editor.
PreferredEditorSettings m_preferred;
// Extra cel used to draw extra stuff (e.g. editor's pen preview, pixels in movement, etc.)
Cel* m_extraCel;

View File

@ -19,6 +19,7 @@
#ifndef DOCUMENT_EVENT_H_INCLUDED
#define DOCUMENT_EVENT_H_INCLUDED
#include "gfx/region.h"
#include "raster/frame_number.h"
class Cel;
@ -36,14 +37,16 @@ public:
Cel* cel = NULL,
Image* image = NULL,
int imageIndex = -1,
FrameNumber frame = FrameNumber())
FrameNumber frame = FrameNumber(),
const gfx::Region& region = gfx::Region())
: m_document(document)
, m_sprite(sprite)
, m_layer(layer)
, m_cel(cel)
, m_image(image)
, m_imageIndex(imageIndex)
, m_frame(frame) {
, m_frame(frame)
, m_region(region) {
}
Document* document() const { return m_document; }
@ -53,6 +56,7 @@ public:
Image* image() const { return m_image; }
int imageIndex() const { return m_imageIndex; }
FrameNumber frame() const { return m_frame; }
const gfx::Region& region() const { return m_region; }
private:
Document* m_document;
@ -62,6 +66,7 @@ private:
Image* m_image;
int m_imageIndex;
FrameNumber m_frame;
gfx::Region m_region;
};
#endif

View File

@ -29,6 +29,10 @@ class DocumentObserver {
public:
virtual ~DocumentObserver() { }
// General update. If an observer receives this event, it's because
// anything in the document could be changed.
virtual void onGeneralUpdate(DocumentEvent& ev) { }
virtual void onAddSprite(DocumentEvent& ev) { }
virtual void onAddLayer(DocumentEvent& ev) { }
virtual void onAddFrame(DocumentEvent& ev) { }
@ -51,6 +55,7 @@ public:
virtual void onImageReplaced(DocumentEvent& ev) { }
virtual void onImagePixelsModified(DocumentEvent& ev) { }
virtual void onSpritePixelsModified(DocumentEvent& ev) { }
// Called to destroy the observer. (Here you could call "delete this".)
virtual void dispose() { }

View File

@ -40,6 +40,9 @@ public:
const_iterator begin() const { return m_documents.begin(); }
const_iterator end() const { return m_documents.end(); }
Document* front() const { return m_documents.front(); }
Document* back() const { return m_documents.back(); }
int size() const { return m_documents.size(); }
bool empty() const { return m_documents.empty(); }

View File

@ -20,692 +20,4 @@
#include "modules/editors.h"
#include "app.h"
#include "document_wrappers.h"
#include "ini_file.h"
#include "modules/gui.h"
#include "modules/palettes.h"
#include "raster/image.h"
#include "raster/sprite.h"
#include "skin/skin_theme.h"
#include "ui/gui.h"
#include "ui_context.h"
#include "util/misc.h"
#include "widgets/editor/editor.h"
#include "widgets/editor/editor_customization_delegate.h"
#include "widgets/editor/editor_view.h"
#include "widgets/main_window.h"
#include "widgets/popup_window_pin.h"
#include "widgets/status_bar.h"
#include "widgets/toolbar.h"
#include <algorithm>
#include <vector>
using namespace ui;
#define FIXUP_TOP_WINDOW() \
App::instance()->getMainWindow()->remapWindow(); \
App::instance()->getMainWindow()->invalidate();
class EditorItem
{
public:
enum Type { Normal, Mini };
EditorItem(Editor* editor, Type type)
: m_editor(editor)
, m_type(type)
{ }
Editor* getEditor() const { return m_editor; }
Type getType() const { return m_type; }
private:
Editor* m_editor;
Type m_type;
};
typedef std::vector<EditorItem> EditorList;
Editor* current_editor = NULL;
Widget* box_editors = NULL;
static EditorList editors;
static bool mini_editor_enabled = true; // True if the user wants to use the mini editor
static Editor* mini_editor = NULL;
static int is_document_in_some_editor(Document* document);
static Document* get_more_reliable_document();
static Widget* find_next_editor(Widget* widget);
static int count_parents(Widget* widget);
static void create_mini_editor_window();
static void hide_mini_editor_window();
static void update_mini_editor_window(Editor* editor);
class WrappedEditor : public Editor,
public EditorObserver,
public EditorCustomizationDelegate
{
public:
WrappedEditor() {
addObserver(this);
setCustomizationDelegate(this);
}
~WrappedEditor() {
removeObserver(this);
setCustomizationDelegate(NULL);
}
// EditorObserver implementation
void dispose() OVERRIDE {
// Do nothing
}
void scrollChanged(Editor* editor) OVERRIDE {
update_mini_editor_window(editor);
}
void documentChanged(Editor* editor) OVERRIDE {
if (editor == current_editor)
update_mini_editor_window(editor);
}
void stateChanged(Editor* editor) OVERRIDE {
// Do nothing
}
// EditorCustomizationDelegate implementation
tools::Tool* getQuickTool(tools::Tool* currentTool) OVERRIDE {
return get_selected_quicktool(currentTool);
}
bool isCopySelectionKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_copy_selection();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isSnapToGridKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_snap_to_grid();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isAngleSnapKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_angle_snap();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isMaintainAspectRatioKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_maintain_aspect_ratio();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isLockAxisKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_lock_axis();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
};
class MiniEditor : public Editor
{
public:
MiniEditor() {
}
bool changePreferredSettings() OVERRIDE {
return false;
}
};
class MiniEditorWindow : public Window
{
public:
// Create mini-editor
MiniEditorWindow() : Window(false, "Mini-Editor") {
child_spacing = 0;
setAutoRemap(false);
setWantFocus(false);
}
protected:
void onClose(CloseEvent& ev) OVERRIDE {
Button* closeButton = dynamic_cast<Button*>(ev.getSource());
if (closeButton != NULL &&
closeButton->getId() == SkinTheme::kThemeCloseButtonId) {
// Here we don't use "enable_mini_editor" to change the state of
// "mini_editor_enabled" because we're coming from a close event
// of the window.
mini_editor_enabled = false;
// Redraw the tool bar because it shows the mini editor enabled state.
// TODO abstract this event
ToolBar::instance()->invalidate();
}
}
};
static MiniEditorWindow* mini_editor_window = NULL;
int init_module_editors()
{
mini_editor_enabled = get_config_bool("MiniEditor", "Enabled", true);
return 0;
}
void exit_module_editors()
{
set_config_bool("MiniEditor", "Enabled", mini_editor_enabled);
if (mini_editor_window) {
save_window_pos(mini_editor_window, "MiniEditor");
delete mini_editor_window;
mini_editor_window = NULL;
}
ASSERT(editors.empty());
}
Editor* create_new_editor()
{
Editor* editor = new WrappedEditor();
editors.push_back(EditorItem(editor, EditorItem::Normal));
return editor;
}
// Removes the specified editor from the "editors" list.
// It does not delete the editor.
void remove_editor(Editor* editor)
{
for (EditorList::iterator
it = editors.begin(),
end = editors.end(); it != end; ++it) {
if (it->getEditor() == editor) {
editors.erase(it);
return;
}
}
ASSERT(false && "Editor not found in the list");
}
void refresh_all_editors()
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
it->getEditor()->invalidate();
}
}
void update_editors_with_document(const Document* document)
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = it->getEditor();
if (document == editor->getDocument())
editor->updateEditor();
}
}
void editors_draw_sprite(const Sprite* sprite, int x1, int y1, int x2, int y2)
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = it->getEditor();
if (sprite == editor->getSprite() && editor->isVisible())
editor->drawSpriteSafe(x1, y1, x2, y2);
}
}
// TODO improve this (with JRegion or something, and without recursivity).
void editors_draw_sprite_tiled(const Sprite* sprite, int x1, int y1, int x2, int y2)
{
int cx1, cy1, cx2, cy2; // Cel rectangle.
int lx1, ly1, lx2, ly2; // Limited rectangle to the cel rectangle.
#ifdef TILED_IN_LAYER
Image *image = GetImage2(sprite, &cx1, &cy1, NULL);
cx2 = cx1+image->w-1;
cy2 = cy1+image->h-1;
#else
cx1 = 0;
cy1 = 0;
cx2 = cx1+sprite->getWidth()-1;
cy2 = cy1+sprite->getHeight()-1;
#endif
lx1 = MAX(x1, cx1);
ly1 = MAX(y1, cy1);
lx2 = MIN(x2, cx2);
ly2 = MIN(y2, cy2);
// Draw the rectangles inside the editor.
editors_draw_sprite(sprite, lx1, ly1, lx2, ly2);
// Left.
if (x1 < cx1 && lx2 < cx2) {
editors_draw_sprite_tiled(sprite,
MAX(lx2+1, cx2+1+(x1-cx1)), y1,
cx2, y2);
}
// Top.
if (y1 < cy1 && ly2 < cy2) {
editors_draw_sprite_tiled(sprite,
x1, MAX(ly2+1, cy2+1+(y1-cx1)),
x2, cy2);
}
// Right.
if (x2 >= cx2+1 && lx1 > cx1) {
#ifdef TILED_IN_LAYER
editors_draw_sprite_tiled(sprite,
cx1, y1,
MIN(lx1-1, x2-image->w), y2);
#else
editors_draw_sprite_tiled(sprite,
cx1, y1,
MIN(lx1-1, x2-sprite->getWidth()), y2);
#endif
}
// Bottom.
if (y2 >= cy2+1 && ly1 > cy1) {
#if TILED_IN_LAYER
editors_draw_sprite_tiled(sprite,
x1, cy1,
x2, MIN(ly1-1, y2-image->h));
#else
editors_draw_sprite_tiled(sprite,
x1, cy1,
x2, MIN(ly1-1, y2-sprite->getHeight()));
#endif
}
}
void editors_hide_document(const Document* document)
{
UIContext* context = UIContext::instance();
Document* activeDocument = context->getActiveDocument();
Sprite* activeSprite = (activeDocument ? activeDocument->getSprite(): NULL);
bool refresh = (activeSprite == document->getSprite()) ? true: false;
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = it->getEditor();
if (document == editor->getDocument())
editor->setDocument(get_more_reliable_document());
}
if (refresh) {
Document* document = current_editor->getDocument();
context->setActiveDocument(document);
app_refresh_screen(document);
}
}
void set_current_editor(Editor* editor)
{
if (current_editor != editor) {
// Here we check if the specified editor in the parameter is the
// mini-editor, in this case, we cannot put the mini-editor as the
// current one.
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
if (it->getEditor() == editor) {
if (it->getType() != EditorItem::Normal) {
// Avoid setting the mini-editor as the current one
return;
}
break;
}
}
if (current_editor)
View::getView(current_editor)->invalidate();
current_editor = editor;
View::getView(current_editor)->invalidate();
UIContext* context = UIContext::instance();
Document* document = current_editor->getDocument();
context->setActiveDocument(document);
app_refresh_screen(document);
app_rebuild_documents_tabs();
update_mini_editor_window(editor);
}
}
void set_document_in_current_editor(Document* document)
{
if (current_editor) {
UIContext* context = UIContext::instance();
context->setActiveDocument(document);
if (document != NULL)
context->sendDocumentToTop(document);
current_editor->setDocument(document);
View::getView(current_editor)->invalidate();
app_refresh_screen(document);
app_rebuild_documents_tabs();
}
}
void set_document_in_more_reliable_editor(Document* document)
{
// The current editor
Editor* best = current_editor;
// Search for any empty editor
if (best->getDocument()) {
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
// Avoid using abnormal editors (mini, etc.)
if (it->getType() != EditorItem::Normal)
continue;
Editor* editor = it->getEditor();
if (!editor->getDocument()) {
best = editor;
break;
}
}
}
set_current_editor(best);
set_document_in_current_editor(document);
}
void split_editor(Editor* editor, int align)
{
if (count_parents(editor) > 10) {
Alert::show("Error<<You cannot split this editor more||&Close");
return;
}
View* view = View::getView(editor);
Widget* parent_box = view->getParent(); // Box or splitter.
// Create a new box to contain both editors, and a new view to put the new editor.
Widget* new_splitter = new Splitter(Splitter::ByPercentage, align);
View* new_view = new EditorView(EditorView::CurrentEditorMode);
Editor* new_editor = create_new_editor();
// Insert the "new_box" in the same location that the view.
parent_box->replaceChild(view, new_splitter);
// Append the new editor.
new_view->attachToView(new_editor);
// Set the sprite for the new editor.
new_editor->setDocument(editor->getDocument());
new_editor->setZoom(editor->getZoom());
// Expansive widgets.
new_splitter->setExpansive(true);
new_view->setExpansive(true);
// Append both views to the "new_splitter".
new_splitter->addChild(view);
new_splitter->addChild(new_view);
// Same position.
{
new_view->setViewScroll(view->getViewScroll());
jrect_copy(new_view->rc, view->rc);
jrect_copy(new_view->getViewport()->rc, view->getViewport()->rc);
jrect_copy(new_editor->rc, editor->rc);
new_editor->setOffsetX(editor->getOffsetX());
new_editor->setOffsetY(editor->getOffsetY());
}
// Fixup window.
FIXUP_TOP_WINDOW();
// Update both editors.
editor->updateEditor();
new_editor->updateEditor();
}
void close_editor(Editor* editor)
{
View* view = View::getView(editor);
Widget* parent_box = view->getParent(); // Box or panel
Widget* other_widget;
// You can't remove all (normal) editors.
int normalEditors = 0;
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
if (it->getType() == EditorItem::Normal)
normalEditors++;
}
if (normalEditors == 1) // In this case we avoid to remove the last normal editor
return;
// Deselect the editor.
if (editor == current_editor)
current_editor = 0;
// Remove this editor.
parent_box->removeChild(view);
delete view;
// Fixup the parent.
other_widget = UI_FIRST_WIDGET(parent_box->getChildren());
parent_box->removeChild(other_widget);
parent_box->getParent()->replaceChild(parent_box, other_widget);
delete parent_box;
// Find next editor to select.
if (!current_editor) {
Widget* next_editor = find_next_editor(other_widget);
if (next_editor) {
ASSERT(next_editor->type == editor_type());
set_current_editor(static_cast<Editor*>(next_editor));
}
}
// Fixup window.
FIXUP_TOP_WINDOW();
// Update all editors.
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = it->getEditor();
editor->updateEditor();
}
}
void make_unique_editor(Editor* editor)
{
View* view = View::getView(editor);
// It's the unique editor.
if (editors.size() == 1)
return;
// Remove the editor-view of its parent.
view->getParent()->removeChild(view);
// Remove all children of main_editor_box.
while (!box_editors->getChildren().empty()) {
Widget* child = box_editors->getChildren().front();
box_editors->removeChild(child);
delete child; // widget
}
// Append the editor to main box.
box_editors->addChild(view);
// New current editor.
set_current_editor(editor);
// Fixup window.
FIXUP_TOP_WINDOW();
// Update new editor.
editor->updateEditor();
}
bool is_mini_editor_enabled()
{
return mini_editor_enabled;
}
void enable_mini_editor(bool state)
{
mini_editor_enabled = state;
update_mini_editor_window(current_editor);
}
static int is_document_in_some_editor(Document* document)
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = it->getEditor();
if (document == editor->getDocument())
return true;
}
return false;
}
// Returns the next sprite that should be show if we close the current one.
static Document* get_more_reliable_document()
{
UIContext* context = UIContext::instance();
const Documents& docs = context->getDocuments();
for (Documents::const_iterator
it = docs.begin(), end = docs.end(); it != end; ++it) {
Document* document = *it;
if (!(is_document_in_some_editor(document)))
return document;
}
return NULL;
}
static Widget* find_next_editor(Widget* widget)
{
Widget* editor = NULL;
if (widget->type == JI_VIEW) {
editor = UI_FIRST_WIDGET(static_cast<View*>(widget)->getViewport()->getChildren());
}
else {
UI_FOREACH_WIDGET(widget->getChildren(), it)
if ((editor = find_next_editor(*it)))
break;
}
return editor;
}
static int count_parents(Widget* widget)
{
int count = 0;
while ((widget = widget->getParent()))
count++;
return count;
}
static void create_mini_editor_window()
{
// Create mini-editor
mini_editor_window = new MiniEditorWindow();
// Create the new for the mini editor
View* newView = new EditorView(EditorView::AlwaysSelected);
newView->setExpansive(true);
// Create mini editor
mini_editor = new MiniEditor();
editors.push_back(EditorItem(mini_editor, EditorItem::Mini));
newView->attachToView(mini_editor);
mini_editor_window->addChild(newView);
// Default bounds
int width = JI_SCREEN_W/4;
int height = JI_SCREEN_H/4;
mini_editor_window->setBounds
(gfx::Rect(JI_SCREEN_W - width - jrect_w(ToolBar::instance()->rc),
JI_SCREEN_H - height - jrect_h(StatusBar::instance()->rc),
width, height));
load_window_pos(mini_editor_window, "MiniEditor");
}
static void hide_mini_editor_window()
{
if (mini_editor_window &&
mini_editor_window->isVisible()) {
mini_editor_window->closeWindow(NULL);
}
}
static void update_mini_editor_window(Editor* editor)
{
if (!mini_editor_enabled || !editor) {
hide_mini_editor_window();
return;
}
Document* document = editor->getDocument();
// Show the mini editor if it wasn't created yet and the user
// zoomed in, or if the mini-editor was created and the zoom of
// both editors is not the same.
if (document && document->getSprite() &&
((!mini_editor && editor->getZoom() > 0) ||
(mini_editor && mini_editor->getZoom() != editor->getZoom()))) {
// If the mini window does not exist, create it
if (!mini_editor_window)
create_mini_editor_window();
if (!mini_editor_window->isVisible())
mini_editor_window->openWindow();
gfx::Rect visibleBounds = editor->getVisibleSpriteBounds();
gfx::Point pt = visibleBounds.getCenter();
// Set the same location as in the given editor.
if (mini_editor->getDocument() != document) {
mini_editor->setDocument(document);
mini_editor->setZoom(0);
mini_editor->setState(EditorStatePtr(new EditorState));
}
mini_editor->centerInSpritePoint(pt.x, pt.y);
}
else {
hide_mini_editor_window();
}
}

View File

@ -19,37 +19,7 @@
#ifndef MODULES_EDITORS_H_INCLUDED
#define MODULES_EDITORS_H_INCLUDED
class Document;
class Editor;
class Sprite;
namespace ui { class Widget; }
extern Editor* current_editor;
extern ui::Widget* box_editors;
int init_module_editors();
void exit_module_editors();
Editor* create_new_editor();
void remove_editor(Editor* editor);
void set_current_editor(Editor* editor);
void refresh_all_editors();
void update_editors_with_document(const Document* document);
void editors_draw_sprite(const Sprite* sprite, int x1, int y1, int x2, int y2);
void editors_draw_sprite_tiled(const Sprite* sprite, int x1, int y1, int x2, int y2);
void editors_hide_document(const Document* document);
void set_document_in_current_editor(Document* document);
void set_document_in_more_reliable_editor(Document* document);
void split_editor(Editor* editor, int align);
void close_editor(Editor* editor);
void make_unique_editor(Editor* editor);
bool is_mini_editor_enabled();
void enable_mini_editor(bool state);
#endif

View File

@ -268,7 +268,7 @@ void set_screen_scaling(int scaling)
screen_scaling = scaling;
}
void update_screen_for_document(const Document* document)
void update_screen_for_document(Document* document)
{
// Without document.
if (!document) {
@ -280,21 +280,10 @@ void update_screen_for_document(const Document* document)
}
// With a document.
else {
const Sprite* sprite = document->getSprite();
// Select the palette of the sprite.
if (set_current_palette(sprite->getPalette(sprite->getCurrentFrame()), false)) {
// If the palette changes, invalidate the whole screen, we've to
// redraw it.
Manager::getDefault()->invalidate();
}
else {
// If it's the same palette update only the editors with the sprite.
update_editors_with_document(document);
}
document->notifyGeneralUpdate();
// Update the tabs (maybe the modified status has been changed).
app_update_document_tab(document);
app_rebuild_documents_tabs();
}
}
@ -311,7 +300,7 @@ void gui_feedback()
ui::UpdateCursorOverlay();
// Avoid updating a non-dirty screen over and over again.
#if 0 // It doesn't work
#if 0 // TODO It doesn't work yet
if (!dirty_display_flag)
return;
#endif

View File

@ -47,7 +47,7 @@ void exit_module_gui();
int get_screen_scaling();
void set_screen_scaling(int scaling);
void update_screen_for_document(const Document* document);
void update_screen_for_document(Document* document);
void gui_run();
void gui_feedback();

View File

@ -42,7 +42,7 @@ public:
Observers() { }
~Observers() {
disposeAllObservers();
ASSERT(m_observers.empty());
}
// Adds the observer in the collection. The observer is owned by the
@ -60,15 +60,6 @@ public:
m_observers.erase(it);
}
// Disposes all observers. It's called automatically in the
// Observers dtor.
void disposeAllObservers() {
while (!empty())
(*begin())->dispose();
m_observers.clear();
}
void notifyObservers(void (observer_type::*method)()) {
for (iterator
it = this->begin(),

View File

@ -26,8 +26,11 @@
#include "tools/point_shape.h"
#include "tools/tool.h"
#include "tools/tool_box.h"
#include "ui/manager.h"
#include "ui_context.h"
#include "widgets/color_bar.h"
#include "widgets/main_window.h"
#include "widgets/workspace.h"
#include <allegro/color.h>
#include <string>
@ -113,6 +116,11 @@ public:
virtual void setOnionskinOpacityStep(int step) OVERRIDE;
private:
void redrawDocumentViews() {
// TODO Redraw only document's views
ui::Manager::getDefault()->invalidate();
}
TiledMode m_tiledMode;
bool m_use_onionskin;
int m_prev_frames_onionskin;
@ -237,16 +245,19 @@ void UIDocumentSettingsImpl::setSnapToGrid(bool state)
void UIDocumentSettingsImpl::setGridVisible(bool state)
{
m_gridVisible = state;
redrawDocumentViews();
}
void UIDocumentSettingsImpl::setGridBounds(const Rect& rect)
{
m_gridBounds = rect;
redrawDocumentViews();
}
void UIDocumentSettingsImpl::setGridColor(const app::Color& color)
{
m_gridColor = color;
redrawDocumentViews();
}
void UIDocumentSettingsImpl::snapToGrid(gfx::Point& point, SnapBehavior snapBehavior) const
@ -279,11 +290,13 @@ app::Color UIDocumentSettingsImpl::getPixelGridColor()
void UIDocumentSettingsImpl::setPixelGridVisible(bool state)
{
m_pixelGridVisible = state;
redrawDocumentViews();
}
void UIDocumentSettingsImpl::setPixelGridColor(const app::Color& color)
{
m_pixelGridColor = color;
redrawDocumentViews();
}
bool UIDocumentSettingsImpl::getUseOnionskin()

View File

@ -324,6 +324,7 @@ SkinTheme::SkinTheme()
color_mapping["filelist_selected_row_text"] = ThemeColor::FileListSelectedRowText;
color_mapping["filelist_selected_row_face"] = ThemeColor::FileListSelectedRowFace;
color_mapping["filelist_disabled_row_text"] = ThemeColor::FileListDisabledRowText;
color_mapping["workspace"] = ThemeColor::Workspace;
reload_skin();
}

View File

@ -95,6 +95,7 @@ namespace ThemeColor {
FileListSelectedRowText,
FileListSelectedRowFace,
FileListDisabledRowText,
Workspace,
MaxColors
};
}

View File

@ -21,7 +21,6 @@
#include "filters/tiled_mode.h"
#include "gfx/point.h"
#include "gfx/rect.h"
#include "tools/trace_policy.h"
class Context;
@ -34,6 +33,8 @@ class Mask;
class Pen;
class Sprite;
namespace gfx { class Region; }
namespace tools {
class Controller;
@ -166,8 +167,12 @@ public:
// Converts a coordinate in the screen to the sprite.
virtual gfx::Point screenToSprite(const gfx::Point& screenPoint) = 0;
// Redraws in the screen the specified are of sprite.
virtual void updateArea(const gfx::Rect& dirty_area) = 0;
// This region is modified by the ToolLoopManager so then you know
// what must be updated in updateDirtyArea().
virtual gfx::Region& getDirtyArea() = 0;
// Redraws the dirty area.
virtual void updateDirtyArea() = 0;
virtual void updateStatusBar(const char* text) = 0;

View File

@ -21,7 +21,9 @@
#include "tools/tool_loop_manager.h"
#include "context.h"
#include "gfx/region.h"
#include "raster/image.h"
#include "raster/sprite.h"
#include "settings/document_settings.h"
#include "tools/controller.h"
#include "tools/ink.h"
@ -141,7 +143,7 @@ void ToolLoopManager::movement(const Pointer& pointer)
void ToolLoopManager::doLoopStep(bool last_step)
{
static Rect old_dirty_area; // TODO Not thread safe
static Region old_dirty_area; // TODO Not thread safe
Points points_to_interwine;
if (!last_step)
@ -179,21 +181,17 @@ void ToolLoopManager::doLoopStep(bool last_step)
else
m_toolLoop->getIntertwine()->fillPoints(m_toolLoop, points_to_interwine);
// Calculat the area to be updated in the screen/editor/sprite
Rect dirty_area;
// Calculate the area to be updated in all document observers.
Region& dirty_area = m_toolLoop->getDirtyArea();
calculateDirtyArea(m_toolLoop, points_to_interwine, dirty_area);
Rect new_dirty_area;
if (m_toolLoop->getTracePolicy() == TracePolicyLast) {
new_dirty_area = old_dirty_area.createUnion(dirty_area);
dirty_area.createUnion(dirty_area, old_dirty_area);
old_dirty_area = dirty_area;
}
else {
new_dirty_area = dirty_area;
}
if (!new_dirty_area.isEmpty())
m_toolLoop->updateArea(new_dirty_area);
if (!dirty_area.isEmpty())
m_toolLoop->updateDirtyArea();
}
// Applies the grid settings to the specified sprite point, if
@ -211,8 +209,10 @@ void ToolLoopManager::snapToGrid(bool flexible, Point& point)
->snapToGrid(point, (flexible ? SnapInRightBottom: NormalSnap));
}
void ToolLoopManager::calculateDirtyArea(ToolLoop* loop, const Points& points, Rect& dirty_area)
void ToolLoopManager::calculateDirtyArea(ToolLoop* loop, const Points& points, Region& dirty_area)
{
dirty_area.clear();
if (points.size() > 0) {
Point minpt, maxpt;
calculateMinMax(points, minpt, maxpt);
@ -221,10 +221,54 @@ void ToolLoopManager::calculateDirtyArea(ToolLoop* loop, const Points& points, R
Rect r1, r2;
loop->getPointShape()->getModifiedArea(loop, minpt.x, minpt.y, r1);
loop->getPointShape()->getModifiedArea(loop, maxpt.x, maxpt.y, r2);
dirty_area = r1.createUnion(r2);
dirty_area.createUnion(dirty_area, Region(r1.createUnion(r2)));
}
// Apply offset mode
Point offset(loop->getOffset());
dirty_area.offset(-offset);
// Apply tiled mode
TiledMode tiledMode = loop->getDocumentSettings()->getTiledMode();
if (tiledMode != TILED_NONE) {
int w = loop->getSprite()->getWidth();
int h = loop->getSprite()->getHeight();
Region sprite_area(Rect(0, 0, w, h));
Region outside;
outside.createSubtraction(dirty_area, sprite_area);
switch (tiledMode) {
case TILED_X_AXIS:
outside.createIntersection(outside, Region(Rect(-w*10000, 0, w*20000, h)));
break;
case TILED_Y_AXIS:
outside.createIntersection(outside, Region(Rect(0, -h*10000, w, h*20000)));
break;
}
Rect outsideBounds = outside.getBounds();
if (outsideBounds.x < 0) outside.offset(w * (1+((-outsideBounds.x) / w)), 0);
if (outsideBounds.y < 0) outside.offset(0, h * (1+((-outsideBounds.y) / h)));
int x1 = outside.getBounds().x;
while (true) {
Region in_sprite;
in_sprite.createIntersection(outside, sprite_area);
outside.createSubtraction(outside, in_sprite);
dirty_area.createUnion(dirty_area, in_sprite);
outsideBounds = outside.getBounds();
if (outsideBounds.isEmpty())
break;
else if (outsideBounds.x+outsideBounds.w > w)
outside.offset(-w, 0);
else if (outsideBounds.y+outsideBounds.h > h)
outside.offset(x1-outsideBounds.x, -h);
else
break;
}
}
else
dirty_area = Rect();
}
void ToolLoopManager::calculateMinMax(const Points& points, Point& minpt, Point& maxpt)

View File

@ -22,7 +22,8 @@
#include <vector>
#include "gfx/point.h"
#include "gfx/rect.h"
namespace gfx { class Region; }
namespace tools {
@ -99,7 +100,7 @@ private:
static void calculateDirtyArea(ToolLoop* loop,
const Points& points,
gfx::Rect& dirty_area);
gfx::Region& dirty_area);
static void calculateMinMax(const Points& points,
gfx::Point& minpt,

View File

@ -13,15 +13,14 @@
struct BITMAP;
#define JI_SCREEN_W ui::ji_screen_w
#define JI_SCREEN_H ui::ji_screen_h
namespace she { class Display; }
namespace ui {
/***********************************************************************/
/* screen related */
#define JI_SCREEN_W ji_screen_w
#define JI_SCREEN_H ji_screen_h
// Screen related
extern struct BITMAP* ji_screen;
extern int ji_screen_w;
@ -33,13 +32,11 @@ namespace ui {
void SetDisplay(she::Display* display);
/***********************************************************************/
/* timer related */
// Timer related
extern int volatile ji_clock; /* in milliseconds */
/***********************************************************************/
/* mouse related */
// Mouse related
// Updates the position of the mouse cursor overlay depending on the
// current mouse position.

View File

@ -28,35 +28,94 @@
#include "ui_context.h"
#include "undo/undo_history.h"
#include "widgets/color_bar.h"
#include "widgets/document_view.h"
#include "widgets/editor/editor.h"
#include "widgets/main_window.h"
#include "widgets/mini_editor.h"
#include "widgets/tabs.h"
#include "widgets/workspace.h"
#include <allegro/file.h>
#include <allegro/system.h>
UIContext* UIContext::m_instance = NULL;
using namespace widgets;
UIContext::UIContext()
: Context(new UISettingsImpl)
{
ASSERT(m_instance == NULL);
m_instance = this;
m_activeView = NULL;
}
UIContext::~UIContext()
{
// No views at this point.
ASSERT(m_allViews.empty());
ASSERT(m_activeView == NULL);
ASSERT(m_instance == this);
m_instance = NULL;
}
widgets::DocumentView* UIContext::getActiveView()
{
return m_activeView;
}
void UIContext::setActiveView(widgets::DocumentView* docView)
{
m_activeView = docView;
current_editor = (docView ? docView->getEditor(): NULL);
App::instance()->getMainWindow()->getTabsBar()->selectTab(docView);
App::instance()->getMainWindow()->getWorkspace()->setActiveView(docView);
if (current_editor)
current_editor->requestFocus();
setActiveDocument(docView ? docView->getDocument(): NULL);
App::instance()->getMainWindow()->getMiniEditor()->updateUsingEditor(current_editor);
}
size_t UIContext::countViewsOf(Document* document) const
{
size_t counter = 0;
for (DocumentViews::const_iterator
it=m_allViews.begin(), end=m_allViews.end(); it != end; ++it) {
DocumentView* view = *it;
if (view->getDocument() == document)
++counter;
}
return counter;
}
Editor* UIContext::getActiveEditor()
{
if (m_activeView)
return m_activeView->getEditor();
else
return NULL;
}
void UIContext::onAddDocument(Document* document)
{
// base method
Context::onAddDocument(document);
// add the tab for this sprite
App::instance()->getMainWindow()->getTabsBar()
->addTab(get_filename(document->getFilename()), document);
// Add a new view for this document
DocumentView* view = new DocumentView(document, DocumentView::Normal);
m_allViews.push_back(view);
// Add a tab with the new view for the document
App::instance()->getMainWindow()->getTabsBar()->addTab(view);
App::instance()->getMainWindow()->getWorkspace()->addView(view);
setActiveView(view);
view->getEditor()->setDefaultScroll();
// Rebuild the list of tabs
app_rebuild_documents_tabs();
@ -64,25 +123,48 @@ void UIContext::onAddDocument(Document* document)
void UIContext::onRemoveDocument(Document* document)
{
// base method
Context::onRemoveDocument(document);
// Remove this document from tabs
App::instance()->getMainWindow()->getTabsBar()->removeTab(document);
// Remove all views of this document
for (DocumentViews::iterator it=m_allViews.begin(); it != m_allViews.end(); ) {
DocumentView* view = *it;
if (view->getDocument() == document) {
App::instance()->getMainWindow()->getTabsBar()->removeTab(view);
App::instance()->getMainWindow()->getWorkspace()->removeView(view);
// We cannot point as "active view" this view that we're destroying.
if (view == m_activeView)
m_activeView = NULL;
delete view;
it = m_allViews.erase(it);
}
else
++it;
}
// Rebuild the tabs
app_rebuild_documents_tabs();
// Select other documents in the editors where are this sprite
editors_hide_document(document);
}
void UIContext::onSetActiveDocument(Document* document)
{
Context::onSetActiveDocument(document);
// Select the document in the tabs.
App::instance()->getMainWindow()->getTabsBar()->selectTab(document);
if (!document)
setActiveView(NULL);
// Select the first view with the given document.
for (DocumentViews::iterator it=m_allViews.begin(), end=m_allViews.end();
it != end; ++it) {
DocumentView* view = *it;
if (view->getDocument() == document) {
setActiveView(view);
break;
}
}
// Change the image-type of color bar.
ColorBar::instance()->setPixelFormat(app_get_current_pixel_format());

View File

@ -21,6 +21,11 @@
#include "context.h"
class Editor;
namespace widgets { class DocumentView; }
typedef std::vector<widgets::DocumentView*> DocumentViews;
class UIContext : public Context
{
public:
@ -31,14 +36,29 @@ public:
virtual bool isUiAvailable() const { return true; }
widgets::DocumentView* getActiveView();
void setActiveView(widgets::DocumentView* documentView);
// Returns the number of views that the given document has.
size_t countViewsOf(Document* document) const;
// Returns the current editor. It can be null.
Editor* getActiveEditor();
// Returns the active editor for the given document, or creates a
// new one if it's necessary.
Editor* getEditorFor(Document* document);
protected:
virtual void onAddDocument(Document* document);
virtual void onRemoveDocument(Document* document);
virtual void onSetActiveDocument(Document* document);
private:
static UIContext* m_instance;
DocumentViews m_allViews;
widgets::DocumentView* m_activeView;
static UIContext* m_instance;
};
#endif

View File

@ -167,6 +167,8 @@ void util::clipboard::copy_image(Image* image, Palette* pal, const gfx::Point& p
void util::clipboard::paste()
{
Editor* editor = current_editor;
if (editor == NULL)
return;
#ifdef ALLEGRO_WINDOWS
// Get the image from the clipboard.

View File

@ -0,0 +1,170 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "widgets/document_view.h"
#include "app.h"
#include "base/path.h"
#include "document_event.h"
#include "modules/editors.h"
#include "modules/gui.h"
#include "ui/accelerator.h"
#include "ui/message.h"
#include "ui/view.h"
#include "widgets/editor/editor.h"
#include "widgets/editor/editor_customization_delegate.h"
#include "widgets/editor/editor_view.h"
#include "widgets/main_window.h"
#include "widgets/mini_editor.h"
#include "widgets/workspace.h"
using namespace ui;
using namespace widgets;
class AppEditor : public Editor,
public EditorObserver,
public EditorCustomizationDelegate
{
public:
AppEditor(Document* document) : Editor(document) {
addObserver(this);
setCustomizationDelegate(this);
}
~AppEditor() {
removeObserver(this);
setCustomizationDelegate(NULL);
}
// EditorObserver implementation
void dispose() OVERRIDE {
App::instance()->getMainWindow()->getMiniEditor()->updateUsingEditor(NULL);
}
void scrollChanged(Editor* editor) OVERRIDE {
App::instance()->getMainWindow()->getMiniEditor()->updateUsingEditor(this);
}
void stateChanged(Editor* editor) OVERRIDE {
// Do nothing
}
// EditorCustomizationDelegate implementation
tools::Tool* getQuickTool(tools::Tool* currentTool) OVERRIDE {
return get_selected_quicktool(currentTool);
}
bool isCopySelectionKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_copy_selection();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isSnapToGridKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_snap_to_grid();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isAngleSnapKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_angle_snap();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isMaintainAspectRatioKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_maintain_aspect_ratio();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
bool isLockAxisKeyPressed() OVERRIDE {
Accelerator* accel = get_accel_to_lock_axis();
if (accel)
return accel->checkFromAllegroKeyArray();
else
return false;
}
};
DocumentView::DocumentView(Document* document, Type type)
: Box(JI_VERTICAL)
, m_document(document)
, m_view(new EditorView(type == Normal ? EditorView::CurrentEditorMode:
EditorView::AlwaysSelected))
, m_editor(type == Normal ? new AppEditor(document):
new Editor(document))
{
addChild(m_view);
m_view->attachToView(m_editor);
m_view->setExpansive(true);
m_view->hideScrollBars();
m_editor->setDocumentView(this);
m_document->addObserver(this);
}
DocumentView::~DocumentView()
{
m_document->removeObserver(this);
delete m_editor;
}
std::string DocumentView::getTabText()
{
std::string str = base::get_file_name(m_document->getFilename());
// Add an asterisk if the document is modified.
if (m_document->isModified())
str += "*";
return str;
}
bool DocumentView::onProcessMessage(Message* msg)
{
switch (msg->type) {
case JM_FOCUSENTER:
m_editor->requestFocus();
break;
}
return Box::onProcessMessage(msg);
}
void DocumentView::onGeneralUpdate(DocumentEvent& ev)
{
if (m_editor->isVisible())
m_editor->updateEditor();
}
void DocumentView::onSpritePixelsModified(DocumentEvent& ev)
{
if (m_editor->isVisible())
m_editor->drawSpriteClipped(ev.region());
}

View File

@ -0,0 +1,73 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef WIDGETS_DOCUMENT_VIEW_H_INCLUDED
#define WIDGETS_DOCUMENT_VIEW_H_INCLUDED
#include "base/compiler_specific.h"
#include "document_observer.h"
#include "ui/box.h"
#include "widgets/tabs.h"
#include "widgets/workspace_view.h"
class Document;
class Editor;
namespace ui { class View; }
namespace widgets {
class DocumentView : public ui::Box
, public TabView
, public DocumentObserver
, public WorkspaceView
{
public:
enum Type {
Normal,
Mini
};
DocumentView(Document* document, Type type);
~DocumentView();
Document* getDocument() const { return m_document; }
Editor* getEditor() { return m_editor; }
// TabView implementation
std::string getTabText() OVERRIDE;
// WorkspaceView implementation
ui::Widget* getContentWidget() OVERRIDE { return this; }
// DocumentObserver implementation
void onGeneralUpdate(DocumentEvent& ev);
void onSpritePixelsModified(DocumentEvent& ev);
protected:
bool onProcessMessage(ui::Message* msg) OVERRIDE;
private:
Document* m_document;
ui::View* m_view;
Editor* m_editor;
};
} // namespace widgets
#endif

View File

@ -152,20 +152,20 @@ static void on_palette_change_update_cursor_color()
static void on_pen_size_before_change()
{
ASSERT(current_editor != NULL);
pen_size_thick = current_editor->getCursorThick();
if (pen_size_thick)
current_editor->hideDrawingCursor();
if (current_editor != NULL) {
pen_size_thick = current_editor->getCursorThick();
if (pen_size_thick)
current_editor->hideDrawingCursor();
}
}
static void on_pen_size_after_change()
{
ASSERT(current_editor != NULL);
// Show drawing cursor
if (current_editor->getSprite() && pen_size_thick > 0)
current_editor->showDrawingCursor();
if (current_editor != NULL) {
// Show drawing cursor
if (current_editor->getSprite() && pen_size_thick > 0)
current_editor->showDrawingCursor();
}
}
static Pen* editor_get_current_pen()
@ -285,10 +285,11 @@ void Editor::editor_draw_cursor(int x, int y, bool refresh)
int pen_color = get_pen_color(m_sprite);
uint32_t new_mask_color;
Pen* pen = editor_get_current_pen();
int half = pen->get_size()/2;
// Create the extra cel to show the pen preview
m_document->prepareExtraCel(x-pen->get_size()/2,
y-pen->get_size()/2,
m_document->prepareExtraCel(x-half,
y-half,
pen->get_size(), pen->get_size(),
tool_settings->getOpacity());
@ -304,14 +305,15 @@ void Editor::editor_draw_cursor(int x, int y, bool refresh)
Image* extraImage = m_document->getExtraCelImage();
if (extraImage->mask_color != new_mask_color)
image_clear(extraImage, extraImage->mask_color = new_mask_color);
image_putpen(extraImage, pen, pen->get_size()/2, pen->get_size()/2, pen_color, extraImage->mask_color);
image_putpen(extraImage, pen, half, half, pen_color, extraImage->mask_color);
if (refresh) {
editors_draw_sprite(m_sprite,
x-pen->get_size()/2,
y-pen->get_size()/2,
x+pen->get_size()/2,
y+pen->get_size()/2);
m_document->notifySpritePixelsModified
(m_sprite,
gfx::Region(gfx::Rect(x-half,
y-half,
pen->get_size(),
pen->get_size())));
}
}
@ -361,11 +363,11 @@ void Editor::editor_move_cursor(int x, int y, bool refresh)
if (cursor_type & CURSOR_PENCIL && m_state->requirePenPreview()) {
Pen* pen = editor_get_current_pen();
editors_draw_sprite(m_sprite,
std::min(new_x, old_x)-pen->get_size()/2,
std::min(new_y, old_y)-pen->get_size()/2,
std::max(new_x, old_x)+pen->get_size()/2,
std::max(new_y, old_y)+pen->get_size()/2);
int half = pen->get_size()/2;
gfx::Rect rc1(old_x-half, old_y-half, pen->get_size(), pen->get_size());
gfx::Rect rc2(new_x-half, new_y-half, pen->get_size(), pen->get_size());
m_document->notifySpritePixelsModified
(m_sprite, gfx::Region(rc1.createUnion(rc2)));
}
/* save area and draw the cursor */
@ -423,11 +425,12 @@ void Editor::editor_clean_cursor(bool refresh)
0); // Opacity = 0
if (refresh) {
editors_draw_sprite(m_sprite,
x-pen->get_size()/2,
y-pen->get_size()/2,
x+pen->get_size()/2,
y+pen->get_size()/2);
m_document->notifySpritePixelsModified
(m_sprite,
gfx::Region(gfx::Rect(x-pen->get_size()/2,
y-pen->get_size()/2,
pen->get_size(),
pen->get_size())));
}
}

View File

@ -30,7 +30,6 @@
#include "commands/params.h"
#include "document_wrappers.h"
#include "ini_file.h"
#include "modules/editors.h"
#include "modules/gfx.h"
#include "modules/gui.h"
#include "modules/palettes.h"
@ -123,20 +122,20 @@ private:
Editor* m_editor;
};
Editor::Editor()
Editor::Editor(Document* document)
: Widget(editor_type())
, m_state(new StandbyState())
, m_decorator(NULL)
, m_document(document)
, m_sprite(m_document->getSprite())
, m_zoom(0)
, m_mask_timer(100, this)
, m_customizationDelegate(NULL)
, m_docView(NULL)
{
// Add the first state into the history.
m_statesHistory.push(m_state);
m_document = NULL;
m_sprite = NULL;
m_zoom = 0;
m_cursor_thick = 0;
m_cursor_screen_x = 0;
m_cursor_screen_y = 0;
@ -163,7 +162,6 @@ Editor::~Editor()
setCustomizationDelegate(NULL);
m_mask_timer.stop();
remove_editor(this);
// Remove this editor as observer of CurrentToolChange signal.
App::instance()->CurrentToolChange.disconnect(m_currentToolChangeSlot);
@ -238,61 +236,13 @@ void Editor::backToPreviousState()
setStateInternal(EditorStatePtr(NULL));
}
void Editor::setDocument(Document* document)
void Editor::setDefaultScroll()
{
//if (this->hasMouse())
//jmanager_free_mouse(); // TODO Why is this here? Review this code
View* view = View::getView(this);
Rect vp = view->getViewportBounds();
// Reset all states (back to standby).
EditorStatePtr firstState(new StandbyState);
m_statesHistory.clear();
m_statesHistory.push(firstState);
setState(firstState);
if (m_cursor_thick)
editor_clean_cursor();
// Change the sprite
m_document = document;
if (m_document) {
m_sprite = m_document->getSprite();
// Get the preferred doc's settings to edit it
PreferredEditorSettings preferred = m_document->getPreferredEditorSettings();
// Change the editor's configuration using the retrieved doc's settings
m_zoom = preferred.zoom;
updateEditor();
if (preferred.virgin) {
View* view = View::getView(this);
Rect vp = view->getViewportBounds();
preferred.virgin = false;
preferred.scroll_x = -vp.w/2 + (m_sprite->getWidth()/2);
preferred.scroll_y = -vp.h/2 + (m_sprite->getHeight()/2);
m_document->setPreferredEditorSettings(preferred);
}
setEditorScroll(m_offset_x + preferred.scroll_x,
m_offset_y + preferred.scroll_y,
false);
}
// In this case document is NULL
else {
m_sprite = NULL;
updateEditor();
setEditorScroll(0, 0, false); // No scroll
}
// Redraw the entire editor (because we have a new sprite to draw)
invalidate();
// Notify observers
m_observers.notifyDocumentChanged(this);
setEditorScroll(m_offset_x - vp.w/2 + (m_sprite->getWidth()/2),
m_offset_y - vp.h/2 + (m_sprite->getHeight()/2), false);
}
// Sets the scroll position of the editor
@ -314,17 +264,6 @@ void Editor::setEditorScroll(int x, int y, int use_refresh_region)
view->setViewScroll(Point(x, y));
Point newScroll = view->getViewScroll();
if (m_document && changePreferredSettings()) {
PreferredEditorSettings preferred;
preferred.virgin = false;
preferred.scroll_x = newScroll.x - m_offset_x;
preferred.scroll_y = newScroll.y - m_offset_y;
preferred.zoom = m_zoom;
m_document->setPreferredEditorSettings(preferred);
}
if (use_refresh_region) {
// Move screen with blits
scrollRegion(region,
@ -344,7 +283,7 @@ void Editor::updateEditor()
View::getView(this)->updateView();
}
void Editor::drawSprite(int x1, int y1, int x2, int y2)
void Editor::drawSpriteUnclippedRect(const gfx::Rect& rc)
{
View* view = View::getView(this);
Rect vp = view->getViewportBounds();
@ -356,12 +295,12 @@ void Editor::drawSprite(int x1, int y1, int x2, int y2)
// Output information
source_x = x1 << m_zoom;
source_y = y1 << m_zoom;
source_x = rc.x << m_zoom;
source_y = rc.y << m_zoom;
dest_x = vp.x - scroll.x + m_offset_x + source_x;
dest_y = vp.y - scroll.y + m_offset_y + source_y;
width = (x2 - x1 + 1) << m_zoom;
height = (y2 - y1 + 1) << m_zoom;
width = rc.w << m_zoom;
height = rc.h << m_zoom;
// Clip from viewport
@ -491,7 +430,7 @@ void Editor::drawSprite(int x1, int y1, int x2, int y2)
}
}
void Editor::drawSpriteSafe(int x1, int y1, int x2, int y2)
void Editor::drawSpriteClipped(const gfx::Region& updateRegion)
{
Region region;
getDrawableRegion(region, kCutTopWindows);
@ -499,12 +438,17 @@ void Editor::drawSpriteSafe(int x1, int y1, int x2, int y2)
int cx1, cy1, cx2, cy2;
get_clip_rect(ji_screen, &cx1, &cy1, &cx2, &cy2);
for (Region::const_iterator it=region.begin(), end=region.end();
it != end; ++it) {
for (Region::const_iterator
it=region.begin(), end=region.end(); it != end; ++it) {
const Rect& rc = *it;
add_clip_rect(ji_screen, rc.x, rc.y, rc.x2()-1, rc.y2()-1);
drawSprite(x1, y1, x2, y2);
for (Region::const_iterator
it2=updateRegion.begin(), end2=updateRegion.end(); it2 != end2; ++it2) {
drawSpriteUnclippedRect(*it2);
}
set_clip_rect(ji_screen, cx1, cy1, cx2, cy2);
}
}
@ -922,7 +866,7 @@ bool Editor::onProcessMessage(Message* msg)
x1-1, y1-1, x2+1, y2+2, theme->getColor(ThemeColor::EditorFace));
// Draw the sprite in the editor
drawSprite(0, 0, m_sprite->getWidth()-1, m_sprite->getHeight()-1);
drawSpriteUnclippedRect(gfx::Rect(0, 0, m_sprite->getWidth(), m_sprite->getHeight()));
// Draw the sprite boundary
rect(ji_screen, x1-1, y1-1, x2+1, y2+1, to_system(theme->getColor(ThemeColor::EditorSpriteBorder)));
@ -956,7 +900,7 @@ bool Editor::onProcessMessage(Message* msg)
case JM_TIMER:
if (msg->timer.timer == &m_mask_timer) {
if (m_sprite) {
if (isVisible() && m_sprite) {
drawMaskSafe();
// Set offset to make selection-movement effect
@ -965,6 +909,9 @@ bool Editor::onProcessMessage(Message* msg)
else
m_offset_count = 0;
}
else if (m_mask_timer.isRunning()) {
m_mask_timer.stop();
}
}
break;

View File

@ -23,6 +23,7 @@
#include "base/compiler_specific.h"
#include "base/signal.h"
#include "document.h"
#include "gfx/rect.h"
#include "ui/base.h"
#include "ui/timer.h"
#include "ui/widget.h"
@ -38,20 +39,20 @@ class EditorCustomizationDelegate;
class PixelsMovement;
class Sprite;
namespace ui {
class View;
}
namespace tools {
class Tool;
}
namespace gfx { class Region; }
namespace tools { class Tool; }
namespace ui { class View; }
namespace widgets { class DocumentView; }
class Editor : public ui::Widget
{
public:
Editor();
Editor(Document* document);
~Editor();
widgets::DocumentView* getDocumentView() { return m_docView; }
void setDocumentView(widgets::DocumentView* docView) { m_docView = docView; }
// Returns the current state.
EditorStatePtr getState() { return m_state; }
@ -67,7 +68,6 @@ public:
void setDecorator(EditorDecorator* decorator) { m_decorator = decorator; }
Document* getDocument() { return m_document; }
void setDocument(Document* document);
Sprite* getSprite() { return m_sprite; }
@ -80,19 +80,14 @@ public:
void setOffsetX(int x) { m_offset_x = x; }
void setOffsetY(int y) { m_offset_y = y; }
void setDefaultScroll();
void setEditorScroll(int x, int y, int use_refresh_region);
// Updates the Editor's view.
void updateEditor();
// Draws the specified portion of sprite in the editor.
// Warning: You should setup the clip of the ji_screen before
// calling this routine.
void drawSprite(int x1, int y1, int x2, int y2);
// Draws the sprite taking care of the whole clipping region.
// For each rectangle calls Editor::drawSprite.
void drawSpriteSafe(int x1, int y1, int x2, int y2);
void drawSpriteClipped(const gfx::Region& updateRegion);
void drawMask();
void drawMaskSafe();
@ -158,12 +153,6 @@ protected:
void onCurrentToolChange();
void onFgColorChange();
// Returns true if this editor should change the preferred document
// settings.
virtual bool changePreferredSettings() {
return true;
}
private:
void setStateInternal(const EditorStatePtr& newState);
void editor_update_quicktool();
@ -180,6 +169,11 @@ private:
int sprite_x, int sprite_y, int color,
void (*pixel)(BITMAP *bmp, int x, int y, int color));
// Draws the specified portion of sprite in the editor. Warning:
// You should setup the clip of the screen before calling this
// routine.
void drawSpriteUnclippedRect(const gfx::Rect& rc);
// Stack of states. The top element in the stack is the current state (m_state).
EditorStatesHistory m_statesHistory;
@ -225,6 +219,10 @@ private:
EditorCustomizationDelegate* m_customizationDelegate;
// TODO This field shouldn't be here. It should be removed when
// editors.cpp are finally replaced with a fully funtional Workspace
// widget.
widgets::DocumentView* m_docView;
};
int editor_type();

View File

@ -32,9 +32,6 @@ public:
// Called when the scroll or zoom of the editor changes.
virtual void scrollChanged(Editor* editor) = 0;
// Called when the document shown in the editor changes.
virtual void documentChanged(Editor* editor) = 0;
};
#endif // WIDGETS_EDITOR_OBSERVER_H_INCLUDED

View File

@ -46,8 +46,3 @@ void EditorObservers::notifyScrollChanged(Editor* editor)
{
m_observers.notifyObservers(&EditorObserver::scrollChanged, editor);
}
void EditorObservers::notifyDocumentChanged(Editor* editor)
{
m_observers.notifyObservers(&EditorObserver::documentChanged, editor);
}

View File

@ -34,7 +34,6 @@ public:
void notifyStateChanged(Editor* editor);
void notifyScrollChanged(Editor* editor);
void notifyDocumentChanged(Editor* editor);
private:
Observers<EditorObserver> m_observers;

View File

@ -22,7 +22,6 @@
#include "app.h"
#include "app/color.h"
#include "modules/editors.h"
#include "modules/gui.h"
#include "raster/image.h"
#include "raster/sprite.h"

View File

@ -27,7 +27,6 @@
#include "commands/command.h"
#include "commands/commands.h"
#include "gfx/rect.h"
#include "modules/editors.h"
#include "modules/gui.h"
#include "raster/algorithm/flip_image.h"
#include "raster/mask.h"
@ -258,18 +257,8 @@ bool MovingPixelsState::onMouseMove(Editor* editor, Message* msg)
transfHandles->invalidateHandles(editor, m_pixelsMovement->getTransformation());
// Drag the image to that position
gfx::Rect bounds = m_pixelsMovement->moveImage(x, y, moveModifier);
m_pixelsMovement->moveImage(x, y, moveModifier);
// If "bounds" is empty is because the cel was not moved
if (!bounds.isEmpty()) {
// Redraw the extra cel in the new position
jmouse_hide();
editors_draw_sprite(editor->getSprite(),
bounds.x, bounds.y,
bounds.x+bounds.w-1,
bounds.y+bounds.h-1);
jmouse_show();
}
editor->updateStatusBar();
return true;
}
@ -413,10 +402,9 @@ void MovingPixelsState::onChangeTransparentColor(const app::Color& color)
void MovingPixelsState::setTransparentColor(const app::Color& color)
{
ASSERT(current_editor != NULL);
ASSERT(m_pixelsMovement != NULL);
Sprite* sprite = current_editor->getSprite();
Sprite* sprite = m_currentEditor->getSprite();
ASSERT(sprite != NULL);
PixelFormat format = sprite->getPixelFormat();

View File

@ -22,6 +22,7 @@
#include "app.h"
#include "document.h"
#include "gfx/region.h"
#include "la/vector2d.h"
#include "modules/gui.h"
#include "raster/algorithm/flip_image.h"
@ -97,9 +98,8 @@ void PixelsMovement::flipImage(raster::algorithm::FlipType flipType)
documentWriter->setMask(m_currentMask);
documentWriter->generateMaskBoundaries(m_currentMask);
update_screen_for_document(documentWriter);
}
update_screen_for_document(m_documentReader);
}
void PixelsMovement::cutMask()
@ -119,9 +119,8 @@ void PixelsMovement::copyMask()
{
DocumentWriter documentWriter(m_documentReader);
documentWriter->generateMaskBoundaries(&emptyMask);
update_screen_for_document(documentWriter);
}
update_screen_for_document(m_documentReader);
}
void PixelsMovement::catchImage(int x, int y, HandleType handle)
@ -151,9 +150,8 @@ void PixelsMovement::catchImageAgain(int x, int y, HandleType handle)
{
DocumentWriter documentWriter(m_documentReader);
documentWriter->generateMaskBoundaries(&emptyMask);
update_screen_for_document(documentWriter);
}
update_screen_for_document(m_documentReader);
}
void PixelsMovement::maskImage(const Image* image, int x, int y)
@ -168,10 +166,10 @@ void PixelsMovement::maskImage(const Image* image, int x, int y)
documentWriter->setMask(m_currentMask);
documentWriter->generateMaskBoundaries(m_currentMask);
update_screen_for_document(m_documentReader);
update_screen_for_document(documentWriter);
}
gfx::Rect PixelsMovement::moveImage(int x, int y, MoveModifier moveModifier)
void PixelsMovement::moveImage(int x, int y, MoveModifier moveModifier)
{
gfx::Transformation::Corners oldCorners;
m_currentData.transformBox(oldCorners);
@ -404,7 +402,12 @@ gfx::Rect PixelsMovement::moveImage(int x, int y, MoveModifier moveModifier)
fullBounds = fullBounds.createUnion(gfx::Rect(oldCorners[i].x, oldCorners[i].y, 1, 1));
fullBounds = fullBounds.createUnion(gfx::Rect(newCorners[i].x, newCorners[i].y, 1, 1));
}
return fullBounds;
// If "fullBounds" is empty is because the cel was not moved
if (!fullBounds.isEmpty()) {
// Notify the modified region.
documentWriter->notifySpritePixelsModified(m_sprite, gfx::Region(fullBounds));
}
}
Image* PixelsMovement::getDraggedImageCopy(gfx::Point& origin)
@ -500,9 +503,8 @@ void PixelsMovement::dropImageTemporarily()
}
documentWriter->generateMaskBoundaries(m_currentMask);
update_screen_for_document(documentWriter);
}
update_screen_for_document(m_documentReader);
}
void PixelsMovement::dropImage()
@ -567,9 +569,8 @@ void PixelsMovement::setMaskColor(uint32_t mask_color)
extraImage->mask_color = mask_color;
redrawExtraImage(documentWriter);
update_screen_for_document(documentWriter);
}
update_screen_for_document(m_documentReader);
}

View File

@ -61,9 +61,8 @@ public:
void maskImage(const Image* image, int x, int y);
// Moves the image to the new position (relative to the start
// position given in the ctor). Returns the rectangle that should be
// redrawn.
gfx::Rect moveImage(int x, int y, MoveModifier moveModifier);
// position given in the ctor).
void moveImage(int x, int y, MoveModifier moveModifier);
// Returns a copy of the current image being dragged with the
// current transformation.

View File

@ -22,7 +22,6 @@
#include "app.h"
#include "gfx/rect.h"
#include "modules/editors.h"
#include "raster/sprite.h"
#include "ui/message.h"
#include "ui/system.h"

View File

@ -25,7 +25,6 @@
#include "commands/params.h"
#include "gfx/rect.h"
#include "ini_file.h"
#include "modules/editors.h"
#include "raster/layer.h"
#include "raster/mask.h"
#include "raster/sprite.h"
@ -142,13 +141,10 @@ bool StandbyState::onMouseDown(Editor* editor, Message* msg)
tools::Tool* current_tool = editor->getCurrentEditorTool();
tools::Ink* clickedInk = current_tool->getInk(msg->mouse.right ? 1: 0);
Sprite* sprite = editor->getSprite();
// Each time an editor is clicked the current editor and the active
// document are set.
set_current_editor(editor);
Document* document = editor->getDocument();
context->setActiveDocument(document);
// When an editor is clicked the current view is changed.
context->setActiveView(editor->getDocumentView());
// Start scroll loop
if (checkForScroll(editor, msg))

View File

@ -24,7 +24,6 @@
#include "app/color.h"
#include "app/color_utils.h"
#include "context.h"
#include "modules/editors.h"
#include "raster/cel.h"
#include "raster/layer.h"
#include "raster/mask.h"
@ -75,6 +74,7 @@ class ToolLoopImpl : public tools::ToolLoop
int m_secondary_color;
UndoTransaction m_undoTransaction;
ExpandCelCanvas m_expandCelCanvas;
gfx::Region m_dirtyArea;
public:
ToolLoopImpl(Editor* editor,
@ -225,16 +225,14 @@ public:
return spritePoint;
}
void updateArea(const gfx::Rect& dirty_area) OVERRIDE
gfx::Region& getDirtyArea() OVERRIDE
{
int x1 = dirty_area.x-m_offset.x;
int y1 = dirty_area.y-m_offset.y;
int x2 = dirty_area.x-m_offset.x+dirty_area.w-1;
int y2 = dirty_area.y-m_offset.y+dirty_area.h-1;
return m_dirtyArea;
}
acquire_bitmap(ji_screen);
editors_draw_sprite_tiled(m_sprite, x1, y1, x2, y2);
release_bitmap(ji_screen);
void updateDirtyArea() OVERRIDE
{
m_document->notifySpritePixelsModified(m_sprite, m_dirtyArea);
}
void updateStatusBar(const char* text) OVERRIDE

View File

@ -30,21 +30,18 @@
#include "ui/view.h"
#include "ui_context.h"
#include "widgets/color_bar.h"
#include "widgets/document_view.h"
#include "widgets/editor/editor.h"
#include "widgets/editor/editor_view.h"
#include "widgets/main_menu_bar.h"
#include "widgets/mini_editor.h"
#include "widgets/status_bar.h"
#include "widgets/tabs.h"
#include "widgets/toolbar.h"
#include "widgets/workspace.h"
using namespace ui;
class AppTabsDelegate : public TabsDelegate
{
public:
void clickTab(Tabs* tabs, void* data, int button);
void mouseOverTab(Tabs* tabs, void* data);
};
using namespace widgets;
MainWindow::MainWindow()
: Window(true, NULL)
@ -59,21 +56,20 @@ MainWindow::MainWindow()
Widget* mainBox = app::load_widget<Widget>("main_window.xml", "main_box");
addChild(mainBox);
box_editors = findChild("editor"); // WARNING / TODO this is a
// global variable defined in
// legacy "editors" modules
Widget* box_menubar = findChild("menubar");
Widget* box_colorbar = findChild("colorbar");
Widget* box_toolbar = findChild("toolbar");
Widget* box_statusbar = findChild("statusbar");
Widget* box_tabsbar = findChild("tabsbar");
Widget* box_workspace = findChild("workspace");
m_menuBar = new MainMenuBar();
m_statusBar = new StatusBar();
m_colorBar = new ColorBar(box_colorbar->getAlign());
m_toolBar = new ToolBar();
m_tabsBar = new Tabs(m_tabsDelegate = new AppTabsDelegate());
m_tabsBar = new Tabs(this);
m_workspace = new Workspace();
m_miniEditor = new MiniEditorWindow();
m_colorBarSplitter = findChildT<Splitter>("colorbarsplitter");
// configure all widgets to expansives
@ -82,45 +78,40 @@ MainWindow::MainWindow()
m_colorBar->setExpansive(true);
m_toolBar->setExpansive(true);
m_tabsBar->setExpansive(true);
m_workspace->setExpansive(true);
// Setup the menus
m_menuBar->setMenu(AppMenus::instance()->getRootMenu());
/* start text of status bar */
// Start text of status bar
app_default_statusbar_message();
/* add the widgets in the boxes */
// Add the widgets in the boxes
if (box_menubar) box_menubar->addChild(m_menuBar);
if (box_colorbar) box_colorbar->addChild(m_colorBar);
if (box_toolbar) box_toolbar->addChild(m_toolBar);
if (box_statusbar) box_statusbar->addChild(m_statusBar);
if (box_tabsbar) box_tabsbar->addChild(m_tabsBar);
if (box_workspace) box_workspace->addChild(m_workspace);
// Prepare the window
remapWindow();
// Initialize editors.
init_module_editors();
// Create the list of tabs
app_rebuild_documents_tabs();
AppMenus::instance()->rebuildRecentList();
}
MainWindow::~MainWindow()
{
delete m_tabsDelegate;
delete m_miniEditor;
// Destroy the workspace first so ~Editor can dettach slots from
// ColorBar. TODO this is a terrible hack for slot/signal stuff,
// connections should be handle in a better/safer way.
delete m_workspace;
// Remove the root-menu from the menu-bar (because the rootmenu
// module should destroy it).
m_menuBar->setMenu(NULL);
// Delete all editors first because they used signals from other
// widgets (e.g. color bar).
delete box_editors;
// Destroy mini-editor.
exit_module_editors();
}
void MainWindow::reloadMenus()
@ -131,22 +122,6 @@ void MainWindow::reloadMenus()
invalidate();
}
void MainWindow::createFirstEditor()
{
View* view = new EditorView(EditorView::CurrentEditorMode);
Editor* editor = create_new_editor();
// Prepare the first editor
view->attachToView(editor);
view->setExpansive(true);
if (box_editors)
box_editors->addChild(view);
// Set current editor
set_current_editor(editor); // TODO remove this line from here
}
void MainWindow::setAdvancedMode(bool advanced)
{
// Check if we already are in the given mode.
@ -180,47 +155,43 @@ void MainWindow::onSaveLayout(SaveLayoutEvent& ev)
m_colorBarSplitter->setPosition(m_lastSplitterPos);
}
//////////////////////////////////////////////////////////////////////
// AppTabsDelegate
void AppTabsDelegate::clickTab(Tabs* tabs, void* data, int button)
void MainWindow::clickTab(Tabs* tabs, TabView* tabView, int button)
{
Document* document = reinterpret_cast<Document*>(data);
if (!tabView)
return;
// put as current sprite
set_document_in_more_reliable_editor(document);
DocumentView* docView = static_cast<DocumentView*>(tabView);
Document* document = docView->getDocument();
if (document) {
Context* context = UIContext::instance();
context->updateFlags();
UIContext* context = UIContext::instance();
context->setActiveView(docView);
context->updateFlags();
// right-button: popup-menu
if (button & 2) {
Menu* popup_menu = AppMenus::instance()->getDocumentTabPopupMenu();
if (popup_menu != NULL) {
popup_menu->showPopup(jmouse_x(0), jmouse_y(0));
}
// Right-button: popup-menu
if (button & 2) {
Menu* popup_menu = AppMenus::instance()->getDocumentTabPopupMenu();
if (popup_menu != NULL) {
popup_menu->showPopup(jmouse_x(0), jmouse_y(0));
}
// middle-button: close the sprite
else if (button & 4) {
Command* close_file_cmd =
CommandsModule::instance()->getCommandByName(CommandId::CloseFile);
}
// Middle-button: close the sprite
else if (button & 4) {
Command* close_file_cmd =
CommandsModule::instance()->getCommandByName(CommandId::CloseFile);
context->executeCommand(close_file_cmd, NULL);
}
context->executeCommand(close_file_cmd, NULL);
}
}
void AppTabsDelegate::mouseOverTab(Tabs* tabs, void* data)
void MainWindow::mouseOverTab(Tabs* tabs, TabView* tabView)
{
// Note: data can be NULL
Document* document = (Document*)data;
if (data) {
StatusBar::instance()->setStatusText(250, "%s",
static_cast<const char*>(document->getFilename()));
// Note: tabView can be NULL
if (tabView) {
DocumentView* docView = static_cast<DocumentView*>(tabView);
Document* document = docView->getDocument();
m_statusBar->setStatusText(250, "%s", static_cast<const char*>(document->getFilename()));
}
else {
StatusBar::instance()->clearText();
m_statusBar->clearText();
}
}

View File

@ -20,18 +20,21 @@
#define MAIN_WINDOW_H_INCLUDED
#include "ui/window.h"
#include "widgets/tabs.h"
class AppTabsDelegate;
class ColorBar;
class MainMenuBar;
class StatusBar;
class Tabs;
namespace ui {
class Splitter;
namespace ui { class Splitter; }
namespace widgets
{
class MiniEditorWindow;
class Workspace;
}
class MainWindow : public ui::Window
, public TabsDelegate
{
public:
MainWindow();
@ -39,18 +42,22 @@ public:
MainMenuBar* getMenuBar() { return m_menuBar; }
Tabs* getTabsBar() { return m_tabsBar; }
widgets::Workspace* getWorkspace() { return m_workspace; }
widgets::MiniEditorWindow* getMiniEditor() { return m_miniEditor; }
void reloadMenus();
void createFirstEditor();
bool isAdvancedMode() const { return m_advancedMode; }
void setAdvancedMode(bool advanced);
// TabsDelegate implementation.
void clickTab(Tabs* tabs, TabView* tabView, int button);
void mouseOverTab(Tabs* tabs, TabView* tabView);
protected:
void onSaveLayout(ui::SaveLayoutEvent& ev) OVERRIDE;
private:
AppTabsDelegate* m_tabsDelegate;
MainMenuBar* m_menuBar; // the menu bar widget
StatusBar* m_statusBar; // the status bar widget
ColorBar* m_colorBar; // the color bar widget
@ -59,6 +66,8 @@ private:
Tabs* m_tabsBar; // The tabs bar widget
double m_lastSplitterPos;
bool m_advancedMode;
widgets::Workspace* m_workspace; // The workspace (document's views)
widgets::MiniEditorWindow* m_miniEditor;
};
#endif

144
src/widgets/mini_editor.cpp Normal file
View File

@ -0,0 +1,144 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "widgets/mini_editor.h"
#include "gfx/rect.h"
#include "ini_file.h"
#include "modules/editors.h"
#include "modules/gui.h"
#include "skin/skin_theme.h"
#include "ui/base.h"
#include "ui/button.h"
#include "ui/close_event.h"
#include "ui/rect.h"
#include "ui/system.h"
#include "widgets/editor/editor.h"
#include "widgets/status_bar.h"
#include "widgets/toolbar.h"
using namespace ui;
namespace widgets {
MiniEditorWindow::MiniEditorWindow()
: Window(false, "Mini-Editor")
, m_docView(NULL)
{
child_spacing = 0;
setAutoRemap(false);
setWantFocus(false);
// Default bounds
int width = JI_SCREEN_W/4;
int height = JI_SCREEN_H/4;
setBounds(gfx::Rect(JI_SCREEN_W - width - jrect_w(ToolBar::instance()->rc),
JI_SCREEN_H - height - jrect_h(StatusBar::instance()->rc),
width, height));
load_window_pos(this, "MiniEditor");
m_isEnabled = get_config_bool("MiniEditor", "Enabled", true);
}
MiniEditorWindow::~MiniEditorWindow()
{
set_config_bool("MiniEditor", "Enabled", m_isEnabled);
save_window_pos(this, "MiniEditor");
}
void MiniEditorWindow::setMiniEditorEnabled(bool state)
{
m_isEnabled = state;
updateUsingEditor(current_editor);
}
void MiniEditorWindow::onClose(ui::CloseEvent& ev)
{
Button* closeButton = dynamic_cast<Button*>(ev.getSource());
if (closeButton != NULL &&
closeButton->getId() == SkinTheme::kThemeCloseButtonId) {
// Here we don't use "setMiniEditorEnabled" to change the state of
// "m_isEnabled" because we're coming from a close event of the
// window.
m_isEnabled = false;
// Redraw the tool bar because it shows the mini editor enabled
// state. TODO abstract this event
ToolBar::instance()->invalidate();
delete m_docView;
m_docView = NULL;
}
}
void MiniEditorWindow::updateUsingEditor(Editor* editor)
{
if (!m_isEnabled || !editor) {
hideWindow();
return;
}
Document* document = editor->getDocument();
Editor* miniEditor = (m_docView ? m_docView->getEditor(): NULL);
// Show the mini editor if it wasn't created yet and the user
// zoomed in, or if the mini-editor was created and the zoom of
// both editors is not the same.
if (document &&
document->getSprite() &&
((!isVisible() && editor->getZoom() > 0) ||
(isVisible() && (!miniEditor || miniEditor->getZoom() != editor->getZoom())))) {
if (!isVisible())
openWindow();
gfx::Rect visibleBounds = editor->getVisibleSpriteBounds();
gfx::Point pt = visibleBounds.getCenter();
// Set the same location as in the given editor.
if (!miniEditor || miniEditor->getDocument() != document) {
delete m_docView;
m_docView = new DocumentView(document, DocumentView::Mini); // MiniEditorDocumentView(document, this);
addChild(m_docView);
miniEditor = m_docView->getEditor();
miniEditor->setZoom(0);
miniEditor->setState(EditorStatePtr(new EditorState));
layout();
}
miniEditor->centerInSpritePoint(pt.x, pt.y);
}
else {
hideWindow();
}
}
void MiniEditorWindow::hideWindow()
{
delete m_docView;
m_docView = NULL;
if (isVisible())
closeWindow(NULL);
}
} // namespace widgets

50
src/widgets/mini_editor.h Normal file
View File

@ -0,0 +1,50 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef WIDGETS_MINI_EDITOR_VIEW_H_INCLUDED
#define WIDGETS_MINI_EDITOR_VIEW_H_INCLUDED
#include "ui/window.h"
#include "widgets/document_view.h"
namespace widgets {
class MiniEditorWindow : public ui::Window
{
public:
MiniEditorWindow();
~MiniEditorWindow();
bool isMiniEditorEnabled() const { return m_isEnabled; }
void setMiniEditorEnabled(bool state);
void updateUsingEditor(Editor* editor);
protected:
void onClose(ui::CloseEvent& ev) OVERRIDE;
private:
void hideWindow();
bool m_isEnabled;
DocumentView* m_docView;
};
} // namespace widgets
#endif

View File

@ -779,8 +779,8 @@ bool StatusBar::onProcessMessage(Message* msg)
sprite->setCurrentLayer(layer);
// Flash the current layer
ASSERT(current_editor != NULL); // Cannot be null when we have a current sprite
current_editor->flashCurrentLayer();
if (current_editor != NULL)
current_editor->flashCurrentLayer();
// Redraw the status-bar
invalidate();

View File

@ -122,10 +122,10 @@ Tabs::~Tabs()
delete m_button_right; // widget
}
void Tabs::addTab(const char* text, void* data)
void Tabs::addTab(TabView* tabView)
{
Tab *tab = new Tab(text, data);
tab->width = calcTabWidth(tab);
Tab* tab = new Tab(tabView);
calcTabWidth(tab);
m_list_of_tabs.push_back(tab);
@ -133,70 +133,66 @@ void Tabs::addTab(const char* text, void* data)
setScrollX(m_scrollX);
startAni(ANI_ADDING_TAB);
//invalidate();
}
void Tabs::removeTab(void* data)
void Tabs::removeTab(TabView* tabView)
{
Tab *tab = getTabByData(data);
Tab* tab = getTabByView(tabView);
if (!tab)
return;
if (tab) {
if (m_hot == tab) m_hot = NULL;
if (m_selected == tab) m_selected = NULL;
if (m_hot == tab) m_hot = NULL;
if (m_selected == tab) m_selected = NULL;
TabsListIterator it =
std::find(m_list_of_tabs.begin(), m_list_of_tabs.end(), tab);
TabsListIterator it =
std::find(m_list_of_tabs.begin(), m_list_of_tabs.end(), tab);
ASSERT(it != m_list_of_tabs.end() && "Removing a tab that is not part of the Tabs widget");
ASSERT(it != m_list_of_tabs.end() && "Removing a tab that is not part of the Tabs widget");
it = m_list_of_tabs.erase(it);
it = m_list_of_tabs.erase(it);
// Width of the removed tab
if (m_removedTab) {
delete m_removedTab;
m_removedTab = NULL;
}
m_removedTab = tab;
// Next tab in the list
if (it != m_list_of_tabs.end())
m_nextTabOfTheRemovedOne = *it;
else
m_nextTabOfTheRemovedOne = NULL;
// Update scroll (in the same position if we can)
setScrollX(m_scrollX);
startAni(ANI_REMOVING_TAB);
// Width of the removed tab
if (m_removedTab) {
delete m_removedTab;
m_removedTab = NULL;
}
m_removedTab = tab;
// Next tab in the list
if (it != m_list_of_tabs.end())
m_nextTabOfTheRemovedOne = *it;
else
m_nextTabOfTheRemovedOne = NULL;
// Update scroll (in the same position if we can)
setScrollX(m_scrollX);
startAni(ANI_REMOVING_TAB);
}
void Tabs::setTabText(const char* text, void* data)
void Tabs::updateTabsText()
{
Tab* tab = getTabByData(data);
if (tab) {
TabsListIterator it, end = m_list_of_tabs.end();
for (it = m_list_of_tabs.begin(); it != end; ++it) {
Tab* tab = *it;
// Change text of the tab
tab->text = text;
tab->width = calcTabWidth(tab);
// Make it visible (if it's the selected)
if (m_selected == tab)
makeTabVisible(tab);
invalidate();
calcTabWidth(tab);
}
invalidate();
}
void Tabs::selectTab(void* data)
void Tabs::selectTab(TabView* tabView)
{
Tab *tab = getTabByData(data);
Tab *tab = getTabByView(tabView);
if (tab != NULL)
selectTabInternal(tab);
}
void Tabs::selectNextTab()
{
TabsListIterator currentTabIt = getTabIteratorByData(m_selected->data);
TabsListIterator currentTabIt = getTabIteratorByView(m_selected->view);
TabsListIterator it = currentTabIt;
if (it != m_list_of_tabs.end()) {
// If we are at the end of the list, cycle to the first tab.
@ -209,14 +205,14 @@ void Tabs::selectNextTab()
if (it != currentTabIt) {
selectTabInternal(*it);
if (m_delegate)
m_delegate->clickTab(this, m_selected->data, 1);
m_delegate->clickTab(this, m_selected->view, 1);
}
}
}
void Tabs::selectPreviousTab()
{
TabsListIterator currentTabIt = getTabIteratorByData(m_selected->data);
TabsListIterator currentTabIt = getTabIteratorByView(m_selected->view);
TabsListIterator it = currentTabIt;
if (it != m_list_of_tabs.end()) {
// If we are at the beginning of the list, cycle to the last tab.
@ -229,15 +225,15 @@ void Tabs::selectPreviousTab()
if (it != currentTabIt) {
selectTabInternal(*it);
if (m_delegate)
m_delegate->clickTab(this, m_selected->data, 1);
m_delegate->clickTab(this, m_selected->view, 1);
}
}
}
void* Tabs::getSelectedTab()
TabView* Tabs::getSelectedTab()
{
if (m_selected != NULL)
return m_selected->data;
return m_selected->view;
else
return NULL;
}
@ -359,7 +355,7 @@ bool Tabs::onProcessMessage(Message* msg)
if (m_selected && m_delegate)
m_delegate->clickTab(this,
m_selected->data,
m_selected->view,
msg->mouse.flags);
}
return true;
@ -457,7 +453,7 @@ void Tabs::onSetText()
for (it = m_list_of_tabs.begin(); it != end; ++it) {
Tab* tab = *it;
tab->width = calcTabWidth(tab);
calcTabWidth(tab);
}
}
@ -523,21 +519,21 @@ void Tabs::drawTab(BITMAP* bmp, JRect box, Tab* tab, int y_delta, bool selected)
#endif
}
Tabs::TabsListIterator Tabs::getTabIteratorByData(void* data)
Tabs::TabsListIterator Tabs::getTabIteratorByView(TabView* tabView)
{
TabsListIterator it, end = m_list_of_tabs.end();
for (it = m_list_of_tabs.begin(); it != end; ++it) {
if ((*it)->data == data)
if ((*it)->view == tabView)
break;
}
return it;
}
Tabs::Tab* Tabs::getTabByData(void* data)
Tabs::Tab* Tabs::getTabByView(TabView* tabView)
{
TabsListIterator it = getTabIteratorByData(data);
TabsListIterator it = getTabIteratorByView(tabView);
if (it != m_list_of_tabs.end())
return *it;
else
@ -656,7 +652,7 @@ void Tabs::calculateHot()
m_hot = hot;
if (m_delegate)
m_delegate->mouseOverTab(this, m_hot ? m_hot->data: NULL);
m_delegate->mouseOverTab(this, m_hot ? m_hot->view: NULL);
invalidate();
}
@ -665,15 +661,18 @@ void Tabs::calculateHot()
jrect_free(box);
}
int Tabs::calcTabWidth(Tab* tab)
void Tabs::calcTabWidth(Tab* tab)
{
// Cache current tab text
tab->text = tab->view->getTabText();
int border = 4*jguiscale();
#ifdef CLOSE_BUTTON_IN_EACH_TAB
SkinTheme* theme = static_cast<SkinTheme*>(this->theme);
int close_icon_w = theme->get_part(PART_WINDOW_CLOSE_BUTTON_NORMAL)->w;
return (border + text_length(getFont(), tab->text.c_str()) + border + close_icon_w + border);
tab->width = (border + text_length(getFont(), tab->text.c_str()) + border + close_icon_w + border);
#else
return (border + text_length(getFont(), tab->text.c_str()) + border);
tab->width = (border + text_length(getFont(), tab->text.c_str()) + border);
#endif
}

View File

@ -27,6 +27,17 @@
class Tabs;
// Required interface to be implemented by each new tab that is added
// in the Tabs widget.
class TabView
{
public:
virtual ~TabView() { }
// Returns the text to be shown in the tab.
virtual std::string getTabText() = 0;
};
// Interface used to control notifications from the Tabs widget.
class TabsDelegate
{
@ -37,29 +48,22 @@ public:
// button & 1 => left click
// button & 2 => right click
// button & 4 => middle click
virtual void clickTab(Tabs* tabs, void* data, int button) = 0;
virtual void clickTab(Tabs* tabs, TabView* tabView, int button) = 0;
// Called when the mouse is over a tab (the data can be null if the
// mouse just leave all tabs)
virtual void mouseOverTab(Tabs* tabs, void* data) = 0;
virtual void mouseOverTab(Tabs* tabs, TabView* tabView) = 0;
};
// Tabs control.
//
// Used to show opened files/sprites.
// Tabs control. Used to show opened documents.
class Tabs : public ui::Widget
{
struct Tab
{
std::string text; // Label in the tab
void* data; // Opaque pointer to user data
int width; // Width of the tab
struct Tab {
TabView* view;
std::string text;
int width;
Tab(const char* text, void* data)
{
this->text = text;
this->data = data;
this->width = 0;
Tab(TabView* view) : view(view), width(0) {
}
};
@ -76,15 +80,14 @@ public:
Tabs(TabsDelegate* delegate);
~Tabs();
void addTab(const char* text, void* data);
void removeTab(void* data);
void addTab(TabView* tabView);
void removeTab(TabView* tabView);
void updateTabsText();
void setTabText(const char* text, void* data);
void selectTab(void* data);
void selectTab(TabView* tabView);
void selectNextTab();
void selectPreviousTab();
void* getSelectedTab();
TabView* getSelectedTab();
void startScrolling();
void stopScrolling();
@ -101,17 +104,17 @@ private:
void selectTabInternal(Tab* tab);
void drawTab(BITMAP* bmp, ui::JRect box, Tab* tab, int y_delta, bool selected);
TabsListIterator getTabIteratorByData(void* data);
Tab* getTabByData(void* data);
TabsListIterator getTabIteratorByView(TabView* tabView);
Tab* getTabByView(TabView* tabView);
int getMaxScrollX();
void makeTabVisible(Tab* tab);
void setScrollX(int scroll_x);
void calculateHot();
int calcTabWidth(Tab* tab);
void calcTabWidth(Tab* tab);
TabsList m_list_of_tabs;
Tab *m_hot;
Tab *m_selected;
Tab* m_hot;
Tab* m_selected;
int m_scrollX;
// Variables for animation purposes

View File

@ -34,6 +34,8 @@
#include "tools/tool_box.h"
#include "ui/gui.h"
#include "ui_context.h"
#include "widgets/main_window.h"
#include "widgets/mini_editor.h"
#include "widgets/status_bar.h"
#include <allegro.h>
@ -190,7 +192,7 @@ bool ToolBar::onProcessMessage(Message* msg)
toolrc = getToolGroupBounds(MiniEditorVisibilityIndex);
toolrc.offset(-msg->draw.rect.x1, -msg->draw.rect.y1);
isHot = (m_hot_index == MiniEditorVisibilityIndex ||
is_mini_editor_enabled());
App::instance()->getMainWindow()->getMiniEditor()->isMiniEditorEnabled());
theme->draw_bounds_nw(doublebuffer,
toolrc,
isHot ? PART_TOOLBUTTON_HOT_NW:
@ -248,7 +250,10 @@ bool ToolBar::onProcessMessage(Message* msg)
toolrc = getToolGroupBounds(MiniEditorVisibilityIndex);
if (msg->mouse.y >= toolrc.y && msg->mouse.y < toolrc.y+toolrc.h) {
// Switch the state of the mini editor
enable_mini_editor(!is_mini_editor_enabled());
widgets::MiniEditorWindow* miniEditorWindow =
App::instance()->getMainWindow()->getMiniEditor();
bool state = miniEditorWindow->isMiniEditorEnabled();
miniEditorWindow->setMiniEditorEnabled(!state);
}
break;
}
@ -501,7 +506,7 @@ void ToolBar::openTipWindow(int group_index, Tool* tool)
tooltip = "Configure Tool";
}
else if (group_index == MiniEditorVisibilityIndex) {
if (is_mini_editor_enabled())
if (App::instance()->getMainWindow()->getMiniEditor()->isMiniEditorEnabled())
tooltip = "Disable Mini-Editor";
else
tooltip = "Enable Mini-Editor";

96
src/widgets/workspace.cpp Normal file
View File

@ -0,0 +1,96 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "widgets/workspace.h"
#include "skin/skin_theme.h"
#include "widgets/workspace_view.h"
#include <algorithm>
using namespace ui;
using namespace widgets;
Workspace::Workspace()
: Box(JI_VERTICAL)
, m_activeView(NULL)
{
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
setBgColor(theme->getColor(ThemeColor::Workspace));
}
Workspace::~Workspace()
{
}
void Workspace::addView(WorkspaceView* view)
{
m_views.push_back(view);
addChild(view->getContentWidget());
setActiveView(view);
}
void Workspace::removeView(WorkspaceView* view)
{
WorkspaceViews::iterator it = std::find(m_views.begin(), m_views.end(), view);
ASSERT(it != m_views.end());
m_views.erase(it);
removeChild(view->getContentWidget());
setActiveView(NULL);
}
void Workspace::setActiveView(WorkspaceView* view)
{
m_activeView = view;
Widget* newContent = (view ? view->getContentWidget(): NULL);
WidgetsList children = getChildren();
UI_FOREACH_WIDGET(children, it) {
if ((*it) != newContent)
(*it)->setVisible(false);
}
if (newContent) {
newContent->setExpansive(true);
newContent->setVisible(true);
newContent->requestFocus();
}
layout();
}
void Workspace::splitView(WorkspaceView* view, int orientation)
{
// TODO
}
void Workspace::closeView(WorkspaceView* view)
{
// TODO
}
void Workspace::makeUnique(WorkspaceView* view)
{
// TODO
}

53
src/widgets/workspace.h Normal file
View File

@ -0,0 +1,53 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef WIDGETS_WORKSPACE_H_INCLUDED
#define WIDGETS_WORKSPACE_H_INCLUDED
#include "ui/box.h"
#include <vector>
namespace widgets {
class WorkspaceView;
typedef std::vector<WorkspaceView*> WorkspaceViews;
class Workspace : public ui::Box
{
public:
Workspace();
~Workspace();
void addView(WorkspaceView* view);
void removeView(WorkspaceView* view);
WorkspaceView* getActiveView() { return m_activeView; }
void setActiveView(WorkspaceView* view);
void splitView(WorkspaceView* view, int orientation);
void closeView(WorkspaceView* view);
void makeUnique(WorkspaceView* view);
private:
WorkspaceView* m_activeView;
WorkspaceViews m_views;
};
} // namespace widgets
#endif

View File

@ -0,0 +1,36 @@
/* ASEPRITE
* Copyright (C) 2001-2012 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef WIDGETS_WORKSPACE_VIEW_H_INCLUDED
#define WIDGETS_WORKSPACE_VIEW_H_INCLUDED
namespace ui { class Widget; }
namespace widgets {
class WorkspaceView
{
public:
virtual ~WorkspaceView() { }
virtual ui::Widget* getContentWidget() = 0;
};
} // namespace widgets
#endif