1 #include "wxExtensions.hpp"
2 
3 #include <stdexcept>
4 #include <cmath>
5 
6 #include <wx/sizer.h>
7 
8 #include <boost/algorithm/string/replace.hpp>
9 
10 #include "BitmapCache.hpp"
11 #include "GUI.hpp"
12 #include "GUI_App.hpp"
13 #include "GUI_ObjectList.hpp"
14 #include "I18N.hpp"
15 #include "GUI_Utils.hpp"
16 #include "Plater.hpp"
17 #include "../Utils/MacDarkMode.hpp"
18 
19 #ifndef __linux__
20 // msw_menuitem_bitmaps is used for MSW and OSX
21 static std::map<int, std::string> msw_menuitem_bitmaps;
22 #ifdef __WXMSW__
msw_rescale_menu(wxMenu * menu)23 void msw_rescale_menu(wxMenu* menu)
24 {
25 	struct update_icons {
26 		static void run(wxMenuItem* item) {
27 			const auto it = msw_menuitem_bitmaps.find(item->GetId());
28 			if (it != msw_menuitem_bitmaps.end()) {
29 				const wxBitmap& item_icon = create_scaled_bitmap(it->second);
30 				if (item_icon.IsOk())
31 					item->SetBitmap(item_icon);
32 			}
33 			if (item->IsSubMenu())
34 				for (wxMenuItem *sub_item : item->GetSubMenu()->GetMenuItems())
35 					update_icons::run(sub_item);
36 		}
37 	};
38 
39 	for (wxMenuItem *item : menu->GetMenuItems())
40 		update_icons::run(item);
41 }
42 #endif /* __WXMSW__ */
43 #endif /* no __WXGTK__ */
44 
enable_menu_item(wxUpdateUIEvent & evt,std::function<bool ()> const cb_condition,wxMenuItem * item,wxWindow * win)45 void enable_menu_item(wxUpdateUIEvent& evt, std::function<bool()> const cb_condition, wxMenuItem* item, wxWindow* win)
46 {
47     const bool enable = cb_condition();
48     evt.Enable(enable);
49 
50 #ifdef __WXOSX__
51     const auto it = msw_menuitem_bitmaps.find(item->GetId());
52     if (it != msw_menuitem_bitmaps.end())
53     {
54         const wxBitmap& item_icon = create_scaled_bitmap(it->second, win, 16, !enable);
55         if (item_icon.IsOk())
56             item->SetBitmap(item_icon);
57     }
58 #endif // __WXOSX__
59 }
60 
append_menu_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,const wxBitmap & icon,wxEvtHandler * event_handler,std::function<bool ()> const cb_condition,wxWindow * parent,int insert_pos)61 wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
62     std::function<void(wxCommandEvent& event)> cb, const wxBitmap& icon, wxEvtHandler* event_handler,
63     std::function<bool()> const cb_condition, wxWindow* parent, int insert_pos/* = wxNOT_FOUND*/)
64 {
65     if (id == wxID_ANY)
66         id = wxNewId();
67 
68     auto *item = new wxMenuItem(menu, id, string, description);
69     if (icon.IsOk()) {
70         item->SetBitmap(icon);
71     }
72     if (insert_pos == wxNOT_FOUND)
73         menu->Append(item);
74     else
75         menu->Insert(insert_pos, item);
76 
77 #ifdef __WXMSW__
78     if (event_handler != nullptr && event_handler != menu)
79         event_handler->Bind(wxEVT_MENU, cb, id);
80     else
81 #endif // __WXMSW__
82         menu->Bind(wxEVT_MENU, cb, id);
83 
84     if (parent) {
85         parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item, parent](wxUpdateUIEvent& evt) {
86             enable_menu_item(evt, cb_condition, item, parent); }, id);
87     }
88 
89     return item;
90 }
91 
append_menu_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,const std::string & icon,wxEvtHandler * event_handler,std::function<bool ()> const cb_condition,wxWindow * parent,int insert_pos)92 wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
93     std::function<void(wxCommandEvent& event)> cb, const std::string& icon, wxEvtHandler* event_handler,
94     std::function<bool()> const cb_condition, wxWindow* parent, int insert_pos/* = wxNOT_FOUND*/)
95 {
96     if (id == wxID_ANY)
97         id = wxNewId();
98 
99     const wxBitmap& bmp = !icon.empty() ? create_scaled_bitmap(icon) : wxNullBitmap;   // FIXME: pass window ptr
100 //#ifdef __WXMSW__
101 #ifndef __WXGTK__
102     if (bmp.IsOk())
103         msw_menuitem_bitmaps[id] = icon;
104 #endif /* __WXMSW__ */
105 
106     return append_menu_item(menu, id, string, description, cb, bmp, event_handler, cb_condition, parent, insert_pos);
107 }
108 
append_submenu(wxMenu * menu,wxMenu * sub_menu,int id,const wxString & string,const wxString & description,const std::string & icon,std::function<bool ()> const cb_condition,wxWindow * parent)109 wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, const std::string& icon,
110     std::function<bool()> const cb_condition, wxWindow* parent)
111 {
112     if (id == wxID_ANY)
113         id = wxNewId();
114 
115     wxMenuItem* item = new wxMenuItem(menu, id, string, description);
116     if (!icon.empty()) {
117         item->SetBitmap(create_scaled_bitmap(icon));    // FIXME: pass window ptr
118 //#ifdef __WXMSW__
119 #ifndef __WXGTK__
120         msw_menuitem_bitmaps[id] = icon;
121 #endif /* __WXMSW__ */
122     }
123 
124     item->SetSubMenu(sub_menu);
125     menu->Append(item);
126 
127     if (parent) {
128         parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item, parent](wxUpdateUIEvent& evt) {
129             enable_menu_item(evt, cb_condition, item, parent); }, id);
130     }
131 
132     return item;
133 }
134 
append_menu_radio_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,wxEvtHandler * event_handler)135 wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
136     std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler)
137 {
138     if (id == wxID_ANY)
139         id = wxNewId();
140 
141     wxMenuItem* item = menu->AppendRadioItem(id, string, description);
142 
143 #ifdef __WXMSW__
144     if (event_handler != nullptr && event_handler != menu)
145         event_handler->Bind(wxEVT_MENU, cb, id);
146     else
147 #endif // __WXMSW__
148         menu->Bind(wxEVT_MENU, cb, id);
149 
150     return item;
151 }
152 
append_menu_check_item(wxMenu * menu,int id,const wxString & string,const wxString & description,std::function<void (wxCommandEvent & event)> cb,wxEvtHandler * event_handler,std::function<bool ()> const enable_condition,std::function<bool ()> const check_condition,wxWindow * parent)153 wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
154     std::function<void(wxCommandEvent & event)> cb, wxEvtHandler* event_handler,
155     std::function<bool()> const enable_condition, std::function<bool()> const check_condition, wxWindow* parent)
156 {
157     if (id == wxID_ANY)
158         id = wxNewId();
159 
160     wxMenuItem* item = menu->AppendCheckItem(id, string, description);
161 
162 #ifdef __WXMSW__
163     if (event_handler != nullptr && event_handler != menu)
164         event_handler->Bind(wxEVT_MENU, cb, id);
165     else
166 #endif // __WXMSW__
167         menu->Bind(wxEVT_MENU, cb, id);
168 
169     if (parent)
170         parent->Bind(wxEVT_UPDATE_UI, [enable_condition, check_condition](wxUpdateUIEvent& evt)
171             {
172                 evt.Enable(enable_condition());
173                 evt.Check(check_condition());
174             }, id);
175 
176     return item;
177 }
178 
179 const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200;
180 const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200;
181 
Create(wxWindow * parent)182 bool wxCheckListBoxComboPopup::Create(wxWindow* parent)
183 {
184     return wxCheckListBox::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0));
185 }
186 
GetControl()187 wxWindow* wxCheckListBoxComboPopup::GetControl()
188 {
189     return this;
190 }
191 
SetStringValue(const wxString & value)192 void wxCheckListBoxComboPopup::SetStringValue(const wxString& value)
193 {
194     m_text = value;
195 }
196 
GetStringValue() const197 wxString wxCheckListBoxComboPopup::GetStringValue() const
198 {
199     return m_text;
200 }
201 
GetAdjustedSize(int minWidth,int prefHeight,int maxHeight)202 wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
203 {
204     // set width dinamically in dependence of items text
205     // and set height dinamically in dependence of items count
206 
207     wxComboCtrl* cmb = GetComboCtrl();
208     if (cmb != nullptr) {
209         wxSize size = GetComboCtrl()->GetSize();
210 
211         unsigned int count = GetCount();
212         if (count > 0) {
213             int max_width = size.x;
214             for (unsigned int i = 0; i < count; ++i) {
215                 max_width = std::max(max_width, 60 + GetTextExtent(GetString(i)).x);
216             }
217             size.SetWidth(max_width);
218             size.SetHeight(count * cmb->GetCharHeight());
219         }
220         else
221             size.SetHeight(DefaultHeight);
222 
223         return size;
224     }
225     else
226         return wxSize(DefaultWidth, DefaultHeight);
227 }
228 
OnKeyEvent(wxKeyEvent & evt)229 void wxCheckListBoxComboPopup::OnKeyEvent(wxKeyEvent& evt)
230 {
231     // filters out all the keys which are not working properly
232     switch (evt.GetKeyCode())
233     {
234     case WXK_LEFT:
235     case WXK_UP:
236     case WXK_RIGHT:
237     case WXK_DOWN:
238     case WXK_PAGEUP:
239     case WXK_PAGEDOWN:
240     case WXK_END:
241     case WXK_HOME:
242     case WXK_NUMPAD_LEFT:
243     case WXK_NUMPAD_UP:
244     case WXK_NUMPAD_RIGHT:
245     case WXK_NUMPAD_DOWN:
246     case WXK_NUMPAD_PAGEUP:
247     case WXK_NUMPAD_PAGEDOWN:
248     case WXK_NUMPAD_END:
249     case WXK_NUMPAD_HOME:
250     {
251         break;
252     }
253     default:
254     {
255         evt.Skip();
256         break;
257     }
258     }
259 }
260 
OnCheckListBox(wxCommandEvent & evt)261 void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt)
262 {
263     // forwards the checklistbox event to the owner wxComboCtrl
264 
265     if (m_check_box_events_status == OnCheckListBoxFunction::FreeToProceed )
266     {
267         wxComboCtrl* cmb = GetComboCtrl();
268         if (cmb != nullptr) {
269             wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId());
270             event.SetEventObject(cmb);
271             cmb->ProcessWindowEvent(event);
272         }
273     }
274 
275     evt.Skip();
276 
277     #ifndef _WIN32  // events are sent differently on OSX+Linux vs Win (more description in header file)
278         if ( m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed )
279             // this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should
280             // explicitly change the state on the checkbox
281             m_check_box_events_status = OnCheckListBoxFunction::WasRefusedLastTime;
282         else
283             // if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it
284             m_check_box_events_status = OnCheckListBoxFunction::RefuseToProceed;
285     #endif
286 }
287 
OnListBoxSelection(wxCommandEvent & evt)288 void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
289 {
290     // transforms list box item selection event into checklistbox item toggle event
291 
292     int selId = GetSelection();
293     if (selId != wxNOT_FOUND)
294     {
295         #ifndef _WIN32
296             if (m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed)
297         #endif
298                 Check((unsigned int)selId, !IsChecked((unsigned int)selId));
299 
300         m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; // so the checkbox reacts to square-click the next time
301 
302         SetSelection(wxNOT_FOUND);
303         wxCommandEvent event(wxEVT_CHECKLISTBOX, GetId());
304         event.SetInt(selId);
305         event.SetEventObject(this);
306         ProcessEvent(event);
307     }
308 }
309 
310 
311 // ***  wxDataViewTreeCtrlComboPopup  ***
312 
313 const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270;
314 const unsigned int wxDataViewTreeCtrlComboPopup::DefaultHeight = 200;
315 const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22;
316 
Create(wxWindow * parent)317 bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent)
318 {
319 	return wxDataViewTreeCtrl::Create(parent, wxID_ANY/*HIGHEST + 1*/, wxPoint(0, 0), wxDefaultSize/*wxSize(270, -1)*/, wxDV_NO_HEADER);
320 }
321 /*
322 wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
323 {
324 	// matches owner wxComboCtrl's width
325 	// and sets height dinamically in dependence of contained items count
326 	wxComboCtrl* cmb = GetComboCtrl();
327 	if (cmb != nullptr)
328 	{
329 		wxSize size = GetComboCtrl()->GetSize();
330 		if (m_cnt_open_items > 0)
331 			size.SetHeight(m_cnt_open_items * DefaultItemHeight);
332 		else
333 			size.SetHeight(DefaultHeight);
334 
335 		return size;
336 	}
337 	else
338 		return wxSize(DefaultWidth, DefaultHeight);
339 }
340 */
OnKeyEvent(wxKeyEvent & evt)341 void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt)
342 {
343 	// filters out all the keys which are not working properly
344 	if (evt.GetKeyCode() == WXK_UP)
345 	{
346 		return;
347 	}
348 	else if (evt.GetKeyCode() == WXK_DOWN)
349 	{
350 		return;
351 	}
352 	else
353 	{
354 		evt.Skip();
355 		return;
356 	}
357 }
358 
OnDataViewTreeCtrlSelection(wxCommandEvent & evt)359 void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& evt)
360 {
361 	wxComboCtrl* cmb = GetComboCtrl();
362 	auto selected = GetItemText(GetSelection());
363 	cmb->SetText(selected);
364 }
365 
366 // edit tooltip : change Slic3r to SLIC3R_APP_KEY
367 // Temporary workaround for localization
edit_tooltip(wxString & tooltip)368 void edit_tooltip(wxString& tooltip)
369 {
370     tooltip.Replace("Slic3r", SLIC3R_APP_KEY, true);
371 }
372 
373 /* Function for rescale of buttons in Dialog under MSW if dpi is changed.
374  * btn_ids - vector of buttons identifiers
375  */
msw_buttons_rescale(wxDialog * dlg,const int em_unit,const std::vector<int> & btn_ids)376 void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids)
377 {
378     const wxSize& btn_size = wxSize(-1, int(2.5f * em_unit + 0.5f));
379 
380     for (int btn_id : btn_ids) {
381         // There is a case [FirmwareDialog], when we have wxControl instead of wxButton
382         // so let casting everything to the wxControl
383         wxControl* btn = static_cast<wxControl*>(dlg->FindWindowById(btn_id, dlg));
384         if (btn)
385             btn->SetMinSize(btn_size);
386     }
387 }
388 
389 /* Function for getting of em_unit value from correct parent.
390  * In most of cases it is m_em_unit value from GUI_App,
391  * but for DPIDialogs it's its own value.
392  * This value will be used to correct rescale after moving between
393  * Displays with different HDPI */
em_unit(wxWindow * win)394 int em_unit(wxWindow* win)
395 {
396     if (win)
397     {
398         wxTopLevelWindow *toplevel = Slic3r::GUI::find_toplevel_parent(win);
399         Slic3r::GUI::DPIDialog* dlg = dynamic_cast<Slic3r::GUI::DPIDialog*>(toplevel);
400         if (dlg)
401             return dlg->em_unit();
402         Slic3r::GUI::DPIFrame* frame = dynamic_cast<Slic3r::GUI::DPIFrame*>(toplevel);
403         if (frame)
404             return frame->em_unit();
405     }
406 
407     return Slic3r::GUI::wxGetApp().em_unit();
408 }
409 
mode_icon_px_size()410 int mode_icon_px_size()
411 {
412 #ifdef __APPLE__
413     return 10;
414 #else
415     return 12;
416 #endif
417 }
418 
419 // win is used to get a correct em_unit value
420 // It's important for bitmaps of dialogs.
421 // if win == nullptr, em_unit value of MainFrame will be used
create_scaled_bitmap(const std::string & bmp_name_in,wxWindow * win,const int px_cnt,const bool grayscale)422 wxBitmap create_scaled_bitmap(  const std::string& bmp_name_in,
423                                 wxWindow *win/* = nullptr*/,
424                                 const int px_cnt/* = 16*/,
425                                 const bool grayscale/* = false*/)
426 {
427     static Slic3r::GUI::BitmapCache cache;
428 
429     unsigned int width = 0;
430     unsigned int height = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f);
431 
432     std::string bmp_name = bmp_name_in;
433     boost::replace_last(bmp_name, ".png", "");
434 
435     // Try loading an SVG first, then PNG if SVG is not found:
436     wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, Slic3r::GUI::wxGetApp().dark_mode());
437     if (bmp == nullptr) {
438         bmp = cache.load_png(bmp_name, width, height, grayscale);
439     }
440 
441     if (bmp == nullptr) {
442         // Neither SVG nor PNG has been found, raise error
443         throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name);
444     }
445 
446     return *bmp;
447 }
448 
get_extruder_color_icons(bool thin_icon)449 std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon/* = false*/)
450 {
451     static Slic3r::GUI::BitmapCache bmp_cache;
452 
453     // Create the bitmap with color bars.
454     std::vector<wxBitmap*> bmps;
455     std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
456 
457     if (colors.empty())
458         return bmps;
459 
460     unsigned char rgb[3];
461 
462     /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display.
463      * So set sizes for solid_colored icons used for filament preset
464      * and scale them in respect to em_unit value
465      */
466     const double em = Slic3r::GUI::wxGetApp().em_unit();
467     const int icon_width = lround((thin_icon ? 1.6 : 3.2) * em);
468     const int icon_height = lround(1.6 * em);
469 
470     for (const std::string& color : colors)
471     {
472         std::string bitmap_key = color + "-h" + std::to_string(icon_height) + "-w" + std::to_string(icon_width);
473 
474         wxBitmap* bitmap = bmp_cache.find(bitmap_key);
475         if (bitmap == nullptr) {
476             // Paint the color icon.
477             Slic3r::GUI::BitmapCache::parse_color(color, rgb);
478             // there is no neede to scale created solid bitmap
479             bitmap = bmp_cache.insert(bitmap_key, bmp_cache.mksolid(icon_width, icon_height, rgb, true));
480         }
481         bmps.emplace_back(bitmap);
482     }
483 
484     return bmps;
485 }
486 
487 
apply_extruder_selector(wxBitmapComboBox ** ctrl,wxWindow * parent,const std::string & first_item,wxPoint pos,wxSize size,bool use_thin_icon)488 void apply_extruder_selector(wxBitmapComboBox** ctrl,
489                              wxWindow* parent,
490                              const std::string& first_item/* = ""*/,
491                              wxPoint pos/* = wxDefaultPosition*/,
492                              wxSize size/* = wxDefaultSize*/,
493                              bool use_thin_icon/* = false*/)
494 {
495     std::vector<wxBitmap*> icons = get_extruder_color_icons(use_thin_icon);
496 
497     if (!*ctrl)
498         *ctrl = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, pos, size,
499             0, nullptr, wxCB_READONLY);
500     else
501     {
502         (*ctrl)->SetPosition(pos);
503         (*ctrl)->SetMinSize(size);
504         (*ctrl)->SetSize(size);
505         (*ctrl)->Clear();
506     }
507     if (first_item.empty())
508         (*ctrl)->Hide();    // to avoid unwanted rendering before layout (ExtruderSequenceDialog)
509 
510     if (icons.empty() && !first_item.empty()) {
511         (*ctrl)->Append(_(first_item), wxNullBitmap);
512         return;
513     }
514 
515     // For ObjectList we use short extruder name (just a number)
516     const bool use_full_item_name = dynamic_cast<Slic3r::GUI::ObjectList*>(parent) == nullptr;
517 
518     int i = 0;
519     wxString str = _(L("Extruder"));
520     for (wxBitmap* bmp : icons) {
521         if (i == 0) {
522             if (!first_item.empty())
523                 (*ctrl)->Append(_(first_item), *bmp);
524             ++i;
525         }
526 
527         (*ctrl)->Append(use_full_item_name
528                         ? Slic3r::GUI::from_u8((boost::format("%1% %2%") % str % i).str())
529                         : wxString::Format("%d", i), *bmp);
530         ++i;
531     }
532     (*ctrl)->SetSelection(0);
533 }
534 
535 
536 // ----------------------------------------------------------------------------
537 // LockButton
538 // ----------------------------------------------------------------------------
539 
LockButton(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size)540 LockButton::LockButton( wxWindow *parent,
541                         wxWindowID id,
542                         const wxPoint& pos /*= wxDefaultPosition*/,
543                         const wxSize& size /*= wxDefaultSize*/):
544                         wxButton(parent, id, wxEmptyString, pos, size, wxBU_EXACTFIT | wxNO_BORDER)
545 {
546     m_bmp_lock_closed   = ScalableBitmap(this, "lock_closed");
547     m_bmp_lock_closed_f = ScalableBitmap(this, "lock_closed_f");
548     m_bmp_lock_open     = ScalableBitmap(this, "lock_open");
549     m_bmp_lock_open_f   = ScalableBitmap(this, "lock_open_f");
550 
551 #ifdef __WXMSW__
552     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
553 #endif // __WXMSW__
554     SetBitmap(m_bmp_lock_open.bmp());
555     SetBitmapDisabled(m_bmp_lock_open.bmp());
556     SetBitmapHover(m_bmp_lock_closed_f.bmp());
557 
558     //button events
559     Bind(wxEVT_BUTTON, &LockButton::OnButton, this);
560 }
561 
OnButton(wxCommandEvent & event)562 void LockButton::OnButton(wxCommandEvent& event)
563 {
564     if (m_disabled)
565         return;
566 
567     m_is_pushed = !m_is_pushed;
568     update_button_bitmaps();
569 
570     event.Skip();
571 }
572 
SetLock(bool lock)573 void LockButton::SetLock(bool lock)
574 {
575     m_is_pushed = lock;
576     update_button_bitmaps();
577 }
578 
msw_rescale()579 void LockButton::msw_rescale()
580 {
581     m_bmp_lock_closed.msw_rescale();
582     m_bmp_lock_closed_f.msw_rescale();
583     m_bmp_lock_open.msw_rescale();
584     m_bmp_lock_open_f.msw_rescale();
585 
586     update_button_bitmaps();
587 }
588 
update_button_bitmaps()589 void LockButton::update_button_bitmaps()
590 {
591     SetBitmap(m_is_pushed ? m_bmp_lock_closed.bmp() : m_bmp_lock_open.bmp());
592     SetBitmapHover(m_is_pushed ? m_bmp_lock_closed_f.bmp() : m_bmp_lock_open_f.bmp());
593 
594     Refresh();
595     Update();
596 }
597 
598 
599 
600 // ----------------------------------------------------------------------------
601 // ModeButton
602 // ----------------------------------------------------------------------------
603 
ModeButton(wxWindow * parent,wxWindowID id,const std::string & icon_name,const wxString & mode,const wxSize & size,const wxPoint & pos)604 ModeButton::ModeButton( wxWindow *          parent,
605                         wxWindowID          id,
606                         const std::string&  icon_name   /* = ""*/,
607                         const wxString&     mode        /* = wxEmptyString*/,
608                         const wxSize&       size        /* = wxDefaultSize*/,
609                         const wxPoint&      pos         /* = wxDefaultPosition*/) :
610     ScalableButton(parent, id, icon_name, mode, size, pos, wxBU_EXACTFIT)
611 {
612     Init(mode);
613 }
614 
ModeButton(wxWindow * parent,const wxString & mode,const std::string & icon_name,int px_cnt)615 ModeButton::ModeButton( wxWindow*           parent,
616                         const wxString&     mode/* = wxEmptyString*/,
617                         const std::string&  icon_name/* = ""*/,
618                         int                 px_cnt/* = 16*/) :
619     ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT)
620 {
621     Init(mode);
622 }
623 
Init(const wxString & mode)624 void ModeButton::Init(const wxString &mode)
625 {
626     std::string mode_str = std::string(mode.ToUTF8());
627     m_tt_focused  = Slic3r::GUI::from_u8((boost::format(_utf8(L("Switch to the %s mode"))) % mode_str).str());
628     m_tt_selected = Slic3r::GUI::from_u8((boost::format(_utf8(L("Current mode is %s"))) % mode_str).str());
629 
630     SetBitmapMargins(3, 0);
631 
632     //button events
633     Bind(wxEVT_BUTTON,          &ModeButton::OnButton, this);
634     Bind(wxEVT_ENTER_WINDOW,    &ModeButton::OnEnterBtn, this);
635     Bind(wxEVT_LEAVE_WINDOW,    &ModeButton::OnLeaveBtn, this);
636 }
637 
OnButton(wxCommandEvent & event)638 void ModeButton::OnButton(wxCommandEvent& event)
639 {
640     m_is_selected = true;
641     focus_button(m_is_selected);
642 
643     event.Skip();
644 }
645 
SetState(const bool state)646 void ModeButton::SetState(const bool state)
647 {
648     m_is_selected = state;
649     focus_button(m_is_selected);
650     SetToolTip(state ? m_tt_selected : m_tt_focused);
651 }
652 
focus_button(const bool focus)653 void ModeButton::focus_button(const bool focus)
654 {
655     const wxFont& new_font = focus ?
656                              Slic3r::GUI::wxGetApp().bold_font() :
657                              Slic3r::GUI::wxGetApp().normal_font();
658 
659     SetFont(new_font);
660     SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT :
661 #if defined (__linux__) && defined (__WXGTK3__)
662         wxSYS_COLOUR_GRAYTEXT
663 #elif defined (__linux__) && defined (__WXGTK2__)
664         wxSYS_COLOUR_BTNTEXT
665 #else
666         wxSYS_COLOUR_BTNSHADOW
667 #endif
668     ));
669 
670     Refresh();
671     Update();
672 }
673 
674 
675 // ----------------------------------------------------------------------------
676 // ModeSizer
677 // ----------------------------------------------------------------------------
678 
ModeSizer(wxWindow * parent,int hgap)679 ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) :
680     wxFlexGridSizer(3, 0, hgap)
681 {
682     SetFlexibleDirection(wxHORIZONTAL);
683 
684     std::vector < std::pair < wxString, std::string >> buttons = {
685         {_(L("Simple")),    "mode_simple"},
686 //        {_(L("Advanced")),  "mode_advanced"},
687         {_CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), "mode_advanced"},
688         {_(L("Expert")),    "mode_expert"},
689     };
690 
691     auto modebtnfn = [](wxCommandEvent &event, int mode_id) {
692         Slic3r::GUI::wxGetApp().save_mode(mode_id);
693         event.Skip();
694     };
695 
696     m_mode_btns.reserve(3);
697     for (const auto& button : buttons) {
698         m_mode_btns.push_back(new ModeButton(parent, button.first, button.second, mode_icon_px_size()));
699 
700         m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1)));
701         Add(m_mode_btns.back());
702     }
703 }
704 
SetMode(const int mode)705 void ModeSizer::SetMode(const int mode)
706 {
707     for (size_t m = 0; m < m_mode_btns.size(); m++)
708         m_mode_btns[m]->SetState(int(m) == mode);
709 }
710 
set_items_flag(int flag)711 void ModeSizer::set_items_flag(int flag)
712 {
713     for (wxSizerItem* item : this->GetChildren())
714         item->SetFlag(flag);
715 }
716 
set_items_border(int border)717 void ModeSizer::set_items_border(int border)
718 {
719     for (wxSizerItem* item : this->GetChildren())
720         item->SetBorder(border);
721 }
722 
msw_rescale()723 void ModeSizer::msw_rescale()
724 {
725     for (size_t m = 0; m < m_mode_btns.size(); m++)
726         m_mode_btns[m]->msw_rescale();
727 }
728 
729 // ----------------------------------------------------------------------------
730 // MenuWithSeparators
731 // ----------------------------------------------------------------------------
732 
DestroySeparators()733 void MenuWithSeparators::DestroySeparators()
734 {
735     if (m_separator_frst) {
736         Destroy(m_separator_frst);
737         m_separator_frst = nullptr;
738     }
739 
740     if (m_separator_scnd) {
741         Destroy(m_separator_scnd);
742         m_separator_scnd = nullptr;
743     }
744 }
745 
SetFirstSeparator()746 void MenuWithSeparators::SetFirstSeparator()
747 {
748     m_separator_frst = this->AppendSeparator();
749 }
750 
SetSecondSeparator()751 void MenuWithSeparators::SetSecondSeparator()
752 {
753     m_separator_scnd = this->AppendSeparator();
754 }
755 
756 // ----------------------------------------------------------------------------
757 // PrusaBitmap
758 // ----------------------------------------------------------------------------
ScalableBitmap(wxWindow * parent,const std::string & icon_name,const int px_cnt,const bool grayscale)759 ScalableBitmap::ScalableBitmap( wxWindow *parent,
760                                 const std::string& icon_name/* = ""*/,
761                                 const int px_cnt/* = 16*/,
762                                 const bool grayscale/* = false*/):
763     m_parent(parent), m_icon_name(icon_name),
764     m_px_cnt(px_cnt)
765 {
766     m_bmp = create_scaled_bitmap(icon_name, parent, px_cnt, grayscale);
767 }
768 
GetBmpSize() const769 wxSize ScalableBitmap::GetBmpSize() const
770 {
771 #ifdef __APPLE__
772     return m_bmp.GetScaledSize();
773 #else
774     return m_bmp.GetSize();
775 #endif
776 }
777 
GetBmpWidth() const778 int ScalableBitmap::GetBmpWidth() const
779 {
780 #ifdef __APPLE__
781     return m_bmp.GetScaledWidth();
782 #else
783     return m_bmp.GetWidth();
784 #endif
785 }
786 
GetBmpHeight() const787 int ScalableBitmap::GetBmpHeight() const
788 {
789 #ifdef __APPLE__
790     return m_bmp.GetScaledHeight();
791 #else
792     return m_bmp.GetHeight();
793 #endif
794 }
795 
796 
msw_rescale()797 void ScalableBitmap::msw_rescale()
798 {
799     m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt, m_grayscale);
800 }
801 
802 // ----------------------------------------------------------------------------
803 // PrusaButton
804 // ----------------------------------------------------------------------------
805 
ScalableButton(wxWindow * parent,wxWindowID id,const std::string & icon_name,const wxString & label,const wxSize & size,const wxPoint & pos,long style,bool use_default_disabled_bitmap,int bmp_px_cnt)806 ScalableButton::ScalableButton( wxWindow *          parent,
807                                 wxWindowID          id,
808                                 const std::string&  icon_name /*= ""*/,
809                                 const wxString&     label /* = wxEmptyString*/,
810                                 const wxSize&       size /* = wxDefaultSize*/,
811                                 const wxPoint&      pos /* = wxDefaultPosition*/,
812                                 long                style /*= wxBU_EXACTFIT | wxNO_BORDER*/,
813                                 bool                use_default_disabled_bitmap/* = false*/,
814                                 int                 bmp_px_cnt/* = 16*/) :
815     m_parent(parent),
816     m_current_icon_name(icon_name),
817     m_use_default_disabled_bitmap (use_default_disabled_bitmap),
818     m_px_cnt(bmp_px_cnt)
819 {
820     Create(parent, id, label, pos, size, style);
821 #ifdef __WXMSW__
822     if (style & wxNO_BORDER)
823         SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
824 #endif // __WXMSW__
825 
826     SetBitmap(create_scaled_bitmap(icon_name, parent, m_px_cnt));
827     if (m_use_default_disabled_bitmap)
828         SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true));
829 
830     if (size != wxDefaultSize)
831     {
832         const int em = em_unit(parent);
833         m_width = size.x/em;
834         m_height= size.y/em;
835     }
836 }
837 
838 
ScalableButton(wxWindow * parent,wxWindowID id,const ScalableBitmap & bitmap,const wxString & label,long style)839 ScalableButton::ScalableButton( wxWindow *          parent,
840                                 wxWindowID          id,
841                                 const ScalableBitmap&  bitmap,
842                                 const wxString&     label /*= wxEmptyString*/,
843                                 long                style /*= wxBU_EXACTFIT | wxNO_BORDER*/) :
844     m_parent(parent),
845     m_current_icon_name(bitmap.name()),
846     m_px_cnt(bitmap.px_cnt())
847 {
848     Create(parent, id, label, wxDefaultPosition, wxDefaultSize, style);
849 #ifdef __WXMSW__
850     if (style & wxNO_BORDER)
851         SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
852 #endif // __WXMSW__
853 
854     SetBitmap(bitmap.bmp());
855 }
856 
SetBitmap_(const ScalableBitmap & bmp)857 void ScalableButton::SetBitmap_(const ScalableBitmap& bmp)
858 {
859     SetBitmap(bmp.bmp());
860     m_current_icon_name = bmp.name();
861 }
862 
SetBitmapDisabled_(const ScalableBitmap & bmp)863 void ScalableButton::SetBitmapDisabled_(const ScalableBitmap& bmp)
864 {
865     SetBitmapDisabled(bmp.bmp());
866     m_disabled_icon_name = bmp.name();
867 }
868 
GetBitmapHeight()869 int ScalableButton::GetBitmapHeight()
870 {
871 #ifdef __APPLE__
872     return GetBitmap().GetScaledHeight();
873 #else
874     return GetBitmap().GetHeight();
875 #endif
876 }
877 
UseDefaultBitmapDisabled()878 void ScalableButton::UseDefaultBitmapDisabled()
879 {
880     m_use_default_disabled_bitmap = true;
881     SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true));
882 }
883 
msw_rescale()884 void ScalableButton::msw_rescale()
885 {
886     SetBitmap(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt));
887     if (!m_disabled_icon_name.empty())
888         SetBitmapDisabled(create_scaled_bitmap(m_disabled_icon_name, m_parent, m_px_cnt));
889     else if (m_use_default_disabled_bitmap)
890         SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true));
891 
892     if (m_width > 0 || m_height>0)
893     {
894         const int em = em_unit(m_parent);
895         wxSize size(m_width * em, m_height * em);
896         SetMinSize(size);
897     }
898 }
899 
900 
901 // ----------------------------------------------------------------------------
902 // BlinkingBitmap
903 // ----------------------------------------------------------------------------
904 
BlinkingBitmap(wxWindow * parent,const std::string & icon_name)905 BlinkingBitmap::BlinkingBitmap(wxWindow* parent, const std::string& icon_name) :
906     wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(int(1.6 * Slic3r::GUI::wxGetApp().em_unit()), -1))
907 {
908     bmp = ScalableBitmap(parent, icon_name);
909 }
910 
msw_rescale()911 void BlinkingBitmap::msw_rescale()
912 {
913     bmp.msw_rescale();
914     this->SetSize(bmp.GetBmpSize());
915     this->SetMinSize(bmp.GetBmpSize());
916 }
917 
invalidate()918 void BlinkingBitmap::invalidate()
919 {
920     this->SetBitmap(wxNullBitmap);
921 }
922 
activate()923 void BlinkingBitmap::activate()
924 {
925     this->SetBitmap(bmp.bmp());
926     show = true;
927 }
928 
blink()929 void BlinkingBitmap::blink()
930 {
931     show = !show;
932     this->SetBitmap(show ? bmp.bmp() : wxNullBitmap);
933 }
934 
935 
936 
937 
938