/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2010 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 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 PCSX2. * If not, see . */ #pragma once #include #include #include #include "common/Dependencies.h" // ---------------------------------------------------------------------------- // wxGuiTools.h // // This file is meant to contain utility classes for users of the wxWidgets library. // All classes in this file are dependent on wxBase and wxCore libraries! Meaning // you will have to use wxCore header files and link against wxCore (GUI) to build // them. For tools which require only wxBase, see wxBaseTools.h // ---------------------------------------------------------------------------- class pxStaticText; class pxStaticHeading; class pxCheckBox; class wxSpinCtrl; namespace pxSizerFlags { static int StdPadding = 4; extern wxSizerFlags StdSpace(); extern wxSizerFlags StdCenter(); extern wxSizerFlags StdExpand(); extern wxSizerFlags TopLevelBox(); extern wxSizerFlags SubGroup(); extern wxSizerFlags StdButton(); extern wxSizerFlags Checkbox(); extern void SetBestPadding(); }; // namespace pxSizerFlags #define wxSF wxSizerFlags() // -------------------------------------------------------------------------------------- // pxAlignment / pxStretchType // -------------------------------------------------------------------------------------- // These are full blown class types instead of enumerations because wxSizerFlags has an // implicit conversion from integer (silly design flaw creating more work for me!) // struct pxAlignmentType { enum { Centre, Center = Centre, Middle, Left, Right, Top, Bottom }; int intval; wxSizerFlags Apply(wxSizerFlags flags = wxSizerFlags()) const; wxSizerFlags operator&(const wxSizerFlags& _flgs) const { return Apply(_flgs); } wxSizerFlags Expand() const { return Apply().Expand(); } wxSizerFlags Border(int dir, int padding) const { return Apply().Border(dir, padding); } wxSizerFlags Proportion() const { return Apply().Proportion(intval); } operator wxSizerFlags() const { return Apply(); } }; struct pxStretchType { enum { Shrink, Expand, Shaped, ReserveHidden, FixedMinimum }; int intval; wxSizerFlags Apply(wxSizerFlags flags = wxSizerFlags()) const; wxSizerFlags operator&(const wxSizerFlags& _flgs) const { return Apply(_flgs); } wxSizerFlags Border(int dir, int padding) const { return Apply().Border(dir, padding); } wxSizerFlags Proportion() const { return Apply().Proportion(intval); } operator wxSizerFlags() const { return Apply(); } }; static __fi wxSizerFlags pxProportion(int prop) { return wxSizerFlags(prop); } static __fi wxSizerFlags pxBorder(int dir = wxALL, int pad = pxSizerFlags::StdPadding) { return wxSizerFlags().Border(dir, pad); } class pxStretchSpacer { public: int proportion; pxStretchSpacer(int prop = 0) { proportion = prop; } }; extern const pxAlignmentType pxCentre, // Horizontal centered alignment pxCenter, pxMiddle, // vertical centered alignment pxAlignLeft, pxAlignRight, pxAlignTop, pxAlignBottom; extern const pxStretchType pxShrink, pxExpand, pxShaped, pxReserveHidden, pxFixedMinimum; // -------------------------------------------------------------------------------------- // pxWindowAndFlags // -------------------------------------------------------------------------------------- // This struct is a go-between for combining windows and sizer flags in "neat" fashion. // To create the struct, use the | operator, like so: // // myPanel | wxSizerFlags().Expand() // // Implementation Note: This struct is a template as it allows us to use a special // version of the += operator that retains the type information of the window, in case // the window implements its own += overloads (one example is pxStaticText). Without the // template, the type of the window would only be known as "wxWindow" when it's added to the // sizer, and would thus fail to invoke the correct operator overload. // template struct pxWindowAndFlags { WinType* window; wxSizerFlags flags; }; extern wxSizerFlags operator&(const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2); template ::value>::type> pxWindowAndFlags operator|(WinType* _win, const wxSizerFlags& _flgs) { pxWindowAndFlags result = {_win, _flgs}; return result; } template ::value>::type> pxWindowAndFlags operator|(WinType& _win, const wxSizerFlags& _flgs) { pxWindowAndFlags result = {&_win, _flgs}; return result; } // -------------------------------------------------------------------------------------- // wxSizer Operator += .. a wxSizer.Add() Substitute // -------------------------------------------------------------------------------------- // This set of operators is the *recommended* method for adding windows to sizers, not just // because it's a lot prettier but also because it allows controls like pxStaticText to over- // ride default sizerflags behavior. // // += operator works on either sizers, wxDialogs or wxPanels. In the latter case, the window // is added to the dialog/panel's toplevel sizer (wxPanel.GetSizer() is used). If the panel // has no sizer set via SetSizer(), an assertion is generated. // extern void operator+=(wxSizer& target, wxWindow* src); extern void operator+=(wxSizer& target, wxSizer* src); extern void operator+=(wxSizer& target, wxWindow& src); extern void operator+=(wxSizer& target, wxSizer& src); extern void operator+=(wxSizer* target, wxWindow& src); extern void operator+=(wxSizer* target, wxSizer& src); extern void operator+=(wxSizer& target, int spacer); extern void operator+=(wxWindow& target, int spacer); extern void operator+=(wxSizer& target, const pxStretchSpacer& spacer); extern void operator+=(wxWindow& target, const pxStretchSpacer& spacer); // ---------------------------------------------------------------------------- // Important: This template is needed in order to retain window type information and // invoke the proper overloaded version of += (which is used by pxStaticText and other // classes to perform special actions when added to sizers). template void operator+=(wxWindow& target, WinType* src) { if (!pxAssert(target.GetSizer() != NULL)) return; *target.GetSizer() += src; } template void operator+=(wxWindow& target, WinType& src) { if (!pxAssert(target.GetSizer() != NULL)) return; *target.GetSizer() += src; } template void operator+=(wxWindow& target, const pxWindowAndFlags& src) { if (!pxAssert(target.GetSizer() != NULL)) return; *target.GetSizer() += src; } template void operator+=(wxSizer& target, const pxWindowAndFlags& src) { target.Add(src.window, src.flags); } wxDECLARE_EVENT(pxEvt_OnDialogCreated, wxCommandEvent); // -------------------------------------------------------------------------------------- // pxDialogCreationFlags // -------------------------------------------------------------------------------------- class pxDialogCreationFlags { public: wxSize MinimumSize; wxOrientation BoxSizerOrient; bool isResizable; bool hasContextHelp; bool hasCaption; bool hasMinimizeBox; bool hasMaximizeBox; bool hasSystemMenu; bool hasCloseBox; public: virtual ~pxDialogCreationFlags() = default; pxDialogCreationFlags() { MinimumSize = wxDefaultSize; BoxSizerOrient = wxVERTICAL; isResizable = false; hasContextHelp = false; hasCloseBox = true; hasSystemMenu = true; hasMinimizeBox = false; hasMaximizeBox = false; hasCaption = true; } pxDialogCreationFlags& SetSizerOrient(wxOrientation orient) { BoxSizerOrient = orient; return *this; } pxDialogCreationFlags& SetResize(bool enable = true) { isResizable = enable; return *this; } pxDialogCreationFlags& SetMinimize(bool enable = true) { hasMinimizeBox = enable; return *this; } pxDialogCreationFlags& SetMaximize(bool enable = true) { hasMaximizeBox = enable; return *this; } // NOTE: Enabling system menu on dialogs usually doesn't work, and might cause // other unwanted behavior, such as a missing close button. pxDialogCreationFlags& SetSystemMenu(bool enable = true) { hasSystemMenu = enable; return *this; } pxDialogCreationFlags& SetCaption(bool enable = true) { hasCaption = enable; return *this; } pxDialogCreationFlags& SetCloseBox(bool enable = true) { hasCloseBox = enable; return *this; } pxDialogCreationFlags SetContextHelp(bool enabled = true) { hasContextHelp = enabled; return *this; } pxDialogCreationFlags& SetMinWidth(int width) { if (width > MinimumSize.x) MinimumSize.SetWidth(width); return *this; } pxDialogCreationFlags& SetMinHeight(int height) { if (height > MinimumSize.y) MinimumSize.SetHeight(height); return *this; } pxDialogCreationFlags& SetMinSize(const wxSize& size) { return SetMinWidth(size.x).SetMinHeight(size.y); } pxDialogCreationFlags Horizontal() const { return pxDialogCreationFlags(*this).SetSizerOrient(wxHORIZONTAL); } pxDialogCreationFlags Vertical() const { return pxDialogCreationFlags(*this).SetSizerOrient(wxVERTICAL); } pxDialogCreationFlags NoSizer() const { return pxDialogCreationFlags(*this).SetSizerOrient((wxOrientation)0); } pxDialogCreationFlags Resize(bool enable = true) const { return pxDialogCreationFlags(*this).SetResize(enable); } pxDialogCreationFlags Minimize(bool enable = true) const { return pxDialogCreationFlags(*this).SetMinimize(enable); } pxDialogCreationFlags Maximize(bool enable = true) const { return pxDialogCreationFlags(*this).SetMaximize(enable); } // NOTE: Enabling system menu on dialogs usually doesn't work, and might cause // other unwanted behavior, such as a missing close button. pxDialogCreationFlags SystemMenu(bool enable = true) const { return pxDialogCreationFlags(*this).SetSystemMenu(false); } pxDialogCreationFlags Caption(bool enable = true) const { return pxDialogCreationFlags(*this).SetCaption(enable); } pxDialogCreationFlags CloseBox(bool enable = true) const { return pxDialogCreationFlags(*this).SetCloseBox(enable); } pxDialogCreationFlags NoResize() const { return pxDialogCreationFlags(*this).SetResize(false); } pxDialogCreationFlags NoMinimize() const { return pxDialogCreationFlags(*this).SetMinimize(false); } pxDialogCreationFlags NoMaximize() const { return pxDialogCreationFlags(*this).SetMaximize(false); } pxDialogCreationFlags NoSystemMenu() const { return pxDialogCreationFlags(*this).SetSystemMenu(false); } pxDialogCreationFlags NoCaption() const { return pxDialogCreationFlags(*this).SetCaption(false); } pxDialogCreationFlags NoCloseBox() const { return pxDialogCreationFlags(*this).SetCloseBox(false); } pxDialogCreationFlags MinWidth(int width) const { return pxDialogCreationFlags(*this).SetMinWidth(width); } pxDialogCreationFlags MinHeight(int height) const { return pxDialogCreationFlags(*this).SetMinHeight(height); } pxDialogCreationFlags MinSize(const wxSize& size) const { return pxDialogCreationFlags(*this).SetMinSize(size); } pxDialogCreationFlags MinSize(int width, int height) const { return pxDialogCreationFlags(*this).SetMinWidth(width).SetMinHeight(height); } int GetWxWindowFlags() const { int retval = 0; if (isResizable) retval |= wxRESIZE_BORDER; if (hasCaption) retval |= wxCAPTION; if (hasMaximizeBox) retval |= wxMAXIMIZE_BOX; if (hasMinimizeBox) retval |= wxMINIMIZE_BOX; if (hasSystemMenu) retval |= wxSYSTEM_MENU; if (hasCloseBox) retval |= wxCLOSE_BOX; return retval; } }; // -------------------------------------------------------------------------------------- // wxDialogWithHelpers // -------------------------------------------------------------------------------------- class wxDialogWithHelpers : public wxDialog { typedef wxDialog _parent; wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDialogWithHelpers); protected: bool m_hasContextHelp; wxBoxSizer* m_extraButtonSizer; wxRect m_CreatedRect; public: wxDialogWithHelpers(); wxDialogWithHelpers(wxWindow* parent, const wxString& title, const pxDialogCreationFlags& cflags = pxDialogCreationFlags()); virtual ~wxDialogWithHelpers() = default; void Init(const pxDialogCreationFlags& cflags); void AddOkCancel(wxSizer& sizer, bool hasApply = false); void AddOkCancel(wxSizer* sizer = NULL, bool hasApply = false); void RememberPosition(); virtual void SmartCenterFit(); virtual int ShowModal(); virtual bool Show(bool show = true); // Must return the same thing as GetNameStatic; a name ideal for use in uniquely // identifying dialogs. (this version is the 'instance' version, which is called // by BaseConfigurationDialog to assign the wxWidgets dialog name, and for saving // screenshots to disk) virtual wxString GetDialogName() const; virtual wxStaticText& Label(const wxString& label); virtual pxStaticText& Text(const wxString& label); virtual pxStaticText& Heading(const wxString& label); wxDialogWithHelpers& SetMinWidth(int newWidth); wxDialogWithHelpers& SetMinHeight(int newHeight); int GetCharHeight() const; protected: void OnDialogCreated(wxCommandEvent& evt); void OnOkCancel(wxCommandEvent& evt); bool ShouldPreventAppExit() const { return false; } void DoAutoCenter(); }; // -------------------------------------------------------------------------------------- // wxPanelWithHelpers // -------------------------------------------------------------------------------------- // Overview of Helpers provided by this class: // * Simpler constructors that have wxID, position, and size parameters removed (We never // use them in pcsx2) // // * Automatic 'primary box sizer' creation, assigned via SetSizer() -- use GetSizer() // to retrieve it, or use the "*this += window;" syntax to add windows directly to it. // // * Built-in support for StaticBoxes (aka groupboxes). Create one at construction with // a wxString label, or add one "after the fact" using AddFrame. // // * Propagates IdealWidth settings from parenting wxPanelWithHelpers classes, and auto- // matically adjusts the width based on the sizer type (groupsizers get truncated to // account for borders). // class wxPanelWithHelpers : public wxPanel { wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxPanelWithHelpers); public: wxPanelWithHelpers(wxWindow* parent, wxOrientation orient, const wxString& staticBoxLabel); wxPanelWithHelpers(wxWindow* parent, wxOrientation orient); wxPanelWithHelpers(wxWindow* parent, const wxPoint& pos, const wxSize& size = wxDefaultSize); explicit wxPanelWithHelpers(wxWindow* parent = NULL); wxPanelWithHelpers* AddFrame(const wxString& label, wxOrientation orient = wxVERTICAL); wxStaticText& Label(const wxString& label); pxStaticText& Text(const wxString& label); pxStaticText& Heading(const wxString& label); virtual wxPanelWithHelpers& SetMinWidth(int newWidth); protected: void Init(); }; // -------------------------------------------------------------------------------------- // pxTextWrapperBase // -------------------------------------------------------------------------------------- // this class is used to wrap the text on word boundary: wrapping is done by calling // OnStartLine() and OnOutputLine() functions. This class by itself can be used as a // line counting tool, but produces no formatted text output. // // [class "borrowed" from wxWidgets private code, made public, and renamed to avoid possible // conflicts with future editions of wxWidgets which might make it public. Why this isn't // publicly available already in wxBase I'll never know-- air] // class pxTextWrapperBase { protected: bool m_eol; int m_linecount; wxString m_indent; public: virtual ~pxTextWrapperBase() = default; pxTextWrapperBase(const wxString& indent = wxEmptyString) : m_indent(indent) { m_eol = false; m_linecount = 0; } // win is used for getting the font, text is the text to wrap, width is the // max line width or -1 to disable wrapping pxTextWrapperBase& Wrap(const wxWindow& win, const wxString& text, int widthMax); int GetLineCount() const { return m_linecount; } protected: // line may be empty virtual void OnOutputLine(const wxString& line) {} // called at the start of every new line (except the very first one) virtual void OnNewLine() {} void DoOutputLine(const wxString& line); bool IsStartOfNewLine(); }; // -------------------------------------------------------------------------------------- // pxTextWrapper // -------------------------------------------------------------------------------------- // This class extends pxTextWrapperBase and adds the ability to retrieve the formatted // result of word wrapping. // class pxTextWrapper : public pxTextWrapperBase { typedef pxTextWrapperBase _parent; protected: wxString m_text; public: pxTextWrapper(const wxString& wrapPrefix = wxEmptyString) : pxTextWrapperBase(wrapPrefix) { } virtual ~pxTextWrapper() = default; const wxString& GetResult() const { return m_text; } pxTextWrapper& Wrap(const wxWindow& win, const wxString& text, int widthMax); pxTextWrapper& Wrap(const wxWindow* win, const wxString& text, int widthMax); protected: void OnOutputLine(const wxString& line); void OnNewLine(); }; // -------------------------------------------------------------------------------------- // pxWindowTextWriter // -------------------------------------------------------------------------------------- class pxWindowTextWriter { protected: wxDC& m_dc; wxAlignment m_align; wxPoint m_curpos; int m_leading; virtual void _DoWriteLn(const wxString& msg); void _DoWrite(const wxChar* msg); public: pxWindowTextWriter(wxDC& dc); virtual ~pxWindowTextWriter() = default; virtual void OnFontChanged(); pxWindowTextWriter& WriteLn(); pxWindowTextWriter& FormatLn(const wxChar* fmt, ...); pxWindowTextWriter& WriteLn(const wxChar* fmt); pxWindowTextWriter& SetFont(const wxFont& font); pxWindowTextWriter& Align(const wxAlignment& align); pxWindowTextWriter& WriteLn(const wxString fmt); pxWindowTextWriter& SetLeading(int lead) { m_leading = lead; return *this; } pxWindowTextWriter& SetWeight(wxFontWeight weight); pxWindowTextWriter& SetStyle(wxFontStyle style); pxWindowTextWriter& Normal(); pxWindowTextWriter& Bold() { return SetWeight(wxFONTWEIGHT_BOLD); } pxWindowTextWriter& Italic() { return SetStyle(wxFONTSTYLE_ITALIC); } pxWindowTextWriter& SetPos(const wxPoint& pos); pxWindowTextWriter& MovePos(const wxSize& delta); pxWindowTextWriter& SetPos(int xpos, int ypos) { return SetPos(wxPoint(xpos, ypos)); } pxWindowTextWriter& MovePos(int xdelta, int ydelta) { return MovePos(wxSize(xdelta, ydelta)); } pxWindowTextWriter& SetY(int ypos); pxWindowTextWriter& MoveY(int ydelta); }; // -------------------------------------------------------------------------------------- // MoreStockCursors // -------------------------------------------------------------------------------------- // Because (inexplicably) the ArrowWait cursor isn't in wxWidgets stock list. // class MoreStockCursors { protected: std::unique_ptr m_arrowWait; public: MoreStockCursors() {} virtual ~MoreStockCursors() = default; const wxCursor& GetArrowWait(); }; enum BusyCursorType { Cursor_NotBusy, Cursor_KindaBusy, Cursor_ReallyBusy, }; extern MoreStockCursors StockCursors; // -------------------------------------------------------------------------------------- // ScopedBusyCursor // -------------------------------------------------------------------------------------- // ... because wxWidgets wxBusyCursor doesn't really do proper nesting (doesn't let me // override a partially-busy cursor with a really busy one) class ScopedBusyCursor { protected: static std::stack m_cursorStack; static BusyCursorType m_defBusyType; public: ScopedBusyCursor(BusyCursorType busytype); virtual ~ScopedBusyCursor(); static void SetDefault(BusyCursorType busytype); static void SetManualBusyCursor(BusyCursorType busytype); }; // -------------------------------------------------------------------------------------- // pxFitToDigits // -------------------------------------------------------------------------------------- // Fits a given text or spinner control to the number of digits requested, since by default // they're usually way over-sized. extern void pxFitToDigits(wxWindow* win, int digits); extern void pxFitToDigits(wxSpinCtrl* win, int digits); extern wxTextCtrl* CreateNumericalTextCtrl(wxWindow* parent, int digits, long flags = wxTE_RIGHT); ////////////////////////////////////////////////////////////////////////////////////////////// extern bool pxDialogExists(const wxString& name); extern bool pxIsValidWindowPosition(const wxWindow& window, const wxPoint& windowPos); extern wxRect wxGetDisplayArea(); extern wxString pxGetAppName(); extern int pxGetCharHeight(const wxWindow* wind, int rows = 1); extern int pxGetCharHeight(const wxWindow& wind, int rows = 1); extern void pxSetToolTip(wxWindow* wind, const wxString& src); extern void pxSetToolTip(wxWindow& wind, const wxString& src); extern wxFont pxGetFixedFont(int ptsize = 8, wxFontWeight weight = wxFONTWEIGHT_NORMAL, bool underline = false); extern pxDialogCreationFlags pxDialogFlags();