/** * Cleans the brush cursor from the specified editor. * * The mouse position is got from the last * call to @c drawBrushPreview. So you must * to use this routine only if you called * @c drawBrushPreview before with the specified * editor @a widget. * * @param widget The editor widget * * @see drawBrushPreview */ void Editor::clearBrushPreview(bool refresh) { ASSERT(m_cursorThick != 0); ASSERT(m_sprite != NULL); getDrawableRegion(clipping_region, kCutTopWindows); gfx::Point pos = m_cursorEditor; if (refresh) { // Restore pixels ScreenGraphics g; SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, m_cursorScreen, pos, gfx::ColorNone, clearpixel); } // Clean pixel/brush preview if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { m_document->destroyExtraCel(); if (refresh) { m_document->notifySpritePixelsModified (m_sprite, gfx::Region(lastBrushBounds)); } } m_cursorThick = 0; clipping_region.clear(); old_clipping_region.clear(); }
static void clearpixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) { if (saved_pixel_n < MAX_SAVED) { if (clipping_region.contains(pt)) g->putPixel(saved_pixel[saved_pixel_n++], pt.x, pt.y); else if (!old_clipping_region.isEmpty() && old_clipping_region.contains(pt)) saved_pixel_n++; } }
void StandbyState::Decorator::getInvalidDecoratoredRegion(Editor* editor, gfx::Region& region) { gfx::Rect box1, box2; if (getSymmetryHandles(editor, box1, box2)) { region.createUnion(region, gfx::Region(box1)); region.createUnion(region, gfx::Region(box2)); } }
void defer_invalid_rect(const gfx::Rect& rc) { if (!defered_invalid_timer) defered_invalid_timer = new ui::Timer(250, manager); defered_invalid_timer->stop(); defered_invalid_timer->start(); defered_invalid_region.createUnion(defered_invalid_region, gfx::Region(rc)); }
PatchCel::PatchCel(doc::Cel* dstCel, const doc::Image* patch, const gfx::Region& patchedRegion, const gfx::Point& patchPos) : WithCel(dstCel) , m_patch(patch) , m_region(patchedRegion) , m_pos(patchPos) { ASSERT(!patchedRegion.isEmpty()); }
static void drawpixel(ui::Graphics* graphics, const gfx::Point& pt, gfx::Color color) { if (saved_pixel_n < MAX_SAVED && clipping_region.contains(pt)) { if (cursor_negative) { int c = saved_pixel[saved_pixel_n++]; int r = gfx::getr(c); int g = gfx::getg(c); int b = gfx::getb(c); graphics->putPixel(color_utils::blackandwhite_neg(gfx::rgba(r, g, b)), pt.x, pt.y); } else { graphics->putPixel(color, pt.x, pt.y); } } }
void Graphics::fillRegion(gfx::Color color, const gfx::Region& rgn) { for (gfx::Region::iterator it=rgn.begin(), end=rgn.end(); it!=end; ++it) fillRect(color, *it); }
static void savepixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) { if (saved_pixel_n < MAX_SAVED && clipping_region.contains(pt)) saved_pixel[saved_pixel_n++] = g->getPixel(pt.x, pt.y); }
namespace app { using namespace ui; // Returns true if the cursor of the editor needs subpixel movement. #define IS_SUBPIXEL(editor) ((editor)->m_zoom.scale() >= 4.0) // Maximum quantity of colors to save pixels overlapped by the cursor. #define MAX_SAVED 4096 static struct { int brush_type; int brush_size; int brush_angle; int nseg; BoundSeg* seg; } cursor_bound = { 0, 0, 0, 0, NULL }; enum { CURSOR_THINCROSS = 1, CURSOR_THICKCROSS = 2, CURSOR_BRUSHBOUNDS = 4 }; static int cursor_type = CURSOR_THINCROSS; static int cursor_negative; static gfx::Color saved_pixel[MAX_SAVED]; static int saved_pixel_n; // These clipping regions are shared between all editors, so we cannot // make assumptions about their old state static gfx::Region clipping_region; static gfx::Region old_clipping_region; static void generate_cursor_boundaries(Editor* editor); static void trace_thincross_pixels(ui::Graphics* g, Editor* editor, const gfx::Point& pt, gfx::Color color, Editor::PixelDelegate pixel); static void trace_thickcross_pixels(ui::Graphics* g, Editor* editor, const gfx::Point& pt, gfx::Color color, int thickness, Editor::PixelDelegate pixel); static void trace_brush_bounds(ui::Graphics* g, Editor* editor, const gfx::Point& pt, gfx::Color color, Editor::PixelDelegate pixel); static void savepixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); static void drawpixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); static void clearpixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); static color_t get_brush_color(Sprite* sprite, Layer* layer); ////////////////////////////////////////////////////////////////////// // CURSOR COLOR ////////////////////////////////////////////////////////////////////// static app::Color cursor_color; static gfx::Color ui_cursor_color; static bool is_cursor_mask; static void update_cursor_color() { ui_cursor_color = color_utils::color_for_ui(cursor_color); is_cursor_mask = (cursor_color.getType() == app::Color::MaskType); } app::Color Editor::get_cursor_color() { return cursor_color; } void Editor::set_cursor_color(const app::Color& color) { cursor_color = color; update_cursor_color(); } ////////////////////////////////////////////////////////////////////// // Slots for App signals ////////////////////////////////////////////////////////////////////// static gfx::Rect lastBrushBounds; static int brush_size_thick = 0; static void on_palette_change_update_cursor_color() { update_cursor_color(); } static void on_brush_before_change() { if (current_editor) { brush_size_thick = current_editor->cursorThick(); if (brush_size_thick) current_editor->hideDrawingCursor(); } } static void on_brush_after_change() { if (current_editor) { // Show drawing cursor if (current_editor->sprite() && brush_size_thick > 0) current_editor->showDrawingCursor(); } } static Brush* get_current_brush() { return App::instance()->getMainWindow()->getContextBar()->activeBrush().get(); } ////////////////////////////////////////////////////////////////////// // CURSOR ////////////////////////////////////////////////////////////////////// void Editor::editor_cursor_init() { // Cursor color set_cursor_color(get_config_color("Tools", "CursorColor", app::Color::fromMask())); App::instance()->PaletteChange.connect(&on_palette_change_update_cursor_color); App::instance()->BrushSizeBeforeChange.connect(&on_brush_before_change); App::instance()->BrushSizeAfterChange.connect(&on_brush_after_change); App::instance()->BrushAngleBeforeChange.connect(&on_brush_before_change); App::instance()->BrushAngleAfterChange.connect(&on_brush_after_change); } void Editor::editor_cursor_exit() { set_config_color("Tools", "CursorColor", cursor_color); if (cursor_bound.seg != NULL) base_free(cursor_bound.seg); } // Draws the brush cursor inside the specified editor. // Warning: You should clean the cursor before to use // this routine with other editor. // // Note: x and y params are absolute positions of the mouse. void Editor::drawBrushPreview(const gfx::Point& pos, bool refresh) { ASSERT(m_cursorThick == 0); ASSERT(m_sprite != NULL); // Get drawable region getDrawableRegion(clipping_region, kCutTopWindows); // Get cursor color cursor_negative = is_cursor_mask; // Cursor in the screen (view) m_cursorScreen = pos; // Get cursor position in the editor gfx::Point spritePos = screenToEditor(pos); // Get the current tool tools::Tool* tool = getCurrentEditorTool(); tools::Ink* ink = getCurrentEditorInk(); // Setup the cursor type debrushding of several factors (current tool, // foreground color, and layer transparency). color_t brush_color = get_brush_color(m_sprite, m_layer); color_t mask_color = m_sprite->transparentColor(); if (ink->isSelection() || ink->isSlice()) { cursor_type = CURSOR_THICKCROSS; } else if ( // Use cursor bounds for inks that are effects (eraser, blur, etc.) (ink->isEffect()) || // or when the brush color is transparent and we are not in the background layer (m_layer && !m_layer->isBackground() && brush_color == mask_color)) { cursor_type = CURSOR_BRUSHBOUNDS; } else { cursor_type = CURSOR_THINCROSS; } // For cursor type 'bounds' we have to generate cursor boundaries if (cursor_type & CURSOR_BRUSHBOUNDS) generate_cursor_boundaries(this); // Draw pixel/brush preview if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { IToolSettings* tool_settings = UIContext::instance() ->settings() ->getToolSettings(tool); Brush* brush = get_current_brush(); gfx::Rect brushBounds = brush->bounds(); brushBounds.offset(spritePos); // Create the extra cel to show the brush preview Site site = getSite(); Cel* cel = site.cel(); m_document->prepareExtraCel( brushBounds, cel ? cel->opacity(): 255); m_document->setExtraCelType(render::ExtraType::NONE); m_document->setExtraCelBlendMode( m_layer ? static_cast<LayerImage*>(m_layer)->getBlendMode(): BLEND_MODE_NORMAL); // In 'indexed' images, if the current color is 0, we have to use // a different mask color (different from 0) to draw the extra layer if (brush_color == mask_color) mask_color = (mask_color == 0 ? 1: 0); Image* extraImage = m_document->getExtraCelImage(); extraImage->setMaskColor(mask_color); clear_image(extraImage, mask_color); if (m_layer) { render::Render().renderLayer( extraImage, m_layer, m_frame, gfx::Clip(0, 0, brushBounds), BLEND_MODE_COPY); // This extra cel is a patch for the current layer/frame m_document->setExtraCelType(render::ExtraType::PATCH); } tools::ToolLoop* loop = create_tool_loop_preview( this, UIContext::instance(), extraImage, -gfx::Point(brushBounds.x, brushBounds.y)); if (loop) { loop->getInk()->prepareInk(loop); loop->getIntertwine()->prepareIntertwine(); loop->getController()->prepareController(); loop->getPointShape()->preparePointShape(loop); loop->getPointShape()->transformPoint( loop, -brush->bounds().x, -brush->bounds().y); delete loop; } if (refresh) { m_document->notifySpritePixelsModified (m_sprite, gfx::Region(lastBrushBounds = brushBounds)); } } // Save area and draw the cursor if (refresh) { ScreenGraphics g; SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, m_cursorScreen, spritePos, ui_cursor_color, savepixel); forEachBrushPixel(&g, m_cursorScreen, spritePos, ui_cursor_color, drawpixel); } // Cursor thickness m_cursorThick = 1; // Cursor in the editor (model) m_cursorEditor = spritePos; // Save the clipping-region to know where to clean the pixels old_clipping_region = clipping_region; } void Editor::moveBrushPreview(const gfx::Point& pos, bool refresh) { ASSERT(m_sprite != NULL); gfx::Point oldScreenPos = m_cursorScreen; gfx::Point oldEditorPos = m_cursorEditor; clearBrushPreview(false); drawBrushPreview(pos, false); gfx::Point newEditorPos = m_cursorEditor; if (refresh) { // Restore pixels { ScreenGraphics g; SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, oldScreenPos, oldEditorPos, gfx::ColorNone, clearpixel); } if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { Brush* brush = get_current_brush(); gfx::Rect newBrushBounds = brush->bounds(); newBrushBounds.offset(newEditorPos); m_document->notifySpritePixelsModified (m_sprite, gfx::Region(lastBrushBounds.createUnion(newBrushBounds))); lastBrushBounds = newBrushBounds; } // Save area and draw the cursor ScreenGraphics g; forEachBrushPixel(&g, m_cursorScreen, newEditorPos, ui_cursor_color, savepixel); forEachBrushPixel(&g, m_cursorScreen, newEditorPos, ui_cursor_color, drawpixel); } } /** * Cleans the brush cursor from the specified editor. * * The mouse position is got from the last * call to @c drawBrushPreview. So you must * to use this routine only if you called * @c drawBrushPreview before with the specified * editor @a widget. * * @param widget The editor widget * * @see drawBrushPreview */ void Editor::clearBrushPreview(bool refresh) { ASSERT(m_cursorThick != 0); ASSERT(m_sprite != NULL); getDrawableRegion(clipping_region, kCutTopWindows); gfx::Point pos = m_cursorEditor; if (refresh) { // Restore pixels ScreenGraphics g; SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); forEachBrushPixel(&g, m_cursorScreen, pos, gfx::ColorNone, clearpixel); } // Clean pixel/brush preview if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { m_document->destroyExtraCel(); if (refresh) { m_document->notifySpritePixelsModified (m_sprite, gfx::Region(lastBrushBounds)); } } m_cursorThick = 0; clipping_region.clear(); old_clipping_region.clear(); } // Returns true if the cursor to draw in the editor has subpixel // movement (a little pixel of the screen that indicates where is the // mouse inside the pixel of the sprite). bool Editor::doesBrushPreviewNeedSubpixel() { return IS_SUBPIXEL(this); } ////////////////////////////////////////////////////////////////////// static void generate_cursor_boundaries(Editor* editor) { tools::Tool* tool = editor->getCurrentEditorTool(); IBrushSettings* brush_settings = NULL; if (tool) brush_settings = UIContext::instance() ->settings() ->getToolSettings(tool) ->getBrush(); if (cursor_bound.seg == NULL || cursor_bound.brush_type != brush_settings->getType() || cursor_bound.brush_size != brush_settings->getSize() || cursor_bound.brush_angle != brush_settings->getAngle()) { cursor_bound.brush_type = brush_settings->getType(); cursor_bound.brush_size = brush_settings->getSize(); cursor_bound.brush_angle = brush_settings->getAngle(); if (cursor_bound.seg != NULL) base_free(cursor_bound.seg); Brush* brush; if (brush_settings) { brush = new Brush(brush_settings->getType(), brush_settings->getSize(), brush_settings->getAngle()); } else brush = new Brush(); cursor_bound.seg = find_mask_boundary(brush->image(), &cursor_bound.nseg, IgnoreBounds, 0, 0, 0, 0); delete brush; } } void Editor::forEachBrushPixel( ui::Graphics* g, const gfx::Point& screenPos, const gfx::Point& spritePos, gfx::Color color, Editor::PixelDelegate pixelDelegate) { saved_pixel_n = 0; if (cursor_type & CURSOR_THINCROSS) trace_thincross_pixels(g, this, screenPos, color, pixelDelegate); if (cursor_type & CURSOR_THICKCROSS) trace_thickcross_pixels(g, this, spritePos, color, 1, pixelDelegate); if (cursor_type & CURSOR_BRUSHBOUNDS) trace_brush_bounds(g, this, spritePos, color, pixelDelegate); if (IS_SUBPIXEL(this)) { pixelDelegate(g, screenPos, color); } } ////////////////////////////////////////////////////////////////////// // New Thin Cross static void trace_thincross_pixels(ui::Graphics* g, Editor* editor, const gfx::Point& pt, gfx::Color color, Editor::PixelDelegate pixelDelegate) { static int cursor_cross[7*7] = { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, }; gfx::Point out; int u, v; for (v=0; v<7; v++) { for (u=0; u<7; u++) { if (cursor_cross[v*7+u]) { out.x = pt.x-3+u; out.y = pt.y-3+v; pixelDelegate(g, out, color); } } } } ////////////////////////////////////////////////////////////////////// // Old Thick Cross static void trace_thickcross_pixels(ui::Graphics* g, Editor* editor, const gfx::Point& pt, gfx::Color color, int thickness, Editor::PixelDelegate pixelDelegate) { static int cursor_cross[6*6] = { 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, }; gfx::Point out, outpt = editor->editorToScreen(pt); int u, v; int size = editor->zoom().apply(thickness/2); int size2 = editor->zoom().apply(thickness); if (size2 == 0) size2 = 1; for (v=0; v<6; v++) { for (u=0; u<6; u++) { if (!cursor_cross[v*6+u]) continue; out = outpt; out.x += ((u<3) ? u-size-3: u-size-3+size2); out.y += ((v<3) ? v-size-3: v-size-3+size2); pixelDelegate(g, out, color); } } } ////////////////////////////////////////////////////////////////////// // Current Brush Bounds struct Data { ui::Graphics* g; gfx::Color color; Editor::PixelDelegate pixelDelegate; }; static void algo_line_proxy(int x, int y, void* _data) { Data* data = (Data*)_data; data->pixelDelegate(data->g, gfx::Point(x, y), data->color); } static void trace_brush_bounds(ui::Graphics* g, Editor* editor, const gfx::Point& pos, gfx::Color color, Editor::PixelDelegate pixelDelegate) { Data data = { g, color, pixelDelegate }; gfx::Point pt1, pt2; BoundSeg* seg; int c; for (c=0; c<cursor_bound.nseg; c++) { seg = cursor_bound.seg+c; pt1.x = pos.x + seg->x1 - cursor_bound.brush_size/2; pt1.y = pos.y + seg->y1 - cursor_bound.brush_size/2; pt2.x = pos.x + seg->x2 - cursor_bound.brush_size/2; pt2.y = pos.y + seg->y2 - cursor_bound.brush_size/2; pt1 = editor->editorToScreen(pt1); pt2 = editor->editorToScreen(pt2); if (seg->open) { // Outside if (pt1.x == pt2.x) { pt1.x--; pt2.x--; pt2.y--; } else { pt1.y--; pt2.y--; pt2.x--; } } else { if (pt1.x == pt2.x) { pt2.y--; } else { pt2.x--; } } doc::algo_line(pt1.x, pt1.y, pt2.x, pt2.y, (void*)&data, algo_line_proxy); } } ////////////////////////////////////////////////////////////////////// // Helpers static void savepixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) { if (saved_pixel_n < MAX_SAVED && clipping_region.contains(pt)) saved_pixel[saved_pixel_n++] = g->getPixel(pt.x, pt.y); } static void drawpixel(ui::Graphics* graphics, const gfx::Point& pt, gfx::Color color) { if (saved_pixel_n < MAX_SAVED && clipping_region.contains(pt)) { if (cursor_negative) { int c = saved_pixel[saved_pixel_n++]; int r = gfx::getr(c); int g = gfx::getg(c); int b = gfx::getb(c); graphics->putPixel(color_utils::blackandwhite_neg(gfx::rgba(r, g, b)), pt.x, pt.y); } else { graphics->putPixel(color, pt.x, pt.y); } } } static void clearpixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) { if (saved_pixel_n < MAX_SAVED) { if (clipping_region.contains(pt)) g->putPixel(saved_pixel[saved_pixel_n++], pt.x, pt.y); else if (!old_clipping_region.isEmpty() && old_clipping_region.contains(pt)) saved_pixel_n++; } } static color_t get_brush_color(Sprite* sprite, Layer* layer) { app::Color c = UIContext::instance()->settings()->getFgColor(); ASSERT(sprite != NULL); // Avoid using invalid colors if (!c.isValid()) return 0; if (layer != NULL) return color_utils::color_for_layer(c, layer); else return color_utils::color_for_image(c, sprite->pixelFormat()); } } // namespace app
namespace app { using namespace gfx; using namespace ui; using namespace app::skin; static struct { int width; int height; int scale; } try_resolutions[] = { { 1024, 768, 2 }, { 800, 600, 2 }, { 640, 480, 2 }, { 320, 240, 1 }, { 320, 200, 1 }, { 0, 0, 0 } }; ////////////////////////////////////////////////////////////////////// class CustomizedGuiManager : public Manager , public LayoutIO { protected: bool onProcessMessage(Message* msg) override; LayoutIO* onGetLayoutIO() override { return this; } void onNewDisplayConfiguration() override; // LayoutIO implementation std::string loadLayout(Widget* widget) override; void saveLayout(Widget* widget, const std::string& str) override; }; static she::Display* main_display = NULL; static she::Clipboard* main_clipboard = NULL; static CustomizedGuiManager* manager = NULL; static Theme* gui_theme = NULL; static ui::Timer* defered_invalid_timer = nullptr; static gfx::Region defered_invalid_region; // Load & save graphics configuration static void load_gui_config(int& w, int& h, bool& maximized, std::string& windowLayout); static void save_gui_config(); static int get_screen_scale() { int scale = Preferences::instance().general.screenScale(); scale = MID(1, scale, 4); return scale; } static bool create_main_display(bool gpuAccel, bool& maximized, std::string& lastError) { int w, h; int scale = get_screen_scale(); std::string windowLayout; load_gui_config(w, h, maximized, windowLayout); she::instance()->setGpuAcceleration(gpuAccel); try { if (w > 0 && h > 0) main_display = she::instance()->createDisplay(w, h, scale); } catch (const she::DisplayCreationException& e) { lastError = e.what(); } if (!main_display) { for (int c=0; try_resolutions[c].width; ++c) { try { main_display = she::instance()->createDisplay(try_resolutions[c].width, try_resolutions[c].height, try_resolutions[c].scale); scale = try_resolutions[c].scale; Preferences::instance().general.screenScale(scale); break; } catch (const she::DisplayCreationException& e) { lastError = e.what(); } } } if (main_display && !windowLayout.empty()) { main_display->setLayout(windowLayout); if (main_display->isMinimized()) main_display->maximize(); } return (main_display != nullptr); } // Initializes GUI. int init_module_gui() { bool maximized = false; std::string lastError = "Unknown error"; bool gpuAccel = Preferences::instance().general.gpuAcceleration(); if (!create_main_display(gpuAccel, maximized, lastError)) { // If we've created the display with hardware acceleration, // now we try to do it without hardware acceleration. if (gpuAccel && (int(she::instance()->capabilities()) & int(she::Capabilities::GpuAccelerationSwitch)) == int(she::Capabilities::GpuAccelerationSwitch)) { if (create_main_display(false, maximized, lastError)) { // Disable hardware acceleration Preferences::instance().general.gpuAcceleration(false); } } } if (!main_display) { she::error_message( ("Unable to create a user-interface display.\nDetails: "+lastError+"\n").c_str()); return -1; } main_clipboard = she::instance()->createClipboard(); // Create the default-manager manager = new CustomizedGuiManager(); manager->setDisplay(main_display); manager->setClipboard(main_clipboard); // Setup the GUI theme for all widgets gui_theme = new SkinTheme(); gui_theme->setScale(Preferences::instance().experimental.uiScale()); CurrentTheme::set(gui_theme); if (maximized) main_display->maximize(); // Set graphics options for next time save_gui_config(); return 0; } void exit_module_gui() { save_gui_config(); delete defered_invalid_timer; delete manager; // Now we can destroy theme CurrentTheme::set(NULL); delete gui_theme; main_clipboard->dispose(); main_display->dispose(); } static void load_gui_config(int& w, int& h, bool& maximized, std::string& windowLayout) { gfx::Size defSize = she::instance()->defaultNewDisplaySize(); w = get_config_int("GfxMode", "Width", defSize.w); h = get_config_int("GfxMode", "Height", defSize.h); maximized = get_config_bool("GfxMode", "Maximized", false); windowLayout = get_config_string("GfxMode", "WindowLayout", ""); } static void save_gui_config() { she::Display* display = manager->getDisplay(); if (display) { set_config_bool("GfxMode", "Maximized", display->isMaximized()); set_config_int("GfxMode", "Width", display->originalWidth()); set_config_int("GfxMode", "Height", display->originalHeight()); std::string windowLayout = display->getLayout(); if (!windowLayout.empty()) set_config_string("GfxMode", "WindowLayout", windowLayout.c_str()); } } void update_screen_for_document(const Document* document) { // Without document. if (!document) { // Well, change to the default palette. if (set_current_palette(NULL, false)) { // If the palette changes, refresh the whole screen. if (manager) manager->invalidate(); } } // With a document. else { const_cast<Document*>(document)->notifyGeneralUpdate(); // Update the tabs (maybe the modified status has been changed). app_rebuild_documents_tabs(); } } void load_window_pos(Widget* window, const char *section) { // Default position Rect orig_pos = window->bounds(); Rect pos = orig_pos; // Load configurated position pos = get_config_rect(section, "WindowPos", pos); pos.w = MID(orig_pos.w, pos.w, ui::display_w()); pos.h = MID(orig_pos.h, pos.h, ui::display_h()); pos.setOrigin(Point(MID(0, pos.x, ui::display_w()-pos.w), MID(0, pos.y, ui::display_h()-pos.h))); window->setBounds(pos); } void save_window_pos(Widget* window, const char *section) { set_config_rect(section, "WindowPos", window->bounds()); } Widget* setup_mini_font(Widget* widget) { SkinPropertyPtr skinProp = get_skin_property(widget); skinProp->setMiniFont(); return widget; } Widget* setup_mini_look(Widget* widget) { return setup_look(widget, MiniLook); } Widget* setup_look(Widget* widget, LookType lookType) { SkinPropertyPtr skinProp = get_skin_property(widget); skinProp->setLook(lookType); return widget; } void setup_bevels(Widget* widget, int b1, int b2, int b3, int b4) { SkinPropertyPtr skinProp = get_skin_property(widget); skinProp->setUpperLeft(b1); skinProp->setUpperRight(b2); skinProp->setLowerLeft(b3); skinProp->setLowerRight(b4); } ////////////////////////////////////////////////////////////////////// // Button style (convert radio or check buttons and draw it like // normal buttons) CheckBox* check_button_new(const char *text, int b1, int b2, int b3, int b4) { CheckBox* widget = new CheckBox(text, kButtonWidget); widget->setAlign(CENTER | MIDDLE); setup_mini_look(widget); setup_bevels(widget, b1, b2, b3, b4); return widget; } void defer_invalid_rect(const gfx::Rect& rc) { if (!defered_invalid_timer) defered_invalid_timer = new ui::Timer(250, manager); defered_invalid_timer->stop(); defered_invalid_timer->start(); defered_invalid_region.createUnion(defered_invalid_region, gfx::Region(rc)); } // Manager event handler. bool CustomizedGuiManager::onProcessMessage(Message* msg) { switch (msg->type()) { case kCloseDisplayMessage: { // Execute the "Exit" command. Command* command = CommandsModule::instance()->getCommandByName(CommandId::Exit); UIContext::instance()->executeCommand(command); } break; case kDropFilesMessage: { // If the main window is not the current foreground one. We // discard the drop-files event. if (getForegroundWindow() != App::instance()->getMainWindow()) break; const DropFilesMessage::Files& files = static_cast<DropFilesMessage*>(msg)->files(); // Open all files Command* cmd_open_file = CommandsModule::instance()->getCommandByName(CommandId::OpenFile); Params params; UIContext* ctx = UIContext::instance(); for (const auto& fn : files) { // If the document is already open, select it. Document* doc = static_cast<Document*>(ctx->documents().getByFileName(fn)); if (doc) { DocumentView* docView = ctx->getFirstDocumentView(doc); if (docView) ctx->setActiveView(docView); else { ASSERT(false); // Must be some DocumentView available } } // Load the file else { params.set("filename", fn.c_str()); ctx->executeCommand(cmd_open_file, params); } } } break; case kKeyDownMessage: { #ifdef _DEBUG // Left Shift+Ctrl+Q generates a crash (useful to test the anticrash feature) if (msg->ctrlPressed() && msg->shiftPressed() && static_cast<KeyMessage*>(msg)->scancode() == kKeyQ) { int* p = nullptr; *p = 0; } #endif // Call base impl to check if there is a foreground window as // top level that needs keys. (In this way we just do not // process keyboard shortcuts for menus and tools). if (Manager::onProcessMessage(msg)) return true; for (const Key* key : *KeyboardShortcuts::instance()) { if (key->isPressed(msg)) { // Cancel menu-bar loops (to close any popup menu) App::instance()->getMainWindow()->getMenuBar()->cancelMenuLoop(); switch (key->type()) { case KeyType::Tool: { tools::Tool* current_tool = App::instance()->activeTool(); tools::Tool* select_this_tool = key->tool(); tools::ToolBox* toolbox = App::instance()->getToolBox(); std::vector<tools::Tool*> possibles; // Collect all tools with the pressed keyboard-shortcut for (tools::Tool* tool : *toolbox) { Key* key = KeyboardShortcuts::instance()->tool(tool); if (key && key->isPressed(msg)) possibles.push_back(tool); } if (possibles.size() >= 2) { bool done = false; for (size_t i=0; i<possibles.size(); ++i) { if (possibles[i] != current_tool && ToolBar::instance()->isToolVisible(possibles[i])) { select_this_tool = possibles[i]; done = true; break; } } if (!done) { for (size_t i=0; i<possibles.size(); ++i) { // If one of the possibilities is the current tool if (possibles[i] == current_tool) { // We select the next tool in the possibilities select_this_tool = possibles[(i+1) % possibles.size()]; break; } } } } ToolBar::instance()->selectTool(select_this_tool); return true; } case KeyType::Command: { Command* command = key->command(); // Commands are executed only when the main window is // the current window running at foreground. for (auto childWidget : children()) { Window* child = static_cast<Window*>(childWidget); // There are a foreground window executing? if (child->isForeground()) { break; } // Is it the desktop and the top-window= else if (child->isDesktop() && child == App::instance()->getMainWindow()) { // OK, so we can execute the command represented // by the pressed-key in the message... UIContext::instance()->executeCommand( command, key->params()); return true; } } break; } case KeyType::Quicktool: { // Do nothing, it is used in the editor through the // KeyboardShortcuts::getCurrentQuicktool() function. break; } } break; } } break; } case kTimerMessage: if (static_cast<TimerMessage*>(msg)->timer() == defered_invalid_timer) { invalidateDisplayRegion(defered_invalid_region); defered_invalid_region.clear(); defered_invalid_timer->stop(); } break; } return Manager::onProcessMessage(msg); } void CustomizedGuiManager::onNewDisplayConfiguration() { Manager::onNewDisplayConfiguration(); save_gui_config(); } std::string CustomizedGuiManager::loadLayout(Widget* widget) { if (widget->window() == nullptr) return ""; std::string windowId = widget->window()->id(); std::string widgetId = widget->id(); return get_config_string(("layout:"+windowId).c_str(), widgetId.c_str(), ""); } void CustomizedGuiManager::saveLayout(Widget* widget, const std::string& str) { if (widget->window() == NULL) return; std::string windowId = widget->window()->id(); std::string widgetId = widget->id(); set_config_string(("layout:"+windowId).c_str(), widgetId.c_str(), str.c_str()); } } // namespace app
// Manager event handler. bool CustomizedGuiManager::onProcessMessage(Message* msg) { switch (msg->type()) { case kCloseDisplayMessage: { // Execute the "Exit" command. Command* command = CommandsModule::instance()->getCommandByName(CommandId::Exit); UIContext::instance()->executeCommand(command); } break; case kDropFilesMessage: { // If the main window is not the current foreground one. We // discard the drop-files event. if (getForegroundWindow() != App::instance()->getMainWindow()) break; const DropFilesMessage::Files& files = static_cast<DropFilesMessage*>(msg)->files(); // Open all files Command* cmd_open_file = CommandsModule::instance()->getCommandByName(CommandId::OpenFile); Params params; UIContext* ctx = UIContext::instance(); for (const auto& fn : files) { // If the document is already open, select it. Document* doc = static_cast<Document*>(ctx->documents().getByFileName(fn)); if (doc) { DocumentView* docView = ctx->getFirstDocumentView(doc); if (docView) ctx->setActiveView(docView); else { ASSERT(false); // Must be some DocumentView available } } // Load the file else { params.set("filename", fn.c_str()); ctx->executeCommand(cmd_open_file, params); } } } break; case kKeyDownMessage: { #ifdef _DEBUG // Left Shift+Ctrl+Q generates a crash (useful to test the anticrash feature) if (msg->ctrlPressed() && msg->shiftPressed() && static_cast<KeyMessage*>(msg)->scancode() == kKeyQ) { int* p = nullptr; *p = 0; } #endif // Call base impl to check if there is a foreground window as // top level that needs keys. (In this way we just do not // process keyboard shortcuts for menus and tools). if (Manager::onProcessMessage(msg)) return true; for (const Key* key : *KeyboardShortcuts::instance()) { if (key->isPressed(msg)) { // Cancel menu-bar loops (to close any popup menu) App::instance()->getMainWindow()->getMenuBar()->cancelMenuLoop(); switch (key->type()) { case KeyType::Tool: { tools::Tool* current_tool = App::instance()->activeTool(); tools::Tool* select_this_tool = key->tool(); tools::ToolBox* toolbox = App::instance()->getToolBox(); std::vector<tools::Tool*> possibles; // Collect all tools with the pressed keyboard-shortcut for (tools::Tool* tool : *toolbox) { Key* key = KeyboardShortcuts::instance()->tool(tool); if (key && key->isPressed(msg)) possibles.push_back(tool); } if (possibles.size() >= 2) { bool done = false; for (size_t i=0; i<possibles.size(); ++i) { if (possibles[i] != current_tool && ToolBar::instance()->isToolVisible(possibles[i])) { select_this_tool = possibles[i]; done = true; break; } } if (!done) { for (size_t i=0; i<possibles.size(); ++i) { // If one of the possibilities is the current tool if (possibles[i] == current_tool) { // We select the next tool in the possibilities select_this_tool = possibles[(i+1) % possibles.size()]; break; } } } } ToolBar::instance()->selectTool(select_this_tool); return true; } case KeyType::Command: { Command* command = key->command(); // Commands are executed only when the main window is // the current window running at foreground. for (auto childWidget : children()) { Window* child = static_cast<Window*>(childWidget); // There are a foreground window executing? if (child->isForeground()) { break; } // Is it the desktop and the top-window= else if (child->isDesktop() && child == App::instance()->getMainWindow()) { // OK, so we can execute the command represented // by the pressed-key in the message... UIContext::instance()->executeCommand( command, key->params()); return true; } } break; } case KeyType::Quicktool: { // Do nothing, it is used in the editor through the // KeyboardShortcuts::getCurrentQuicktool() function. break; } } break; } } break; } case kTimerMessage: if (static_cast<TimerMessage*>(msg)->timer() == defered_invalid_timer) { invalidateDisplayRegion(defered_invalid_region); defered_invalid_region.clear(); defered_invalid_timer->stop(); } break; } return Manager::onProcessMessage(msg); }