// Copyright (C) 2005 Davis E. King (davis@dlib.net), Keita Mochizuki // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_WIDGETs_ #define DLIB_WIDGETs_ #include "../algs.h" #include "widgets_abstract.h" #include "drawable.h" #include "../gui_core.h" #include "fonts.h" #include <string> #include <sstream> #include "../timer.h" #include "base_widgets.h" #include "../member_function_pointer.h" #include "../array.h" #include "../array2d.h" #include "../sequence.h" #include "../dir_nav.h" #include "../queue.h" #include "../smart_pointers.h" #include "style.h" #include "../string.h" #include "../misc_api.h" #include <cctype> #include <vector> #ifdef _MSC_VER // This #pragma directive is also located in the algs.h file but for whatever // reason visual studio 9 just ignores it when it is only there. // this is to disable the "'this' : used in base member initializer list" // warning you get from some of the GUI objects since all the objects // require that their parent class be passed into their constructor. // In this case though it is totally safe so it is ok to disable this warning. #pragma warning(disable : 4355) #endif namespace dlib { // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class label // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class label : public drawable { public: label( drawable_window& w ) : drawable(w), text_color_(0,0,0) { enable_events(); } ~label() { disable_events(); parent.invalidate_rectangle(rect); } void set_text ( const std::string& text ); void set_text ( const std::wstring& text ); void set_text ( const dlib::ustring& text ); const std::string text ( ) const; const std::wstring wtext ( ) const; const dlib::ustring utext ( ) const; void set_text_color ( const rgb_pixel color ); const rgb_pixel text_color ( ) const; void set_main_font ( const shared_ptr_thread_safe<font>& f ); private: dlib::ustring text_; rgb_pixel text_color_; // restricted functions label(label&); // copy constructor label& operator=(label&); // assignment operator protected: void draw ( const canvas& c ) const; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class toggle_button // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class toggle_button : public button_action { /*! INITIAL VALUE - checked == false CONVENTION - is_checked() == checked !*/ public: toggle_button( drawable_window& w ) : button_action(w), btn_tooltip(w), checked(false) { style.reset(new toggle_button_style_default()); enable_events(); } ~toggle_button() { disable_events(); parent.invalidate_rectangle(rect); } void set_name ( const std::string& name ); void set_name ( const std::wstring& name ); void set_name ( const dlib::ustring& name ); void set_size ( unsigned long width_, unsigned long height_ ); void set_tooltip_text ( const std::string& text ); void set_tooltip_text ( const std::wstring& text ); void set_tooltip_text ( const ustring& text ); const std::string tooltip_text ( ) const; const std::wstring tooltip_wtext ( ) const; const dlib::ustring tooltip_utext ( ) const; bool is_checked ( ) const; const std::string name ( ) const; const std::wstring wname ( ) const; const dlib::ustring uname ( ) const; void set_checked ( ); void set_unchecked ( ); void show ( ); void hide ( ); void enable ( ); void disable ( ); void set_main_font ( const shared_ptr_thread_safe<font>& f ); void set_pos ( long x, long y ); template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); rect = move_rect(style->get_min_size(name_,*mfont), rect.left(), rect.top()); parent.invalidate_rectangle(rect); } template < typename T > void set_click_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(m); event_handler.set(object,event_handler_); event_handler_self.clear(); } template < typename T > void set_click_handler ( T& object, void (T::*event_handler_)(toggle_button&) ) { auto_mutex M(m); event_handler_self.set(object,event_handler_); event_handler.clear(); } private: // restricted functions toggle_button(toggle_button&); // copy constructor toggle_button& operator=(toggle_button&); // assignment operator dlib::ustring name_; tooltip btn_tooltip; bool checked; member_function_pointer<>::kernel_1a event_handler; member_function_pointer<toggle_button&>::kernel_1a event_handler_self; scoped_ptr<toggle_button_style> style; protected: void draw ( const canvas& c ) const { style->draw_toggle_button(c,rect,enabled,*mfont,lastx,lasty,name_,is_depressed(),checked); } void on_button_up ( bool mouse_over ); void on_mouse_over ( ){ if (style->redraw_on_mouse_over()) parent.invalidate_rectangle(rect); } void on_mouse_not_over ( ){ if (style->redraw_on_mouse_over()) parent.invalidate_rectangle(rect); } }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class text_field // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class text_field : public drawable { /*! INITIAL VALUE text_color_ == rgb_pixel(0,0,0) bg_color_ == rgb_pixel(255,255,255) cursor_pos == 0 text_width == 0 text_ == "" has_focus == false cursor_visible == false recent_movement == false highlight_start == 0 highlight_end == -1 shift_pos == -1 text_pos == 0 CONVENTION - cursor_pos == the position of the cursor in the string text_. The cursor appears before the letter text_[cursor_pos] - cursor_x == the x coordinate of the cursor relative to the left side of rect. i.e. the number of pixels that separate the cursor from the left side of the text_field. - has_focus == true if this text field has keyboard input focus - cursor_visible == true if the cursor should be painted - text_ == text() - text_pos == the index of the first letter in text_ that appears in this text field. - text_width == the width of text_[text_pos] though text_[text.size()-1] - if (has_focus && the user has recently moved the cursor) then - recent_movement == true - else - recent_movement == false - if (highlight_start <= highlight_end) then - text[highlight_start] though text[highlight_end] should be highlighted - if (shift_pos != -1) then - has_focus == true - the shift key is being held down or the left mouse button is being held down. - shift_pos == the position of the cursor when the shift or mouse key was first pressed. - text_color() == text_color_ - background_color() == bg_color_ !*/ public: text_field( drawable_window& w ) : drawable(w,MOUSE_CLICK | KEYBOARD_EVENTS | MOUSE_MOVE | STRING_PUT), text_color_(0,0,0), bg_color_(255,255,255), text_width(0), text_pos(0), recent_movement(false), has_focus(false), cursor_visible(false), cursor_pos(0), highlight_start(0), highlight_end(-1), shift_pos(-1), t(*this,&text_field::timer_action), right_click_menu(w) { style.reset(new text_field_style_default()); rect.set_bottom(mfont->height()+ (style->get_padding(*mfont))*2); rect.set_right((style->get_padding(*mfont))*2); cursor_x = style->get_padding(*mfont); right_click_menu.menu().add_menu_item(menu_item_text("Cut",*this,&text_field::on_cut,'t')); right_click_menu.menu().add_menu_item(menu_item_text("Copy",*this,&text_field::on_copy,'C')); right_click_menu.menu().add_menu_item(menu_item_text("Paste",*this,&text_field::on_paste,'P')); right_click_menu.menu().add_menu_item(menu_item_text("Delete",*this,&text_field::on_delete_selected,'D')); right_click_menu.menu().add_menu_item(menu_item_separator()); right_click_menu.menu().add_menu_item(menu_item_text("Select All",*this,&text_field::on_select_all,'A')); right_click_menu.set_rect(get_text_rect()); enable_events(); t.set_delay_time(500); } ~text_field ( ) { disable_events(); parent.invalidate_rectangle(rect); t.stop_and_wait(); } template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); // call this just so that this widget redraws itself with the new style set_main_font(mfont); } void set_text ( const std::string& text_ ); void set_text ( const std::wstring& text_ ); void give_input_focus ( ); void select_all_text ( ); void set_text ( const dlib::ustring& text_ ); const std::string text ( ) const; const std::wstring wtext ( ) const; const dlib::ustring utext ( ) const; void set_text_color ( const rgb_pixel color ); const rgb_pixel text_color ( ) const; void set_background_color ( const rgb_pixel color ); const rgb_pixel background_color ( ) const; void set_width ( unsigned long width ); void set_pos ( long x, long y ); void set_main_font ( const shared_ptr_thread_safe<font>& f ); int next_free_user_event_number ( ) const { return drawable::next_free_user_event_number()+1; } void disable ( ); void enable ( ); void hide ( ); void show ( ); template < typename T > void set_text_modified_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); text_modified_handler.set(object,event_handler); } template < typename T > void set_enter_key_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); enter_key_handler.set(object,event_handler); } template < typename T > void set_focus_lost_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); focus_lost_handler.set(object,event_handler); } private: void on_cut ( ); void on_copy ( ); void on_paste ( ); void on_select_all ( ); void on_delete_selected ( ); void on_text_is_selected ( ); void on_no_text_selected ( ); void on_user_event ( int num ) { // ignore this user event if it isn't for us if (num != drawable::next_free_user_event_number()) return; if (recent_movement == false) { cursor_visible = !cursor_visible; parent.invalidate_rectangle(rect); } else { if (cursor_visible == false) { cursor_visible = true; parent.invalidate_rectangle(rect); } recent_movement = false; } } void timer_action ( ) { parent.trigger_user_event(this,drawable::next_free_user_event_number()); } /*! ensures - flips the state of cursor_visible !*/ void move_cursor ( unsigned long pos ); /*! requires - pos <= text_.size() ensures - moves the cursor to the position given by pos and moves the text in the text box if necessary - if the position changes then the parent window will be updated !*/ rectangle get_text_rect ( ) const; /*! ensures - returns the rectangle that should contain the text in this widget !*/ dlib::ustring text_; rgb_pixel text_color_; rgb_pixel bg_color_; unsigned long text_width; unsigned long text_pos; bool recent_movement; bool has_focus; bool cursor_visible; long cursor_pos; unsigned long cursor_x; // this tells you what part of the text is highlighted long highlight_start; long highlight_end; long shift_pos; member_function_pointer<>::kernel_1a_c text_modified_handler; member_function_pointer<>::kernel_1a_c enter_key_handler; member_function_pointer<>::kernel_1a_c focus_lost_handler; scoped_ptr<text_field_style> style; timer<text_field>::kernel_2a t; popup_menu_region right_click_menu; // restricted functions text_field(text_field&); // copy constructor text_field& operator=(text_field&); // assignment operator protected: void draw ( const canvas& c ) const; void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ); void on_mouse_move ( unsigned long state, long x, long y ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); void on_string_put ( const std::wstring &str ); }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class text_box // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class text_box : public scrollable_region { /*! INITIAL VALUE text_color_ == rgb_pixel(0,0,0) bg_color_ == rgb_pixel(255,255,255) cursor_pos == 0 text_ == "" has_focus == false cursor_visible == false recent_movement == false highlight_start == 0 highlight_end == -1 shift_pos == -1 CONVENTION - cursor_pos == the position of the cursor in the string text_. The cursor appears before the letter text_[cursor_pos] - cursor_rect == The rectangle that should be drawn for the cursor. The position is relative to total_rect(). - has_focus == true if this text field has keyboard input focus - cursor_visible == true if the cursor should be painted - text_ == text() - if (has_focus && the user has recently moved the cursor) then - recent_movement == true - else - recent_movement == false - if (highlight_start <= highlight_end) then - text[highlight_start] though text[highlight_end] should be highlighted - if (shift_pos != -1) then - has_focus == true - the shift key is being held down or the left mouse button is being held down. - shift_pos == the position of the cursor when the shift or mouse key was first pressed. - text_color() == text_color_ - background_color() == bg_color_ !*/ public: text_box( drawable_window& w ) : scrollable_region(w,MOUSE_CLICK | KEYBOARD_EVENTS | MOUSE_MOVE | STRING_PUT), text_color_(0,0,0), bg_color_(255,255,255), recent_movement(false), has_focus(false), cursor_visible(false), cursor_pos(0), highlight_start(0), highlight_end(-1), shift_pos(-1), t(*this,&text_box::timer_action), right_click_menu(w) { style.reset(new text_box_style_default()); const long padding = static_cast<long>(style->get_padding(*mfont)); cursor_rect = mfont->compute_cursor_rect(rectangle(padding,padding,1000000,1000000), text_, 0); adjust_total_rect(); set_vertical_mouse_wheel_scroll_increment(mfont->height()); set_horizontal_mouse_wheel_scroll_increment(mfont->height()); right_click_menu.menu().add_menu_item(menu_item_text("Cut",*this,&text_box::on_cut,'t')); right_click_menu.menu().add_menu_item(menu_item_text("Copy",*this,&text_box::on_copy,'C')); right_click_menu.menu().add_menu_item(menu_item_text("Paste",*this,&text_box::on_paste,'P')); right_click_menu.menu().add_menu_item(menu_item_text("Delete",*this,&text_box::on_delete_selected,'D')); right_click_menu.menu().add_menu_item(menu_item_separator()); right_click_menu.menu().add_menu_item(menu_item_text("Select All",*this,&text_box::on_select_all,'A')); right_click_menu.set_rect(get_text_rect()); set_size(100,100); enable_events(); t.set_delay_time(500); } ~text_box ( ) { disable_events(); parent.invalidate_rectangle(rect); t.stop_and_wait(); } template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); scrollable_region::set_style(style_.get_scrollable_region_style()); // call this just so that this widget redraws itself with the new style set_main_font(mfont); } void set_text ( const std::string& text_ ); void set_text ( const std::wstring& text_ ); void set_text ( const dlib::ustring& text_ ); const std::string text ( ) const; const std::wstring wtext ( ) const; const dlib::ustring utext ( ) const; void set_text_color ( const rgb_pixel color ); const rgb_pixel text_color ( ) const; void set_background_color ( const rgb_pixel color ); const rgb_pixel background_color ( ) const; void set_size ( unsigned long width, unsigned long height ); void set_pos ( long x, long y ); void set_main_font ( const shared_ptr_thread_safe<font>& f ); int next_free_user_event_number ( ) const { return scrollable_region::next_free_user_event_number()+1; } void disable ( ); void enable ( ); void hide ( ); void show ( ); template < typename T > void set_text_modified_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); text_modified_handler.set(object,event_handler); } template < typename T > void set_enter_key_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); enter_key_handler.set(object,event_handler); } template < typename T > void set_focus_lost_handler ( T& object, void (T::*event_handler)() ) { auto_mutex M(m); focus_lost_handler.set(object,event_handler); } private: void on_cut ( ); void on_copy ( ); void on_paste ( ); void on_select_all ( ); void on_delete_selected ( ); void on_text_is_selected ( ); void on_no_text_selected ( ); void on_user_event ( int num ) { // ignore this user event if it isn't for us if (num != scrollable_region::next_free_user_event_number()) return; if (recent_movement == false) { cursor_visible = !cursor_visible; parent.invalidate_rectangle(rect); } else { if (cursor_visible == false) { cursor_visible = true; parent.invalidate_rectangle(rect); } recent_movement = false; } } // The reason for using user actions here rather than just having the timer just call // what it needs directly is to avoid a potential deadlock during destruction of this widget. void timer_action ( ) { parent.trigger_user_event(this,scrollable_region::next_free_user_event_number()); } /*! ensures - flips the state of cursor_visible !*/ void move_cursor ( unsigned long pos ); /*! requires - pos <= text_.size() ensures - moves the cursor to the position given by pos and moves the text in the text box if necessary - if the position changes then the parent window will be updated !*/ rectangle get_text_rect ( ) const; /*! ensures - returns the rectangle that should contain the text in this widget !*/ void adjust_total_rect ( ); /*! ensures - adjusts total_rect() so that it is big enough to contain the text currently in this object. !*/ dlib::ustring text_; rgb_pixel text_color_; rgb_pixel bg_color_; bool recent_movement; bool has_focus; bool cursor_visible; long cursor_pos; rectangle cursor_rect; // this tells you what part of the text is highlighted long highlight_start; long highlight_end; long shift_pos; member_function_pointer<>::kernel_1a_c text_modified_handler; member_function_pointer<>::kernel_1a_c enter_key_handler; member_function_pointer<>::kernel_1a_c focus_lost_handler; scoped_ptr<text_box_style> style; timer<text_box>::kernel_2a t; popup_menu_region right_click_menu; // restricted functions text_box(text_box&); // copy constructor text_box& operator=(text_box&); // assignment operator protected: void draw ( const canvas& c ) const; void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ); void on_mouse_move ( unsigned long state, long x, long y ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); void on_string_put ( const std::wstring &str ); }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class check_box // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class check_box : public toggle_button { public: check_box( drawable_window& w ) : toggle_button(w) { set_style(toggle_button_style_check_box()); } }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class radio_button // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class radio_button : public toggle_button { public: radio_button ( drawable_window& w ) : toggle_button(w) { set_style(toggle_button_style_radio_button()); } }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class tabbed_display // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class tabbed_display : public drawable { /*! INITIAL VALUE - tabs.size() == 0 - selected_tab_ == 0 CONVENTION - number_of_tabs() == tabs.size() - tab_name(idx) == tabs[idx] - if (tabs.size() > 0) then - selected_tab_ == the index of the tab that is currently selected - for all valid i: - tabs[i].width == mfont->compute_size(tabs[i].name) - tabs[i].rect == the rectangle that defines where this tab is - if (tabs[i].group != 0) then - tabs[i].group == a pointer to the widget_group for this tab. - left_pad == the amount of padding in a tab to the left of the name string. - right_pad == the amount of padding in a tab to the right of the name string. - top_pad == the amount of padding in a tab to the top of the name string. - bottom_pad == the amount of padding in a tab to the bottom of the name string. - if (event_handler.is_set()) then - event_handler() is what is called to process click events on this object. !*/ public: tabbed_display( drawable_window& w ); virtual ~tabbed_display( ); void set_size ( unsigned long width, unsigned long height ); void set_number_of_tabs ( unsigned long num ); unsigned long number_of_tabs ( ) const; const std::string tab_name ( unsigned long idx ) const; const std::wstring tab_wname ( unsigned long idx ) const; const dlib::ustring& tab_uname ( unsigned long idx ) const; void set_tab_name ( unsigned long idx, const std::string& new_name ); void set_tab_name ( unsigned long idx, const std::wstring& new_name ); void set_tab_name ( unsigned long idx, const dlib::ustring& new_name ); void set_pos ( long x, long y ); template < typename T > void set_click_handler ( T& object, void (T::*eh)(unsigned long new_idx,unsigned long old_idx) ) { auto_mutex M(m); event_handler.set(object,eh); } void set_tab_group ( unsigned long idx, widget_group& group ); void show ( ); void hide ( ); void enable ( ); void disable ( ); void set_main_font ( const shared_ptr_thread_safe<font>& f ); void fit_to_contents ( ); protected: void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void draw ( const canvas& c ) const; private: void recompute_tabs ( ); /*! ensures - recomputes the rectangles for all the tabs and makes this object wider if needed !*/ void draw_tab ( const rectangle& tab, const canvas& c ) const; /*! ensures - draws the outline of a tab as given by the rectangle onto c !*/ struct tab_data { tab_data() : width(0), group(0) {} dlib::ustring name; unsigned long width; rectangle rect; widget_group* group; }; unsigned long selected_tab_; array<tab_data>::kernel_2a_c tabs; const long left_pad; const long right_pad; const long top_pad; const long bottom_pad; member_function_pointer<unsigned long,unsigned long>::kernel_1a event_handler; // restricted functions tabbed_display(tabbed_display&); // copy constructor tabbed_display& operator=(tabbed_display&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class named_rectangle // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class named_rectangle : public drawable { /*! INITIAL VALUE name == "" CONVENTION name_ == name() !*/ public: named_rectangle( drawable_window& w ); virtual ~named_rectangle( ); void set_size ( unsigned long width, unsigned long height ); void set_name ( const std::string& name ); void set_name ( const std::wstring& name ); void set_name ( const dlib::ustring& name ); const std::string name ( ) const; const std::wstring wname ( ) const; const dlib::ustring uname ( ) const; void wrap_around ( const rectangle& rect ); void set_main_font ( const shared_ptr_thread_safe<font>& f ); protected: void draw ( const canvas& c ) const; private: void make_name_fit_in_rect ( ); dlib::ustring name_; unsigned long name_width; unsigned long name_height; // restricted functions named_rectangle(named_rectangle&); // copy constructor named_rectangle& operator=(named_rectangle&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class mouse_tracker // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class mouse_tracker : public draggable { public: mouse_tracker( drawable_window& w ); ~mouse_tracker( ); void show ( ); void hide ( ); void enable ( ); void disable ( ); void set_pos ( long x, long y ); void set_main_font ( const shared_ptr_thread_safe<font>& f ); protected: void on_mouse_move ( unsigned long state, long x, long y ); void on_drag ( ); void draw ( const canvas& c ) const; void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); private: const long offset; named_rectangle nr; label x_label; label y_label; std::ostringstream sout; long click_x, click_y; // restricted functions mouse_tracker(mouse_tracker&); // copy constructor mouse_tracker& operator=(mouse_tracker&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // function message_box() // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- namespace message_box_helper { class box_win : public drawable_window { void initialize ( ); public: box_win ( const std::string& title_, const std::string& message_ ); box_win ( const std::wstring& title_, const std::wstring& message_ ); box_win ( const dlib::ustring& title_, const dlib::ustring& message_ ); ~box_win ( ); template < typename T > void set_click_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(wm); event_handler.set(object,event_handler_); } private: static void deleter_thread ( void* param ); void on_click ( ); on_close_return_code on_window_close ( ); const std::wstring title; const std::wstring message; label msg; button btn_ok; member_function_pointer<>::kernel_1a event_handler; }; class blocking_box_win : public drawable_window { void initialize ( ); public: blocking_box_win ( const std::string& title_, const std::string& message_ ); blocking_box_win ( const std::wstring& title_, const std::wstring& message_ ); blocking_box_win ( const dlib::ustring& title_, const dlib::ustring& message_ ); ~blocking_box_win ( ); private: void on_click ( ); const std::wstring title; const std::wstring message; label msg; button btn_ok; }; } template < typename T > void message_box ( const std::string& title, const std::string& message, T& object, void (T::*event_handler)() ) { using namespace message_box_helper; box_win* win = new box_win(title,message); win->set_click_handler(object,event_handler); } inline void message_box ( const std::string& title, const std::string& message ) { using namespace message_box_helper; new box_win(title,message); } inline void message_box_blocking ( const std::string& title, const std::string& message ) { using namespace message_box_helper; blocking_box_win w(title,message); w.wait_until_closed(); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class list_box // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- namespace list_box_helper{ template <typename S = std::string> class list_box : public scrollable_region, public enumerable<const S> { /*! INITIAL VALUE - ms_enabled == false - items.size() == 0 - last_selected = 0 CONVENTION - size() == items.size() - (*this)[i] == items[i].name - is_selected(i) == items[i].is_selected - ms_enabled == multiple_select_enabled() - items[i].width == the width of items[i].name as given by font::compute_size() - items[i].height == the height of items[i].name as given by font::compute_size() - last_selected == the last item the user selected !*/ public: list_box( drawable_window& w ); ~list_box( ); bool is_selected ( unsigned long index ) const; void select ( unsigned long index ); void unselect ( unsigned long index ); template < typename style_type > void set_style ( const style_type& style_ ) { auto_mutex M(m); style.reset(new style_type(style_)); scrollable_region::set_style(style_.get_scrollable_region_style()); parent.invalidate_rectangle(rect); } template <typename T> void get_selected ( T& list ) const { auto_mutex M(m); list.clear(); for (unsigned long i = 0; i < items.size(); ++i) { if (items[i].is_selected) { unsigned long idx = i; list.enqueue(idx); } } } template <typename T> void load ( const T& list ) { auto_mutex M(m); items.clear(); unsigned long i = 0; items.set_max_size(list.size()); items.set_size(list.size()); list.reset(); unsigned long max_width = 0; unsigned long total_height = 0; while (list.move_next()) { items[i].is_selected = false; items[i].name = list.element(); mfont->compute_size(items[i].name,items[i].width, items[i].height); if (items[i].width > max_width) max_width = items[i].width; total_height += items[i].height; ++i; } set_total_rect_size(max_width, total_height); parent.invalidate_rectangle(rect); last_selected = 0; } const S& operator[] ( unsigned long index ) const; bool multiple_select_enabled ( ) const; void enable_multiple_select ( ); void disable_multiple_select ( ); template < typename T > void set_double_click_handler ( T& object, void (T::*eh)(unsigned long index) ) { auto_mutex M(m); event_handler.set(object,eh); } template < typename T > void set_click_handler ( T& object, void (T::*eh)(unsigned long index) ) { auto_mutex M(m); single_click_event_handler.set(object,eh); } bool at_start ( ) const; void reset ( ) const; bool current_element_valid ( ) const; const S& element ( ) const; const S& element ( ); bool move_next ( ) const; unsigned long size ( ) const; unsigned long get_selected ( ) const; void set_main_font ( const shared_ptr_thread_safe<font>& f ); private: void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void draw ( const canvas& c ) const; template <typename SS> struct data { SS name; bool is_selected; unsigned long width; unsigned long height; }; bool ms_enabled; typename array<data<S> >::kernel_2a_c items; member_function_pointer<unsigned long>::kernel_1a event_handler; member_function_pointer<unsigned long>::kernel_1a single_click_event_handler; unsigned long last_selected; scoped_ptr<list_box_style> style; // restricted functions list_box(list_box&); // copy constructor list_box& operator=(list_box&); // assignment operator }; } typedef list_box_helper::list_box<std::string> list_box; typedef list_box_helper::list_box<std::wstring> wlist_box; typedef list_box_helper::list_box<dlib::ustring> ulist_box; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // function open_file_box() // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- namespace open_file_box_helper { class box_win : public drawable_window { public: box_win ( const std::string& title, bool has_text_field = false ); ~box_win ( ); template < typename T > void set_click_handler ( T& object, void (T::*event_handler_)(const std::string&) ) { auto_mutex M(wm); event_handler.set(object,event_handler_); } private: void set_sizes( ); void on_window_resized ( ); void deleter_thread ( ); void enter_folder ( const std::string& folder_name ); void on_dirs_click ( unsigned long idx ); void on_files_click ( unsigned long idx ); void on_files_double_click ( unsigned long ); void on_cancel_click ( ); void on_open_click ( ); void on_path_button_click ( toggle_button& btn ); bool set_dir ( const std::string& dir ); void on_root_click ( ); on_close_return_code on_window_close ( ); label lbl_dirs; label lbl_files; label lbl_file_name; list_box lb_dirs; list_box lb_files; button btn_ok; button btn_cancel; toggle_button btn_root; text_field tf_file_name; std::string path; std::string prefix; int cur_dir; member_function_pointer<const std::string&>::kernel_1a event_handler; sequence<scoped_ptr<toggle_button> >::kernel_2a_c sob; }; } template < typename T > void open_file_box ( T& object, void (T::*event_handler)(const std::string&) ) { using namespace open_file_box_helper; box_win* win = new box_win("Open File",true); win->set_click_handler(object,event_handler); } template < typename T > void open_existing_file_box ( T& object, void (T::*event_handler)(const std::string&) ) { using namespace open_file_box_helper; box_win* win = new box_win("Open File"); win->set_click_handler(object,event_handler); } template < typename T > void save_file_box ( T& object, void (T::*event_handler)(const std::string&) ) { using namespace open_file_box_helper; box_win* win = new box_win("Save File",true); win->set_click_handler(object,event_handler); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class menu_bar // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class menu_bar : public drawable { /*! INITIAL VALUE - menus.size() == 0 - open_menu == 0 CONVENTION - size() == menus.size() - all menu data is stored in menus - menus[x].name == the name of the xth menu - if (menus[x].underline_pos != std::string::npos) then - menus[x].underline_pos == the position of the character in the menu name that should be underlined - menus[x].underline_p1 != menus[x].underline_p2 and these two points define the underline bar - else - menus[x].underline_p1 == menus[x].underline_p2 - menus[x].menu == menu(x) - menus[x].rect == the rectangle in which menus[x].name is drawn - menus[x].bgrect == the rectangle for the xth menu button - if (there is an open menu on the screen) then - open_menu == the index of the open menu from menus - else - open_menu == menus.size() !*/ public: menu_bar( drawable_window& w ); ~menu_bar(); // this function does nothing void set_pos(long,long){} void set_main_font ( const shared_ptr_thread_safe<font>& f ); void set_number_of_menus ( unsigned long num ); unsigned long number_of_menus ( ) const; void set_menu_name ( unsigned long idx, const std::string name, char underline_ch = '\0' ); void set_menu_name ( unsigned long idx, const std::wstring name, char underline_ch = '\0' ); void set_menu_name ( unsigned long idx, const dlib::ustring name, char underline_ch = '\0' ); const std::string menu_name ( unsigned long idx ) const; const std::wstring menu_wname ( unsigned long idx ) const; const dlib::ustring menu_uname ( unsigned long idx ) const; popup_menu& menu ( unsigned long idx ); const popup_menu& menu ( unsigned long idx ) const; protected: void on_window_resized ( ); void draw ( const canvas& c ) const; void on_window_moved ( ); void on_focus_lost ( ); void on_mouse_down ( unsigned long btn, unsigned long , long x, long y, bool ); void on_mouse_move ( unsigned long , long x, long y ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); private: void show_menu ( unsigned long i ); void hide_menu ( ); void on_popup_hide ( ); void compute_menu_geometry ( ); void adjust_position ( ); struct menu_data { menu_data():underline_pos(dlib::ustring::npos){} dlib::ustring name; dlib::ustring::size_type underline_pos; popup_menu menu; rectangle rect; rectangle bgrect; point underline_p1; point underline_p2; }; array<menu_data>::kernel_2a_c menus; unsigned long open_menu; // restricted functions menu_bar(menu_bar&); // copy constructor menu_bar& operator=(menu_bar&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class directed_graph_drawer // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template <typename graph_type> class directed_graph_drawer : public zoomable_region { /*! INITIAL VALUE - edge_selected == false - mouse_drag == false - selected_node == 0 - graph_.number_of_nodes() == 0 - external_graph.number_of_nodes() == 0 - radius == 25 - last_mouse_click_in_display == false CONVENTION - radius == the radius of the nodes when they aren't zoomed - external_graph and graph_ have the same graph structure - external_graph == graph() - external_graph.node(i) == graph_node(i) - if (one of the nodes is selected) then - selected_node < graph_.number_of_nodes() - graph_.node(selected_node) == the selected node - else - selected_node == graph_.number_of_nodes() - if (the user is dragging a node with the mouse) then - mouse_drag == true - drag_offset == the vector from the mouse position to the center of the node - else - mouse_drag == false - if (the user has selected an edge) then - edge_selected == true - the parent node is graph_.node(selected_edge_parent) - the child node is graph_.node(selected_edge_parent) - else - edge_selected == false - for all valid i: - graph_.node(i).data.p == the center of the node in graph space - graph_.node(i).data.name == node_label(i) - graph_.node(i).data.color == node_color(i) - graph_.node(i).data.str_rect == a rectangle sized to contain graph_.node(i).data.name - if (the last mouse click in our parent window as in our display_rect_ ) then - last_mouse_click_in_display == true - else - last_mouse_click_in_display == false !*/ public: directed_graph_drawer ( drawable_window& w ) : zoomable_region(w,MOUSE_CLICK | MOUSE_WHEEL | KEYBOARD_EVENTS), radius(25), edge_selected(false), last_mouse_click_in_display(false) { mouse_drag = false; selected_node = 0; // Whenever you make your own drawable (or inherit from draggable or button_action) // you have to remember to call this function to enable the events. The idea // here is that you can perform whatever setup you need to do to get your // object into a valid state without needing to worry about event handlers // triggering before you are ready. enable_events(); } ~directed_graph_drawer ( ) { // Disable all further events for this drawable object. We have to do this // because we don't want draw() events coming to this object while or after // it has been destructed. disable_events(); // Tell the parent window to redraw its area that previously contained this // drawable object. parent.invalidate_rectangle(rect); } void clear_graph ( ) { auto_mutex M(m); graph_.clear(); external_graph.clear(); parent.invalidate_rectangle(display_rect()); } const typename graph_type::node_type& graph_node ( unsigned long i ) const { DLIB_ASSERT ( i < number_of_nodes() , "\tgraph_type::node_type& directed_graph_drawer::graph_node(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return external_graph.node(i); } typename graph_type::node_type& graph_node ( unsigned long i ) { DLIB_ASSERT ( i < number_of_nodes() , "\tgraph_type::node_type& directed_graph_drawer::graph_node(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return external_graph.node(i); } const graph_type& graph ( ) const { return external_graph; } void save_graph ( std::ostream& out ) { auto_mutex M(m); serialize(external_graph, out); serialize(graph_, out); parent.invalidate_rectangle(display_rect()); } void load_graph ( std::istream& in ) { auto_mutex M(m); deserialize(external_graph, in); deserialize(graph_, in); parent.invalidate_rectangle(display_rect()); } unsigned long number_of_nodes ( ) const { auto_mutex M(m); return graph_.number_of_nodes(); } void set_node_label ( unsigned long i, const std::string& label ) { set_node_label(i, convert_mbstring_to_wstring(label)); } void set_node_label ( unsigned long i, const std::wstring& label ) { set_node_label(i, convert_wstring_to_utf32(label)); } void set_node_label ( unsigned long i, const dlib::ustring& label ) { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tvoid directed_graph_drawer::set_node_label(i,label)" << "\n\ti: " << i << "\n\tlabel: " << narrow(label) << "\n\tnumber_of_nodes(): " << number_of_nodes() ); graph_.node(i).data.name = label.c_str(); unsigned long width, height; mfont->compute_size(label,width,height); graph_.node(i).data.str_rect = rectangle(width,height); parent.invalidate_rectangle(display_rect()); } void set_node_color ( unsigned long i, rgb_pixel color ) { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tvoid directed_graph_drawer::set_node_color(i,label)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); graph_.node(i).data.color = color; parent.invalidate_rectangle(display_rect()); } rgb_pixel node_color ( unsigned long i ) const { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\trgb_pixel directed_graph_drawer::node_color(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return graph_.node(i).data.color; } const std::string node_label ( unsigned long i ) const { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tconst std::ustring directed_graph_drawer::node_label(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return narrow(graph_.node(i).data.name); } const std::wstring node_wlabel ( unsigned long i ) const { return convert_utf32_to_wstring(node_ulabel(i)); } const dlib::ustring node_ulabel ( unsigned long i ) const { auto_mutex M(m); DLIB_ASSERT ( i < number_of_nodes() , "\tconst std::ustring directed_graph_drawer::node_label(i)" << "\n\ti: " << i << "\n\tnumber_of_nodes(): " << number_of_nodes() ); return graph_.node(i).data.name.c_str(); } template < typename T > void set_node_selected_handler ( T& object, void (T::*event_handler_)(unsigned long) ) { auto_mutex M(m); node_selected_handler.set(object,event_handler_); } template < typename T > void set_node_deselected_handler ( T& object, void (T::*event_handler_)(unsigned long) ) { auto_mutex M(m); node_deselected_handler.set(object,event_handler_); } template < typename T > void set_node_deleted_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(m); node_deleted_handler.set(object,event_handler_); } template < typename T > void set_graph_modified_handler ( T& object, void (T::*event_handler_)() ) { auto_mutex M(m); graph_modified_handler.set(object,event_handler_); } protected: void on_keydown ( unsigned long key, bool , unsigned long ) { // ignore all keyboard input if the last thing the user clicked on // wasn't the display area if (last_mouse_click_in_display == false) return; // if a node is selected if (selected_node != graph_.number_of_nodes()) { // deselect the node if the user hits escape if (key == base_window::KEY_ESC) { parent.invalidate_rectangle(display_rect()); if (node_deselected_handler.is_set()) node_deselected_handler(selected_node); selected_node = graph_.number_of_nodes(); } // delete the node if the user hits delete if (key == base_window::KEY_DELETE || key == base_window::KEY_BACKSPACE) { parent.invalidate_rectangle(display_rect()); graph_.remove_node(selected_node); external_graph.remove_node(selected_node); selected_node = graph_.number_of_nodes(); mouse_drag = false; if (graph_modified_handler.is_set()) graph_modified_handler(); if (node_deleted_handler.is_set()) node_deleted_handler(); } } // if an edge is selected if (edge_selected) { // deselect the node if the user hits escape if (key == base_window::KEY_ESC) { parent.invalidate_rectangle(display_rect()); edge_selected = false; } // delete the node if the user hits delete if (key == base_window::KEY_DELETE || key == base_window::KEY_BACKSPACE) { parent.invalidate_rectangle(display_rect()); graph_.remove_edge(selected_edge_parent, selected_edge_child); external_graph.remove_edge(selected_edge_parent, selected_edge_child); edge_selected = false; if (graph_modified_handler.is_set()) graph_modified_handler(); } } } void on_mouse_move ( unsigned long state, long x, long y ) { if (mouse_drag) { const point p(nearest_point(display_rect(),point(x,y))); point center = drag_offset + p; graph_.node(selected_node).data.p = gui_to_graph_space(center); parent.invalidate_rectangle(display_rect()); } else { zoomable_region::on_mouse_move(state,x,y); } // check if the mouse isn't being dragged anymore if ((state & base_window::LEFT) == 0) { mouse_drag = false; } } void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ) { mouse_drag = false; zoomable_region::on_mouse_up(btn,state,x,y); } void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ) { bool redraw = false; if (display_rect().contains(x,y) && (btn == base_window::RIGHT || btn == base_window::LEFT) && (state & base_window::SHIFT) == 0 ) { // start out saying no edge is selected if (edge_selected) { edge_selected = false; redraw = true; } bool click_hit_node = false; dlib::vector<double,2> p(gui_to_graph_space(point(x,y))); // check if this click is on an existing node for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { dlib::vector<double,2> n(graph_.node(i).data.p); if ((p-n).length() < radius) { click_hit_node = true; point center = graph_to_gui_space(graph_.node(i).data.p); mouse_drag = true; drag_offset = center - point(x,y); // only do something if the click isn't on the currently // selected node if (selected_node != i) { // send out the deselected event if appropriate if (selected_node != graph_.number_of_nodes() && node_deselected_handler.is_set()) node_deselected_handler(selected_node); selected_node = i; redraw = true; if (node_selected_handler.is_set()) node_selected_handler(selected_node); } break; } } // if the click didn't hit any node then make sure nothing is selected if (click_hit_node == false && selected_node != graph_.number_of_nodes()) { if (node_deselected_handler.is_set()) node_deselected_handler(selected_node); selected_node = graph_.number_of_nodes(); redraw = true; } // check if this click is on an edge if we didn't click on a node if (click_hit_node == false) { for (unsigned long n = 0; n < graph_.number_of_nodes() && edge_selected == false; ++n) { const dlib::vector<double,2> parent_center(graph_to_gui_space(graph_.node(n).data.p)); for (unsigned long e = 0; e < graph_.node(n).number_of_children() && edge_selected == false; ++e) { const dlib::vector<double,2> child_center(graph_to_gui_space(graph_.node(n).child(e).data.p)); rectangle area; area += parent_center; area += child_center; // if the point(x,y) is between the two nodes then lets consider it further if (area.contains(point(x,y))) { p = point(x,y); const dlib::vector<double> z(0,0,1); // find the distance from the line between the two nodes const dlib::vector<double,2> perpendicular(z.cross(parent_center-child_center).normalize()); double distance = std::abs((child_center-p).dot(perpendicular)); if (distance < 8) { edge_selected = true; selected_edge_parent = n; selected_edge_child = graph_.node(n).child(e).index(); redraw = true; } } } } } // if the click didn't land on any node then add a new one if this was // a right mouse button click if (click_hit_node == false && btn == base_window::RIGHT) { const unsigned long n = graph_.add_node(); external_graph.add_node(); graph_.node(n).data.p = gui_to_graph_space(point(x,y)); redraw = true; selected_node = n; mouse_drag = false; if (graph_modified_handler.is_set()) graph_modified_handler(); if (node_selected_handler.is_set()) node_selected_handler(selected_node); } else if (selected_node == graph_.number_of_nodes()) { // in this case the click landed in the white area between nodes zoomable_region::on_mouse_down( btn, state, x, y, is_double_click); } } // If the user is shift clicking with the mouse then see if we // should add a new edge. if (display_rect().contains(x,y) && btn == base_window::LEFT && (state & base_window::SHIFT) && selected_node != graph_.number_of_nodes() ) { dlib::vector<double,2> p(gui_to_graph_space(point(x,y))); // check if this click is on an existing node for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { dlib::vector<double,2> n(graph_.node(i).data.p); if ((p-n).length() < radius) { // add the edge if it doesn't already exist and isn't an edge back to // the same node if (graph_.has_edge(selected_node,i) == false && selected_node != i && graph_.has_edge(i, selected_node) == false) { graph_.add_edge(selected_node,i); external_graph.add_edge(selected_node,i); redraw = true; if (graph_modified_handler.is_set()) graph_modified_handler(); } break; } } } if (redraw) parent.invalidate_rectangle(display_rect()); if (display_rect().contains(x,y) == false) last_mouse_click_in_display = false; else last_mouse_click_in_display = true; } void draw ( const canvas& c ) const { zoomable_region::draw(c); rectangle area = c.intersect(display_rect()); if (area.is_empty() == true) return; if (enabled) fill_rect(c,display_rect(),255); else fill_rect(c,display_rect(),128); const unsigned long rad = static_cast<unsigned long>(radius*zoom_scale()); point center; // first draw all the edges for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { center = graph_to_gui_space(graph_.node(i).data.p); const rectangle circle_area(centered_rect(center,2*(rad+8),2*(rad+8))); // draw lines to all this node's parents const dlib::vector<double> z(0,0,1); for (unsigned long j = 0; j < graph_.node(i).number_of_parents(); ++j) { point p(graph_to_gui_space(graph_.node(i).parent(j).data.p)); rgb_pixel color(0,0,0); // if this is the selected edge then draw it with red instead of black if (edge_selected && selected_edge_child == i && selected_edge_parent == graph_.node(i).parent(j).index()) { color.red = 255; // we need to be careful when drawing this line to not draw it over the node dots since it // has a different color from them and would look weird dlib::vector<double,2> v(p-center); v = v.normalize()*rad; draw_line(c,center+v,p-v ,color, area); } else { draw_line(c,center,p ,color, area); } // draw the triangle pointing to this node if (area.intersect(circle_area).is_empty() == false) { dlib::vector<double,2> v(p-center); v = v.normalize(); dlib::vector<double,2> cross = z.cross(v).normalize(); dlib::vector<double,2> r(center + v*rad); for (double i = 0; i < 8*zoom_scale(); i += 0.1) draw_line(c,(r+v*i)+cross*i, (r+v*i)-cross*i,color,area); } } } // now draw all the node dots for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { center = graph_to_gui_space(graph_.node(i).data.p); const rectangle circle_area(centered_rect(center,2*(rad+8),2*(rad+8))); // draw the actual dot for this node if (area.intersect(circle_area).is_empty()==false) { rgb_alpha_pixel color; assign_pixel(color, graph_.node(i).data.color); // this node is in area so lets draw it and all of it's edges as well draw_solid_circle(c,center,rad-3,color,area); color.alpha = 240; draw_circle(c,center,rad-3,color,area); color.alpha = 200; draw_circle(c,center,rad-2.5,color,area); color.alpha = 160; draw_circle(c,center,rad-2.0,color,area); color.alpha = 120; draw_circle(c,center,rad-1.5,color,area); color.alpha = 80; draw_circle(c,center,rad-1.0,color,area); color.alpha = 40; draw_circle(c,center,rad-0.5,color,area); } if (i == selected_node) draw_circle(c,center,rad+5,rgb_pixel(0,0,255),area); } // now draw all the strings last for (unsigned long i = 0; i < graph_.number_of_nodes(); ++i) { center = graph_to_gui_space(graph_.node(i).data.p); rectangle circle_area(centered_rect(center,2*rad+3,2*rad+3)); if (area.intersect(circle_area).is_empty()==false) { rgb_pixel color = graph_.node(i).data.color; // invert this color color.red = 255-color.red; color.green = 255-color.green; color.blue = 255-color.blue; sout << i; unsigned long width, height; mfont->compute_size(sout.str(),width,height); rectangle str_rect(centered_rect(center, width,height)); if (circle_area.contains(str_rect)) { mfont->draw_string(c,str_rect,sout.str(),color,0,std::string::npos,area); // draw the label for this node if it isn't empty if(graph_.node(i).data.name.size() > 0) { rectangle str_rect(graph_.node(i).data.str_rect); str_rect = centered_rect(center.x(), center.y()-rad-mfont->height(), str_rect.width(), str_rect.height()); mfont->draw_string(c,str_rect,graph_.node(i).data.name,0,0,std::string::npos,area); } } sout.str(""); } } } private: struct data { data() : color(0,0,0) {} vector<double> p; dlib::ustring name; rectangle str_rect; rgb_pixel color; }; friend void serialize(const data& item, std::ostream& out) { serialize(item.p, out); serialize(item.name, out); serialize(item.str_rect, out); serialize(item.color, out); } friend void deserialize(data& item, std::istream& in) { deserialize(item.p, in); deserialize(item.name, in); deserialize(item.str_rect, in); deserialize(item.color, in); } mutable std::ostringstream sout; const double radius; unsigned long selected_node; bool mouse_drag; // true if the user is dragging a node point drag_offset; bool edge_selected; unsigned long selected_edge_parent; unsigned long selected_edge_child; member_function_pointer<unsigned long>::kernel_1a node_selected_handler; member_function_pointer<unsigned long>::kernel_1a node_deselected_handler; member_function_pointer<>::kernel_1a node_deleted_handler; member_function_pointer<>::kernel_1a graph_modified_handler; graph_type external_graph; // rebind the graph_ type to make us a graph_ of data structs typename graph_type::template rebind<data,char, typename graph_type::mem_manager_type>::other graph_; bool last_mouse_click_in_display; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // class text_grid // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class text_grid : public scrollable_region { /*! INITIAL VALUE - has_focus == false - vertical_scroll_increment() == 10 - horizontal_scroll_increment() == 10 - border_color_ == rgb_pixel(128,128,128) CONVENTION - grid.nr() == row_height.size() - grid.nc() == col_width.size() - border_color() == border_color_ - text(r,c) == grid[r][c].text - text_color(r,c) == grid[r][c].text_color - background_color(r,c) == grid[r][c].bg_color - if (the user has clicked on this widget and caused one of the boxes to have input focus) then - has_focus == true - grid[active_row][active_col] == the active text box - cursor_pos == the position of the cursor in the above box - if (the cursor should be displayed) then - show_cursor == true - else - show_cursor == false - else - has_focus == false !*/ public: text_grid ( drawable_window& w ); ~text_grid ( ); void set_grid_size ( unsigned long rows, unsigned long cols ); unsigned long number_of_columns ( ) const; unsigned long number_of_rows ( ) const; int next_free_user_event_number ( ) const; rgb_pixel border_color ( ) const; void set_border_color ( rgb_pixel color ); const std::string text ( unsigned long row, unsigned long col ) const; const std::wstring wtext ( unsigned long row, unsigned long col ) const; const dlib::ustring utext ( unsigned long row, unsigned long col ) const; void set_text ( unsigned long row, unsigned long col, const std::string& str ); void set_text ( unsigned long row, unsigned long col, const std::wstring& str ); void set_text ( unsigned long row, unsigned long col, const dlib::ustring& str ); const rgb_pixel text_color ( unsigned long row, unsigned long col ) const; void set_text_color ( unsigned long row, unsigned long col, const rgb_pixel color ); const rgb_pixel background_color ( unsigned long row, unsigned long col ) const; void set_background_color ( unsigned long row, unsigned long col, const rgb_pixel color ); bool is_editable ( unsigned long row, unsigned long col ) const; void set_editable ( unsigned long row, unsigned long col, bool editable ); void set_column_width ( unsigned long col, unsigned long width ); void set_row_height ( unsigned long row, unsigned long height ); void disable ( ); void hide ( ); template < typename T > void set_text_modified_handler ( T& object, void (T::*eh)(unsigned long, unsigned long) ) { text_modified_handler.set(object,eh); } private: void on_user_event ( int num ); void timer_action ( ); /*! ensures - flips the state of show_cursor !*/ void compute_bg_rects ( ); void compute_total_rect ( ); void on_keydown ( unsigned long key, bool is_printable, unsigned long state ); void on_mouse_down ( unsigned long btn, unsigned long state, long x, long y, bool is_double_click ); void on_mouse_up ( unsigned long btn, unsigned long state, long x, long y ); void on_focus_lost ( ); void draw ( const canvas& c ) const; rectangle get_text_rect ( unsigned long row, unsigned long col ) const; rectangle get_bg_rect ( unsigned long row, unsigned long col ) const; struct data_type { data_type(): text_color(0,0,0), bg_color(255,255,255), first(0), is_editable(true) {} dlib::ustring text; rgb_pixel text_color; rgb_pixel bg_color; rectangle bg_rect; dlib::ustring::size_type first; bool is_editable; }; void drop_input_focus ( ); void move_cursor ( long row, long col, long new_cursor_pos ); array2d<data_type>::kernel_1a_c grid; array<unsigned long>::kernel_2a_c col_width; array<unsigned long>::kernel_2a_c row_height; bool has_focus; long active_col; long active_row; long cursor_pos; bool show_cursor; bool recent_cursor_move; timer<text_grid>::kernel_2a cursor_timer; rgb_pixel border_color_; member_function_pointer<unsigned long, unsigned long>::kernel_1a_c text_modified_handler; }; // ---------------------------------------------------------------------------------------- class image_display : public scrollable_region { /*! INITIAL VALUE - img.size() == 0 - overlay_rects.size() == 0 - overlay_lines.size() == 0 CONVENTION - img == the image this object displays - overlay_rects == the overlay rectangles this object displays - overlay_lines == the overlay lines this object displays !*/ public: image_display( drawable_window& w ); ~image_display( ); template < typename image_type > void set_image ( const image_type& new_img ) { auto_mutex M(m); // if the new image has a different size when compared to the previous image // then we should readjust the total rectangle size. if (new_img.nr() != img.nr() || new_img.nc() != img.nc()) set_total_rect_size(new_img.nc(), new_img.nr()); else parent.invalidate_rectangle(rect); assign_image_scaled(img,new_img); } struct overlay_rect { overlay_rect() { assign_pixel(color, 0);} template <typename pixel_type> overlay_rect(const rectangle& r, pixel_type p) : rect(r) { assign_pixel(color, p); } template <typename pixel_type> overlay_rect(const rectangle& r, pixel_type p, const std::string& l) : rect(r),label(l) { assign_pixel(color, p); } rectangle rect; rgb_alpha_pixel color; std::string label; }; struct overlay_line { overlay_line() { assign_pixel(color, 0);} template <typename pixel_type> overlay_line(const point& p1_, const point& p2_, pixel_type p) : p1(p1_), p2(p2_) { assign_pixel(color, p); } point p1; point p2; rgb_alpha_pixel color; }; void add_overlay ( const overlay_rect& overlay ); void add_overlay ( const overlay_line& overlay ); void add_overlay ( const std::vector<overlay_rect>& overlay ); void add_overlay ( const std::vector<overlay_line>& overlay ); void clear_overlay ( ); private: void draw ( const canvas& c ) const; array2d<rgb_alpha_pixel>::kernel_1a img; std::vector<overlay_rect> overlay_rects; std::vector<overlay_line> overlay_lines; // restricted functions image_display(image_display&); // copy constructor image_display& operator=(image_display&); // assignment operator }; // ---------------------------------------------------------------------------------------- class image_window : public drawable_window { public: typedef image_display::overlay_rect overlay_rect; typedef image_display::overlay_line overlay_line; image_window( ); template < typename image_type > image_window( const image_type& img ) : gui_img(*this), nr(0), nc(0) { set_image(img); show(); } ~image_window( ); template < typename image_type > void set_image ( const image_type& img ) { const unsigned long padding = scrollable_region_style_default().get_border_size(); auto_mutex M(wm); gui_img.set_image(img); // Only readjust the size of the window if the new image has a different size // than the last image given to this object. if (img.nr() != nr || img.nc() != nc) { // set the size of this window to match the size of the input image set_size(img.nc()+padding*2,img.nr()+padding*2); // call this to make sure everything else is setup properly on_window_resized(); nr = img.nr(); nc = img.nc(); } } void add_overlay ( const overlay_rect& overlay ); void add_overlay ( const overlay_line& overlay ); void add_overlay ( const std::vector<overlay_rect>& overlay ); void add_overlay ( const std::vector<overlay_line>& overlay ); void clear_overlay ( ); private: void on_window_resized( ); // restricted functions image_window(image_window&); image_window& operator= (image_window&); image_display gui_img; long nr, nc; }; // ---------------------------------------------------------------------------------------- } #ifdef NO_MAKEFILE #include "widgets.cpp" #endif #endif // DLIB_WIDGETs_