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