1 /* Change Display
2  *
3  * Demonstrates migrating a window between different displays and
4  * screens. A display is a mouse and keyboard with some number of
5  * associated monitors. A screen is a set of monitors grouped
6  * into a single physical work area. The neat thing about having
7  * multiple displays is that they can be on a completely separate
8  * computers, as long as there is a network connection to the
9  * computer where the application is running.
10  *
11  * Only some of the windowing systems where GTK+ runs have the
12  * concept of multiple displays and screens. (The X Window System
13  * is the main example.) Other windowing systems can only
14  * handle one keyboard and mouse, and combine all monitors into
15  * a single screen.
16  *
17  * This is a moderately complex example, and demonstrates:
18  *
19  *  - Tracking the currently open displays and screens
20  *
21  *  - Changing the screen for a window
22  *
23  *  - Letting the user choose a window by clicking on it
24  *
25  *  - Using Gtk::ListStore and Gtk::TreeView
26  *
27  *  - Using Gtk::Dialog
28  */
29 
30 #include <gtkmm.h>
31 
32 
33 class Popup : public Gtk::Window
34 {
35 public:
36   Popup(const Glib::RefPtr<Gdk::Screen> screen, const Glib::ustring& prompt);
37   virtual ~Popup();
38 
39 protected:
40   Gtk::Frame m_Frame;
41   Gtk::Label m_Label;
42 };
43 
44 class Example_ChangeDisplay : public Gtk::Dialog
45 {
46 public:
47   Example_ChangeDisplay();
48   virtual ~Example_ChangeDisplay();
49 
50 protected:
51   virtual void setup_frame(Gtk::Frame& frame, Gtk::TreeView& treeview, Gtk::Box& buttonbox);
52 
53   virtual void initialize_displays();
54   virtual void fill_screens();
55   virtual void query_change_display();
56   virtual Gtk::Widget* find_toplevel_at_pointer(const Glib::RefPtr<Gdk::Display>& display);
57   virtual Gtk::Window* query_for_toplevel(const Glib::RefPtr<Gdk::Screen>& screen, const Glib::ustring& prompt);
58 
59   //signal handlers:
60   virtual void on_button_display_open();
61   virtual void on_button_display_close();
62 
63   virtual void on_treeview_display_selection_changed();
64   virtual void on_treeview_screen_selection_changed();
65 
66   virtual void on_display_closed(bool is_error, Glib::RefPtr<Gdk::Display> display);
67 
68   virtual bool on_popup_button_release_event(GdkEventButton* event);
69 
70   virtual void on_response(int response_id);
71 
72 
73   class ModelColumns_Display : public Gtk::TreeModelColumnRecord
74   {
75   public:
76     Gtk::TreeModelColumn<Glib::ustring> m_name;
77     Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Display> > m_display; //hidden
78 
ModelColumns_Display()79     ModelColumns_Display() { add(m_name); add(m_display); }
80   };
81   ModelColumns_Display m_columns_display;
82 
83   class ModelColumns_Screen : public Gtk::TreeModelColumnRecord
84   {
85   public:
86     Gtk::TreeModelColumn<int> m_number;
87     Gtk::TreeModelColumn< Glib::RefPtr<Gdk::Screen> > m_screen; //hidden
88 
ModelColumns_Screen()89     ModelColumns_Screen() { add(m_number); add(m_screen); }
90   };
91   ModelColumns_Screen m_columns_screen;
92 
93   Gtk::VBox m_VBox;
94   Gtk::Frame m_Frame_Display, m_Frame_Screen;
95   Gtk::TreeView m_TreeView_Display, m_TreeView_Screen;
96   Glib::RefPtr<Gtk::ListStore> m_refListStore_Display,  m_refListStore_Screen;
97   Gtk::VBox m_ButtonBox_Display, m_ButtonBox_Screen;
98 
99   Gtk::Button m_Button_Display_Open, m_Button_Display_Close;
100 
101   Glib::RefPtr<Gtk::SizeGroup> m_refSizeGroup_Display, m_refSizeGroup_Screen;
102 
103 
104   Glib::RefPtr<Gdk::Display> m_refCurrentDisplay;
105   Glib::RefPtr<Gdk::Screen> m_refCurrentScreen;
106 
107   Popup* m_pPopup;
108 
109   bool m_popup_clicked;
110 };
111 
112 
113 
114 
Example_ChangeDisplay()115 Example_ChangeDisplay::Example_ChangeDisplay()
116 : Gtk::Dialog("Change Screen or display"),
117   m_VBox(false, 5),
118   m_Frame_Display("Display"),
119   m_Frame_Screen("Screen"),
120   m_ButtonBox_Display(false, 5), m_ButtonBox_Screen(false, 5),
121   m_Button_Display_Open("_Open...", true), m_Button_Display_Close("_Close...", true),
122   m_pPopup(0),
123   m_popup_clicked(false)
124 {
125   add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE);
126   add_button("Change", Gtk::RESPONSE_OK);
127 
128   set_default_size(300, 400);
129 
130   m_VBox.set_border_width(8);
131   get_vbox()->pack_start(m_VBox);
132 
133 
134   //Display:
135   {
136     setup_frame(m_Frame_Display, m_TreeView_Display, m_ButtonBox_Display);
137     m_ButtonBox_Display.pack_start(m_Button_Display_Open, Gtk::PACK_SHRINK);
138     m_Button_Display_Open.signal_clicked().connect( sigc::mem_fun(*this, &Example_ChangeDisplay::on_button_display_open) );
139     m_ButtonBox_Display.pack_start(m_Button_Display_Close, Gtk::PACK_SHRINK);
140     m_Button_Display_Close.signal_clicked().connect( sigc::mem_fun(*this, &Example_ChangeDisplay::on_button_display_close) );
141 
142     //Setup TreeView:
143     m_refListStore_Display = Gtk::ListStore::create(m_columns_display);
144     m_TreeView_Display.set_model(m_refListStore_Display);
145     m_TreeView_Display.append_column("Name", m_columns_display.m_name);
146 
147     //Connect signal:
148     Glib::RefPtr<Gtk::TreeView::Selection> refSelection = m_TreeView_Display.get_selection();
149     refSelection->signal_changed().connect( sigc::mem_fun(*this, &Example_ChangeDisplay::on_treeview_display_selection_changed) );
150 
151     m_VBox.pack_start(m_Frame_Display);
152 
153     m_refSizeGroup_Display = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
154     m_refSizeGroup_Display->add_widget(m_ButtonBox_Display);
155   }
156 
157   //Screen:
158   {
159     setup_frame(m_Frame_Screen, m_TreeView_Screen, m_ButtonBox_Screen);
160 
161     //Setup TreeView:
162     m_refListStore_Screen = Gtk::ListStore::create(m_columns_screen);
163     m_TreeView_Screen.set_model(m_refListStore_Screen);
164     m_TreeView_Screen.append_column("Number", m_columns_screen.m_number);
165 
166     //Connect signal:
167     Glib::RefPtr<Gtk::TreeView::Selection> refSelection = m_TreeView_Screen.get_selection();
168     refSelection->signal_changed().connect( sigc::mem_fun(*this, &Example_ChangeDisplay::on_treeview_screen_selection_changed) );
169 
170     m_VBox.pack_start(m_Frame_Screen);
171 
172     m_refSizeGroup_Screen = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
173     m_refSizeGroup_Screen->add_widget(m_ButtonBox_Screen);
174   }
175 
176   initialize_displays();
177 
178   show_all();
179 }
180 
~Example_ChangeDisplay()181 Example_ChangeDisplay::~Example_ChangeDisplay()
182 {
183   if(m_pPopup)
184   {
185     delete m_pPopup;
186     m_pPopup = 0;
187   }
188 }
189 
setup_frame(Gtk::Frame & frame,Gtk::TreeView & treeview,Gtk::Box & buttonbox)190 void Example_ChangeDisplay::setup_frame(Gtk::Frame& frame, Gtk::TreeView& treeview, Gtk::Box& buttonbox)
191 {
192   Gtk::HBox* pHBox = Gtk::manage( new Gtk::HBox(false, 8) );
193   pHBox->set_border_width(8);
194   frame.add(*pHBox);
195 
196   Gtk::ScrolledWindow* pScrolledWindow = Gtk::manage( new Gtk::ScrolledWindow() );
197   pScrolledWindow->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
198   pScrolledWindow->set_shadow_type(Gtk::SHADOW_IN);
199   pHBox->pack_start(*pScrolledWindow);
200 
201   treeview.set_headers_visible(false);
202   pScrolledWindow->add(treeview);
203 
204   Glib::RefPtr<Gtk::TreeView::Selection> refSelection = treeview.get_selection();
205   refSelection->set_mode(Gtk::SELECTION_BROWSE);
206 
207   pHBox->pack_start(buttonbox, Gtk::PACK_SHRINK);
208 }
209 
initialize_displays()210 void Example_ChangeDisplay::initialize_displays()
211 {
212 
213 #ifndef G_OS_WIN32
214   Glib::RefPtr<Gdk::DisplayManager> refDisplayManager = Gdk::DisplayManager::get();
215 
216   typedef std::list< Glib::RefPtr<Gdk::Display> > type_listDisplays;
217   type_listDisplays listDisplays = refDisplayManager->list_displays();
218 
219   for(type_listDisplays::iterator iter = listDisplays.begin(); iter != listDisplays.end(); ++iter)
220   {
221     Glib::RefPtr<Gdk::Display> refDisplay = *iter;
222 
223     Gtk::TreeRow row = *(m_refListStore_Display->append());
224     row[m_columns_display.m_name] = refDisplay->get_name();;
225     row[m_columns_display.m_display] = refDisplay;
226 
227     refDisplay->signal_closed().connect(
228       sigc::bind<-1>( sigc::mem_fun(*this, &Example_ChangeDisplay::on_display_closed), refDisplay) );
229   }
230 
231 #endif
232 }
233 
on_display_closed(bool,Glib::RefPtr<Gdk::Display> display)234 void Example_ChangeDisplay::on_display_closed(bool /* is_error */, Glib::RefPtr<Gdk::Display> display)
235 {
236   Gtk::TreeModel::Children children = m_refListStore_Display->children();
237   for(Gtk::TreeModel::iterator iter = children.begin(); iter != children.end(); ++iter)
238   {
239     Glib::RefPtr<Gdk::Display> refDisplay = (*iter)[m_columns_display.m_display];
240     if(refDisplay == display)
241     {
242       m_refListStore_Display->erase(iter);
243     }
244   }
245 }
246 
on_button_display_open()247 void Example_ChangeDisplay::on_button_display_open()
248 {
249   Gtk::Dialog dialog("Open Display", true);
250   dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
251   dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
252 
253   dialog.set_default_response(Gtk::RESPONSE_OK);
254 
255   Gtk::Entry entry;
256   entry.set_activates_default();
257 
258   Gtk::Label label("Please enter the name of\nthe new display\n");
259 
260   dialog.get_vbox()->add(label);
261   dialog.get_vbox()->add(entry);
262 
263   entry.grab_focus();
264 
265   dialog.show_all_children();
266 
267   Glib::RefPtr<Gdk::Display> refResult;
268   while (!refResult)
269   {
270     gint response_id = dialog.run();
271     if (response_id != Gtk::RESPONSE_OK)
272       break;
273 
274     Glib::ustring new_screen_name = entry.get_text();
275 
276     if( !new_screen_name.empty() )
277     {
278       refResult = Gdk::Display::open(new_screen_name);
279       if (!refResult)
280       {
281         label.set_text("Can't open display.\nplease try another one\n");
282       }
283     }
284   }
285 }
286 
on_button_display_close()287 void Example_ChangeDisplay::on_button_display_close()
288 {
289   if(m_refCurrentDisplay)
290     m_refCurrentDisplay->close();
291 }
292 
on_treeview_display_selection_changed()293 void Example_ChangeDisplay::on_treeview_display_selection_changed()
294 {
295   Glib::RefPtr<Gtk::TreeSelection> refSelection = m_TreeView_Display.get_selection();
296   Gtk::TreeModel::iterator iter = refSelection->get_selected();
297   if(iter)
298     m_refCurrentDisplay = (*iter)[m_columns_display.m_display];
299   else
300     m_refCurrentDisplay.reset();
301 
302   fill_screens();
303 }
304 
on_treeview_screen_selection_changed()305 void Example_ChangeDisplay::on_treeview_screen_selection_changed()
306 {
307   Glib::RefPtr<Gtk::TreeSelection> refSelection = m_TreeView_Screen.get_selection();
308   Gtk::TreeModel::iterator iter = refSelection->get_selected();
309   if(iter)
310     m_refCurrentScreen = (*iter)[m_columns_screen.m_screen];
311   else
312     m_refCurrentScreen.reset();
313 }
314 
315 /* Fills in the screen list based on the current display
316  */
fill_screens()317 void Example_ChangeDisplay::fill_screens()
318 {
319   m_refListStore_Screen->clear();
320 
321   if(m_refCurrentScreen)
322   {
323     int n_screens = m_refCurrentDisplay->get_n_screens();
324 
325     for (int i = 0; i < n_screens; i++)
326     {
327       Glib::RefPtr<Gdk::Screen> refScreen = m_refCurrentDisplay->get_screen(i);
328 
329       Gtk::TreeModel::Row row = *(m_refListStore_Screen->append());
330       row[m_columns_screen.m_number] = i;
331       row[m_columns_screen.m_screen] = refScreen;
332 
333       if (i == 0)
334       {
335         Glib::RefPtr<Gtk::TreeSelection> refSelection = m_TreeView_Screen.get_selection();
336         refSelection->select(row);
337       }
338     }
339   }
340 }
341 
342 /* Prompts the user for a toplevel window to move, and then moves
343  * that window to the currently selected display
344  */
query_change_display()345 void Example_ChangeDisplay::query_change_display()
346 {
347   Glib::RefPtr<Gdk::Screen> refScreen = get_screen();
348   Gtk::Window* pTopLevel = query_for_toplevel(refScreen,
349    "Please select the toplevel\n"
350    "to move to the new screen");
351 
352   if (pTopLevel)
353     pTopLevel->set_screen(m_refCurrentScreen);
354   else
355     refScreen->get_display()->beep();
356 }
357 
358 
on_response(int response_id)359 void Example_ChangeDisplay::on_response(int response_id)
360 {
361   if (response_id == Gtk::RESPONSE_OK)
362     query_change_display();
363   else
364     hide();
365 }
366 
367 
368 
369 /* Asks the user to click on a window, then waits for them click
370  * the mouse. When the mouse is released, returns the toplevel
371  * window under the pointer, or NULL, if there is none.
372  */
query_for_toplevel(const Glib::RefPtr<Gdk::Screen> & screen,const Glib::ustring & prompt)373 Gtk::Window* Example_ChangeDisplay::query_for_toplevel(const Glib::RefPtr<Gdk::Screen>& screen, const Glib::ustring& prompt)
374 {
375   Glib::RefPtr<Gdk::Display> refDisplay = screen->get_display();
376 
377   if(m_pPopup)
378   {
379     delete m_pPopup;
380     m_pPopup = 0;
381   }
382 
383   m_pPopup = new Popup(screen, prompt);
384 
385   m_pPopup->show();
386 
387   Gdk::Cursor cursor(refDisplay, Gdk::CROSSHAIR);
388 
389   Gtk::Window* toplevel = 0;
390 
391   //TODO: Find a suitable replacement for this:
392   //const GdkGrabStatus grabbed =  m_pPopup->get_window()->grab(false, Gdk::BUTTON_RELEASE_MASK, cursor, GDK_CURRENT_TIME);
393   //Check it when the GTK+ example has been updated and file a bug about the unhelpful deprecation comment.
394   const Gdk::GrabStatus grabbed = Gdk::GRAB_SUCCESS;
395   if(grabbed == Gdk::GRAB_SUCCESS )
396   {
397     m_popup_clicked = false;
398     m_pPopup->signal_button_release_event().connect( sigc::mem_fun(*this, &Example_ChangeDisplay::on_popup_button_release_event) );
399 
400 
401     // Process events until clicked is set by button_release_event_cb.
402     // We pass in may_block=true since we want to wait if there
403     // are no events currently.
404     while (!m_popup_clicked)
405       Gtk::Main::iteration(true);
406 
407     toplevel = dynamic_cast<Gtk::Window*>(find_toplevel_at_pointer(screen->get_display()));
408     if (toplevel == m_pPopup)
409        toplevel = 0;
410   }
411 
412   Gdk::flush(); /* Really release the grab */
413 
414   return toplevel;
415 }
416 
417 // Finds the toplevel window under the mouse pointer, if any.
find_toplevel_at_pointer(const Glib::RefPtr<Gdk::Display> &)418 Gtk::Widget* Example_ChangeDisplay::find_toplevel_at_pointer(const Glib::RefPtr<Gdk::Display>& /* display */)
419 {
420   //TODO: This needs to use Device::get_window_at_position(), when we can figure that out.
421   //See https://bugzilla.gnome.org/show_bug.cgi?id=638907
422   /*
423   Glib::RefPtr<Gdk::Window> refPointerWindow = display->get_window_at_pointer();
424 
425   if (refPointerWindow)
426   {
427     // The user data field of a GdkWindow is used to store a pointer
428     // to the widget that created it.
429     GtkWidget* cWidget = 0;
430     gpointer* user_data = 0;
431     refPointerWindow->get_user_data(user_data);
432     cWidget = (GtkWidget*)user_data;
433 
434     Gtk::Widget* pWidget = Glib::wrap(cWidget);
435     if(pWidget)
436       return pWidget->get_toplevel();
437   }
438   */
439 
440   return 0;
441 }
442 
443 
on_popup_button_release_event(GdkEventButton *)444 bool Example_ChangeDisplay::on_popup_button_release_event(GdkEventButton* /* event */)
445 {
446   m_popup_clicked = true;
447   return true;
448 }
449 
Popup(const Glib::RefPtr<Gdk::Screen> screen,const Glib::ustring & prompt)450 Popup::Popup(const Glib::RefPtr<Gdk::Screen> screen, const Glib::ustring& prompt)
451 : Gtk::Window(Gtk::WINDOW_POPUP),
452   m_Label(prompt)
453 {
454   set_screen(screen);
455   set_modal(true);
456   set_position(Gtk::WIN_POS_CENTER);
457 
458   m_Frame.set_shadow_type(Gtk::SHADOW_OUT);
459   add(m_Frame);
460 
461   m_Label.set_padding(10, 10);
462   m_Frame.add(m_Label);
463 
464   show_all_children();
465 }
466 
~Popup()467 Popup::~Popup()
468 {
469 }
470 
471 
472 // called by DemoWindow
do_change_display()473 Gtk::Window* do_change_display()
474 {
475   return new Example_ChangeDisplay();
476 }
477