metacity-window-demo.c 32.4 KB
Newer Older
1 2
/* Metacity window types/properties demo app */

3
/*
4
 * Copyright (C) 2002 Havoc Pennington
5
 *
6 7 8 9 10 11 12 13 14
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 19 20 21 22
 */

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
23
#include <unistd.h>
24

25 26 27 28
static void
do_appwindow (GSimpleAction *action,
              GVariant      *parameter,
              gpointer       user_data);
29

30
static gboolean aspect_on;
31

32 33 34 35 36 37 38
static void
set_gdk_window_struts (GdkWindow *window,
                       int        left,
                       int        right,
                       int        top,
                       int        bottom)
{
39
  long vals[12];
40

41 42 43 44
  vals[0] = left;
  vals[1] = right;
  vals[2] = top;
  vals[3] = bottom;
45 46 47 48 49 50 51 52 53
  vals[4] = 000;
  vals[5] = 400;
  vals[6] = 200;
  vals[7] = 600;
  vals[8] = 76;
  vals[9] = 676;
  vals[10] = 200;
  vals[11] = 800;

54
  XChangeProperty (GDK_WINDOW_XDISPLAY (window),
55
                   GDK_WINDOW_XID (window),
56
                   XInternAtom (GDK_WINDOW_XDISPLAY (window),
57
                                "_NET_WM_STRUT_PARTIAL", False),
58
                   XA_CARDINAL, 32, PropModeReplace,
59
                   (guchar *)vals, 12);
60 61 62 63 64 65
}

static void
on_realize_set_struts (GtkWindow *window,
                       gpointer   data)
{
66
  GtkWidget *widget;
67 68 69 70 71
  int left;
  int right;
  int top;
  int bottom;

72 73 74
  widget = GTK_WIDGET (window);

  g_return_if_fail (gtk_widget_get_realized (widget));
75 76 77 78 79

  left = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "meta-strut-left"));
  right = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "meta-strut-right"));
  top = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "meta-strut-top"));
  bottom = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "meta-strut-bottom"));
80

81
  set_gdk_window_struts (gtk_widget_get_window (widget),
82 83 84 85 86 87 88 89 90 91
                         left, right, top, bottom);
}

static void
set_gtk_window_struts (GtkWidget  *window,
                       int         left,
                       int         right,
                       int         top,
                       int         bottom)
{
92 93 94 95
  GtkWidget *widget;

  widget = GTK_WIDGET (window);

96 97 98 99 100 101 102 103
  g_object_set_data (G_OBJECT (window), "meta-strut-left",
                     GINT_TO_POINTER (left));
  g_object_set_data (G_OBJECT (window), "meta-strut-right",
                     GINT_TO_POINTER (right));
  g_object_set_data (G_OBJECT (window), "meta-strut-top",
                     GINT_TO_POINTER (top));
  g_object_set_data (G_OBJECT (window), "meta-strut-bottom",
                     GINT_TO_POINTER (bottom));
104

105 106 107
  g_signal_handlers_disconnect_by_func (G_OBJECT (window),
                                        on_realize_set_struts,
                                        NULL);
108

109 110 111 112 113
  g_signal_connect_after (G_OBJECT (window),
                          "realize",
                          G_CALLBACK (on_realize_set_struts),
                          NULL);

114 115
  if (gtk_widget_get_realized (widget))
    set_gdk_window_struts (gtk_widget_get_window (widget),
116 117 118 119 120 121 122 123
                           left, right, top, bottom);
}

static void
set_gdk_window_type (GdkWindow  *window,
                     const char *type)
{
  Atom atoms[2] = { None, None };
124

125 126
  atoms[0] = XInternAtom (GDK_WINDOW_XDISPLAY (window),
                          type, False);
127

128
  XChangeProperty (GDK_WINDOW_XDISPLAY (window),
129
                   GDK_WINDOW_XID (window),
130 131
                   XInternAtom (GDK_WINDOW_XDISPLAY (window), "_NET_WM_WINDOW_TYPE", False),
                   XA_ATOM, 32, PropModeReplace,
132
                   (guchar *)atoms,
133 134 135 136 137 138 139
                   1);
}

static void
on_realize_set_type (GtkWindow *window,
                     gpointer   data)
{
140
  GtkWidget *widget;
141 142
  const char *type;

143 144 145
  widget = GTK_WIDGET (window);

  g_return_if_fail (gtk_widget_get_realized (widget));
146 147 148 149

  type = g_object_get_data (G_OBJECT (window), "meta-window-type");

  g_return_if_fail (type != NULL);
150

151
  set_gdk_window_type (gtk_widget_get_window (widget),
152 153 154 155 156 157 158
                       type);
}

static void
set_gtk_window_type (GtkWindow  *window,
                     const char *type)
{
159 160 161 162
  GtkWidget *widget;

  widget = GTK_WIDGET (window);

163 164 165 166 167
  g_object_set_data (G_OBJECT (window), "meta-window-type", (char*) type);

  g_signal_handlers_disconnect_by_func (G_OBJECT (window),
                                        on_realize_set_type,
                                        NULL);
168

169 170 171 172 173
  g_signal_connect_after (G_OBJECT (window),
                          "realize",
                          G_CALLBACK (on_realize_set_type),
                          NULL);

174 175
  if (gtk_widget_get_realized (widget))
    set_gdk_window_type (gtk_widget_get_window (widget),
176 177 178
                         type);
}

179 180 181 182 183 184 185 186 187 188
static void
set_gdk_window_border_only (GdkWindow *window)
{
  gdk_window_set_decorations (window, GDK_DECOR_BORDER);
}

static void
on_realize_set_border_only (GtkWindow *window,
                            gpointer   data)
{
189 190 191 192 193
  GtkWidget *widget;

  widget = GTK_WIDGET (window);

  g_return_if_fail (gtk_widget_get_realized (widget));
194

195
  set_gdk_window_border_only (gtk_widget_get_window (widget));
196 197 198 199 200
}

static void
set_gtk_window_border_only (GtkWindow  *window)
{
201 202 203 204
  GtkWidget *widget;

  widget = GTK_WIDGET (window);

205 206 207
  g_signal_handlers_disconnect_by_func (G_OBJECT (window),
                                        on_realize_set_border_only,
                                        NULL);
208

209 210 211 212 213
  g_signal_connect_after (G_OBJECT (window),
                          "realize",
                          G_CALLBACK (on_realize_set_border_only),
                          NULL);

214 215
  if (gtk_widget_get_realized (widget))
    set_gdk_window_border_only (gtk_widget_get_window (widget));
216 217
}

218 219 220
int
main (int argc, char **argv)
{
Havoc Pennington's avatar
Havoc Pennington committed
221 222 223
  GList *list;
  GdkPixbuf *pixbuf;
  GError *err;
224

225 226
  gtk_init (&argc, &argv);

Havoc Pennington's avatar
Havoc Pennington committed
227 228 229 230 231 232
  err = NULL;
  pixbuf = gdk_pixbuf_new_from_file (METACITY_ICON_DIR"/metacity-window-demo.png",
                                     &err);
  if (pixbuf)
    {
      list = g_list_prepend (NULL, pixbuf);
233

Havoc Pennington's avatar
Havoc Pennington committed
234 235 236 237 238 239 240 241 242
      gtk_window_set_default_icon_list (list);
      g_list_free (list);
      g_object_unref (G_OBJECT (pixbuf));
    }
  else
    {
      g_printerr ("Could not load icon: %s\n", err->message);
      g_error_free (err);
    }
243

244
  do_appwindow (NULL, NULL, NULL);
245 246

  gtk_main ();
247

248 249 250 251
  return 0;
}

static void
252 253 254 255 256 257 258
response_cb (GtkDialog *dialog,
             int        response_id,
             void      *data);

static void
make_dialog (GtkWidget *parent,
             int        depth)
259 260
{
  GtkWidget *dialog;
Havoc Pennington's avatar
Havoc Pennington committed
261
  char *str;
262

263
  dialog = gtk_message_dialog_new (parent ? GTK_WINDOW (parent) : NULL,
264 265 266
                                   GTK_DIALOG_DESTROY_WITH_PARENT,
                                   GTK_MESSAGE_INFO,
                                   GTK_BUTTONS_CLOSE,
267 268
                                   parent ? "Here is a dialog %d" :
                                   "Here is a dialog %d with no transient parent",
269
                                   depth);
270

Havoc Pennington's avatar
Havoc Pennington committed
271 272 273
  str = g_strdup_printf ("%d dialog", depth);
  gtk_window_set_title (GTK_WINDOW (dialog), str);
  g_free (str);
274

275 276 277
  gtk_dialog_add_button (GTK_DIALOG (dialog),
                         "Open child dialog",
                         GTK_RESPONSE_ACCEPT);
278

279 280 281
  /* Close dialog on user response */
  g_signal_connect (G_OBJECT (dialog),
                    "response",
282
                    G_CALLBACK (response_cb),
283
                    NULL);
284 285 286

  g_object_set_data (G_OBJECT (dialog), "depth",
                     GINT_TO_POINTER (depth));
287

288 289 290
  gtk_widget_show (dialog);
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
static void
response_cb (GtkDialog *dialog,
             int        response_id,
             void      *data)
{
  switch (response_id)
    {
    case GTK_RESPONSE_ACCEPT:
      make_dialog (GTK_WIDGET (dialog),
                   GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog),
                                                       "depth")) + 1);
      break;

    default:
      gtk_widget_destroy (GTK_WIDGET (dialog));
      break;
    }
}

static void
311 312 313
dialog_cb (GSimpleAction *action,
           GVariant      *parameter,
           gpointer       callback_data)
314 315 316 317
{
  make_dialog (GTK_WIDGET (callback_data), 1);
}

Havoc Pennington's avatar
Havoc Pennington committed
318
static void
319 320 321
modal_dialog_cb (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       callback_data)
Havoc Pennington's avatar
Havoc Pennington committed
322 323
{
  GtkWidget *dialog;
324

Havoc Pennington's avatar
Havoc Pennington committed
325 326 327 328 329 330 331
  dialog = gtk_message_dialog_new (GTK_WINDOW (callback_data),
                                   GTK_DIALOG_DESTROY_WITH_PARENT,
                                   GTK_MESSAGE_INFO,
                                   GTK_BUTTONS_CLOSE,
                                   "Here is a MODAL dialog");

  set_gtk_window_type (GTK_WINDOW (dialog), "_NET_WM_WINDOW_TYPE_MODAL_DIALOG");
332

Havoc Pennington's avatar
Havoc Pennington committed
333 334 335 336 337
  gtk_dialog_run (GTK_DIALOG (dialog));

  gtk_widget_destroy (dialog);
}

Havoc Pennington's avatar
Havoc Pennington committed
338
static void
339 340 341
no_parent_dialog_cb (GSimpleAction *action,
                     GVariant      *parameter,
                     gpointer       callback_data)
Havoc Pennington's avatar
Havoc Pennington committed
342
{
343
  make_dialog (NULL, 1);
Havoc Pennington's avatar
Havoc Pennington committed
344 345
}

346
static void
347 348 349
utility_cb (GSimpleAction *action,
            GVariant      *parameter,
            gpointer       callback_data)
350 351 352 353
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *button;
354

355 356
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  set_gtk_window_type (GTK_WINDOW (window), "_NET_WM_WINDOW_TYPE_UTILITY");
357
  gtk_window_set_title (GTK_WINDOW (window), "Utility");
358

359
  gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (callback_data));
360

361
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
362 363 364 365 366 367 368 369 370 371 372 373 374 375

  gtk_container_add (GTK_CONTAINER (window), vbox);

  button = gtk_button_new_with_mnemonic ("_A button");
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

  button = gtk_button_new_with_mnemonic ("_B button");
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

  button = gtk_button_new_with_mnemonic ("_C button");
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

  button = gtk_button_new_with_mnemonic ("_D button");
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
376

377 378 379
  gtk_widget_show_all (window);
}

380
static void
381 382 383
toolbar_cb (GSimpleAction *action,
            GVariant      *parameter,
            gpointer       callback_data)
384 385 386 387
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *label;
388

389 390 391
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  set_gtk_window_type (GTK_WINDOW (window), "_NET_WM_WINDOW_TYPE_TOOLBAR");
  gtk_window_set_title (GTK_WINDOW (window), "Toolbar");
392

393
  gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (callback_data));
394

395
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
396 397 398 399 400

  gtk_container_add (GTK_CONTAINER (window), vbox);

  label = gtk_label_new ("FIXME this needs a resize grip, etc.");
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
401

402 403 404 405
  gtk_widget_show_all (window);
}

static void
406 407 408
menu_cb (GSimpleAction *action,
         GVariant      *parameter,
         gpointer       callback_data)
409 410 411 412
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *label;
413

414 415 416
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  set_gtk_window_type (GTK_WINDOW (window), "_NET_WM_WINDOW_TYPE_MENU");
  gtk_window_set_title (GTK_WINDOW (window), "Menu");
417

418
  gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (callback_data));
419

420
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
421 422 423 424 425

  gtk_container_add (GTK_CONTAINER (window), vbox);

  label = gtk_label_new ("FIXME this isn't a menu.");
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
426

427 428 429
  gtk_widget_show_all (window);
}

430
static void
431 432 433
override_redirect_cb (GSimpleAction *action,
                      GVariant      *parameter,
                      gpointer       callback_data)
434 435 436 437
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *label;
438

439 440
  window = gtk_window_new (GTK_WINDOW_POPUP);
  gtk_window_set_title (GTK_WINDOW (window), "Override Redirect");
441

442
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
443 444 445 446 447

  gtk_container_add (GTK_CONTAINER (window), vbox);

  label = gtk_label_new ("This is an override\nredirect window\nand should not be managed");
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
448

449 450 451
  gtk_widget_show_all (window);
}

452
static void
453 454 455
border_only_cb (GSimpleAction *action,
                GVariant      *parameter,
                gpointer       callback_data)
456 457 458 459
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *label;
460

461 462 463
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  set_gtk_window_border_only (GTK_WINDOW (window));
  gtk_window_set_title (GTK_WINDOW (window), "Border only");
464

465
  gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (callback_data));
466

467
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
468 469 470 471 472

  gtk_container_add (GTK_CONTAINER (window), vbox);

  label = gtk_label_new ("This window is supposed to have a border but no titlebar.");
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
473

474 475 476
  gtk_widget_show_all (window);
}

477 478 479 480 481 482 483 484 485 486
static gboolean
focus_in_event_cb (GtkWidget *window,
                   GdkEvent  *event,
                   gpointer   data)
{
  GtkWidget *widget;

  widget = GTK_WIDGET (data);

  gtk_label_set_text (GTK_LABEL (widget), "Has focus");
487 488

  return TRUE;
489 490 491 492 493 494 495 496 497 498 499 500 501
}


static gboolean
focus_out_event_cb (GtkWidget *window,
                    GdkEvent  *event,
                    gpointer   data)
{
  GtkWidget *widget;

  widget = GTK_WIDGET (data);

  gtk_label_set_text (GTK_LABEL (widget), "Not focused");
502

503
  return TRUE;
504 505 506 507 508 509
}

static GtkWidget*
focus_label (GtkWidget *window)
{
  GtkWidget *label;
510

511 512 513 514 515 516 517
  label = gtk_label_new ("Not focused");

  g_signal_connect (G_OBJECT (window), "focus_in_event",
                    G_CALLBACK (focus_in_event_cb), label);

  g_signal_connect (G_OBJECT (window), "focus_out_event",
                    G_CALLBACK (focus_out_event_cb), label);
518

519 520 521 522
  return label;
}

static void
523 524 525
splashscreen_cb (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       callback_data)
526 527 528 529
{
  GtkWidget *window;
  GtkWidget *image;
  GtkWidget *vbox;
530

531
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
532
  set_gtk_window_type (GTK_WINDOW (window), "_NET_WM_WINDOW_TYPE_SPLASH");
533
  gtk_window_set_title (GTK_WINDOW (window), "Splashscreen");
534

535
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
536

537
  image = gtk_image_new_from_icon_name ("dialog-information", GTK_ICON_SIZE_DIALOG);
538 539
  gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);

540 541
  gtk_box_pack_start (GTK_BOX (vbox), focus_label (window), FALSE, FALSE, 0);

542
  gtk_container_add (GTK_CONTAINER (window), vbox);
543

544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
  gtk_widget_show_all (window);
}

enum
{
  DOCK_TOP = 1,
  DOCK_BOTTOM = 2,
  DOCK_LEFT = 3,
  DOCK_RIGHT = 4,
  DOCK_ALL = 5
};

static void
make_dock (int type)
{
  GtkWidget *window;
  GtkWidget *image;
  GtkWidget *box;
  GtkWidget *button;
563 564
  GdkScreen *screen;
  GdkWindow *root;
565 566 567 568 569 570 571

  g_return_if_fail (type != DOCK_ALL);

  box = NULL;
  switch (type)
    {
    case DOCK_LEFT:
572
    case DOCK_RIGHT:
573
      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
574 575 576
      break;
    case DOCK_TOP:
    case DOCK_BOTTOM:
577
      box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
578 579
      break;
    case DOCK_ALL:
580
    default:
581 582 583 584 585
      break;
    }

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  set_gtk_window_type (GTK_WINDOW (window), "_NET_WM_WINDOW_TYPE_DOCK");
586

587
  image = gtk_image_new_from_icon_name ("dialog-information", GTK_ICON_SIZE_DIALOG);
588 589 590
  gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (box), focus_label (window), FALSE, FALSE, 0);
591 592 593 594 595 596

  button = gtk_button_new_with_label ("Close");
  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);

  g_signal_connect_swapped (G_OBJECT (button), "clicked",
                            G_CALLBACK (gtk_widget_destroy), window);
597

598 599
  gtk_container_add (GTK_CONTAINER (window), box);

600 601 602
  screen = gdk_screen_get_default ();
  root = gdk_screen_get_root_window (screen);

603 604 605 606
#define DOCK_SIZE 48
  switch (type)
    {
    case DOCK_LEFT:
607 608
      gtk_widget_set_size_request (window, DOCK_SIZE, 400);
      gtk_window_move (GTK_WINDOW (window), 0, 000);
609
      set_gtk_window_struts (window, DOCK_SIZE, 0, 0, 0);
610
      gtk_window_set_title (GTK_WINDOW (window), "LeftDock");
611
      break;
612
    case DOCK_RIGHT:
613
      gtk_widget_set_size_request (window, DOCK_SIZE, 400);
614
      gtk_window_move (GTK_WINDOW (window), gdk_window_get_width (root) - DOCK_SIZE, 200);
615
      set_gtk_window_struts (window, 0, DOCK_SIZE, 0, 0);
616
      gtk_window_set_title (GTK_WINDOW (window), "RightDock");
617 618
      break;
    case DOCK_TOP:
619 620
      gtk_widget_set_size_request (window, 600, DOCK_SIZE);
      gtk_window_move (GTK_WINDOW (window), 76, 0);
621
      set_gtk_window_struts (window, 0, 0, DOCK_SIZE, 0);
622
      gtk_window_set_title (GTK_WINDOW (window), "TopDock");
623 624
      break;
    case DOCK_BOTTOM:
625
      gtk_widget_set_size_request (window, 600, DOCK_SIZE);
626
      gtk_window_move (GTK_WINDOW (window), 200, gdk_window_get_height (root) - DOCK_SIZE);
627
      set_gtk_window_struts (window, 0, 0, 0, DOCK_SIZE);
628
      gtk_window_set_title (GTK_WINDOW (window), "BottomDock");
629 630
      break;
    case DOCK_ALL:
631
    default:
632 633
      break;
    }
634

635 636 637 638
  gtk_widget_show_all (window);
}

static void
639 640 641
dock_cb (GSimpleAction *action,
         GVariant      *parameter,
         gpointer       callback_data)
642
{
643 644 645
  guint callback_action;
  const gchar *name;

646
  g_object_get (G_OBJECT (action), "name", &name, NULL);
647

648
  if (!g_strcmp0 (name, "top-dock"))
649
    callback_action = DOCK_TOP;
650
  else if (!g_strcmp0 (name, "bottom-dock"))
651
    callback_action = DOCK_BOTTOM;
652
  else if (!g_strcmp0 (name, "left-dock"))
653
    callback_action = DOCK_LEFT;
654
  else if (!g_strcmp0 (name, "right-dock"))
655
    callback_action = DOCK_RIGHT;
656
  else if (!g_strcmp0 (name, "all-docks"))
657 658 659 660
    callback_action = DOCK_ALL;
  else
    return;

661 662 663 664 665 666 667 668 669 670 671 672 673
  if (callback_action == DOCK_ALL)
    {
      make_dock (DOCK_TOP);
      make_dock (DOCK_BOTTOM);
      make_dock (DOCK_LEFT);
      make_dock (DOCK_RIGHT);
    }
  else
    {
      make_dock (callback_action);
    }
}

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
static void
override_background_color (GtkWidget *widget,
                           GdkRGBA   *rgba)
{
  gchar          *css;
  GtkCssProvider *provider;

  provider = gtk_css_provider_new ();

  css = g_strdup_printf ("* { background-color: %s; }",
                         gdk_rgba_to_string (rgba));
  gtk_css_provider_load_from_data (provider, css, -1, NULL);
  g_free (css);

  gtk_style_context_add_provider (gtk_widget_get_style_context (widget),
                                  GTK_STYLE_PROVIDER (provider),
                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  g_object_unref (provider);
}

694
static void
695 696 697
desktop_cb (GSimpleAction *action,
            GVariant      *parameter,
            gpointer       callback_data)
698
{
699 700
  GdkScreen *screen;
  GdkWindow *root;
701 702
  GtkWidget *window;
  GtkWidget *label;
Alberts Muktupāvels's avatar
Alberts Muktupāvels committed
703
  GdkRGBA    desktop_color;
704

705 706 707
  screen = gdk_screen_get_default ();
  root = gdk_screen_get_root_window (screen);

708 709
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  set_gtk_window_type (GTK_WINDOW (window), "_NET_WM_WINDOW_TYPE_DESKTOP");
710
  gtk_window_set_title (GTK_WINDOW (window), "Desktop");
711
  gtk_widget_set_size_request (window,
712 713
                               gdk_window_get_width (root),
                               gdk_window_get_height (root));
714
  gtk_window_move (GTK_WINDOW (window), 0, 0);
715

Alberts Muktupāvels's avatar
Alberts Muktupāvels committed
716 717 718 719
  desktop_color.red = 0.32;
  desktop_color.green = 0.46;
  desktop_color.blue = 0.65;
  desktop_color.alpha = 1.0;
720

721
  override_background_color (window, &desktop_color);
722

723
  label = focus_label (window);
724

725
  gtk_container_add (GTK_CONTAINER (window), label);
726

727 728 729
  gtk_widget_show_all (window);
}

730
static void
731 732 733
sleep_cb (GSimpleAction *action,
          GVariant      *parameter,
          gpointer       data)
734 735 736 737
{
  sleep (1000);
}

738
static void
739 740 741
toggle_aspect_ratio (GSimpleAction *action,
                     GVariant      *parameter,
                     gpointer       data)
742 743 744
{
  GtkWidget *window;
  GdkGeometry geom;
745
  GtkWidget *widget = GTK_WIDGET (data);
746 747 748 749 750 751 752 753 754 755 756 757 758 759

  if (aspect_on)
    {
      geom.min_aspect = 0;
      geom.max_aspect = 65535;
    }
  else
    {
      geom.min_aspect = 1.777778;
      geom.max_aspect = 1.777778;
    }

  aspect_on = !aspect_on;

760
  window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
761 762 763 764 765
  if (window)
    gtk_window_set_geometry_hints (GTK_WINDOW (window),
				   GTK_WIDGET (data),
				   &geom,
				   GDK_HINT_ASPECT);
766

767 768
}

769
static void
770 771 772
toggle_decorated_cb (GSimpleAction *action,
                     GVariant      *parameter,
                     gpointer       data)
773 774
{
  GtkWidget *window;
775
  window = gtk_widget_get_ancestor (data, GTK_TYPE_WINDOW);
776 777 778 779 780
  if (window)
    gtk_window_set_decorated (GTK_WINDOW (window),
                              !gtk_window_get_decorated (GTK_WINDOW (window)));
}

781
static void
782 783 784
clicked_toolbar_cb (GSimpleAction *action,
                    GVariant      *parameter,
                    gpointer       data)
785 786
{
  GtkWidget *dialog;
787

788 789 790 791 792 793 794 795 796 797 798
  dialog = gtk_message_dialog_new (GTK_WINDOW (data),
                                   GTK_DIALOG_DESTROY_WITH_PARENT,
                                   GTK_MESSAGE_INFO,
                                   GTK_BUTTONS_CLOSE,
                                   "Clicking the toolbar buttons doesn't do anything");

  /* Close dialog on user response */
  g_signal_connect (G_OBJECT (dialog),
                    "response",
                    G_CALLBACK (gtk_widget_destroy),
                    NULL);
799

800 801 802 803 804 805 806 807 808 809 810
  gtk_widget_show (dialog);
}

static void
update_statusbar (GtkTextBuffer *buffer,
                  GtkStatusbar  *statusbar)
{
  gchar *msg;
  gint row, col;
  gint count;
  GtkTextIter iter;
811

812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
  gtk_statusbar_pop (statusbar, 0); /* clear any previous message, underflow is allowed */

  count = gtk_text_buffer_get_char_count (buffer);

  gtk_text_buffer_get_iter_at_mark (buffer,
                                    &iter,
                                    gtk_text_buffer_get_insert (buffer));

  row = gtk_text_iter_get_line (&iter);
  col = gtk_text_iter_get_line_offset (&iter);

  msg = g_strdup_printf ("Cursor at row %d column %d - %d chars in document",
                         row, col, count);

  gtk_statusbar_push (statusbar, 0, msg);

  g_free (msg);
}

static void
mark_set_callback (GtkTextBuffer     *buffer,
                   const GtkTextIter *new_location,
                   GtkTextMark       *mark,
                   gpointer           data)
{
  update_statusbar (buffer, GTK_STATUSBAR (data));
}

840 841 842 843 844 845 846 847 848 849
static int window_count = 0;

static void
destroy_cb (GtkWidget *w, gpointer data)
{
  --window_count;
  if (window_count == 0)
    gtk_main_quit ();
}

850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
static const gchar *xml =
  "<interface>"
    "<menu id='menubar'>"
      "<submenu>"
        "<attribute name='label'>Windows</attribute>"
        "<section>"
          "<item>"
            "<attribute name='label'>Dialog</attribute>"
            "<attribute name='action'>demo.dialog1</attribute>"
            "<attribute name='accel'>&lt;control&gt;d</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Modal dialog</attribute>"
            "<attribute name='action'>demo.dialog2</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Parentless dialog</attribute>"
            "<attribute name='action'>demo.dialog3</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Utility</attribute>"
            "<attribute name='action'>demo.utility</attribute>"
            "<attribute name='accel'>&lt;control&gt;u</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Splashscreen</attribute>"
            "<attribute name='action'>demo.splashscreen</attribute>"
            "<attribute name='accel'>&lt;control&gt;s</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Top dock</attribute>"
            "<attribute name='action'>demo.top-dock</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Bottom dock</attribute>"
            "<attribute name='action'>demo.bottom-dock</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Left dock</attribute>"
            "<attribute name='action'>demo.left-dock</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Right dock</attribute>"
            "<attribute name='action'>demo.right-dock</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>All docks</attribute>"
            "<attribute name='action'>demo.all-docks</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Desktop</attribute>"
            "<attribute name='action'>demo.desktop</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Menu</attribute>"
            "<attribute name='action'>demo.menu</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Toolbar</attribute>"
            "<attribute name='action'>demo.toolbar</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Override Redirect</attribute>"
            "<attribute name='action'>demo.override-redirect</attribute>"
          "</item>"
          "<item>"
            "<attribute name='label'>Border Only</attribute>"
            "<attribute name='action'>demo.border-only</attribute>"
          "</item>"
        "</section>"
      "</submenu>"
    "</menu>"
  "</interface>";

static GActionEntry demo_entries[] =
{
  /* menubar */
  { "dialog1",           dialog_cb,            NULL, NULL, NULL, {} },
  { "dialog2",           modal_dialog_cb,      NULL, NULL, NULL, {} },
  { "dialog3",           no_parent_dialog_cb,  NULL, NULL, NULL, {} },
  { "utility",           utility_cb,           NULL, NULL, NULL, {} },
  { "splashscreen",      splashscreen_cb,      NULL, NULL, NULL, {} },
  { "top-dock",          dock_cb,              NULL, NULL, NULL, {} },
  { "bottom-dock",       dock_cb,              NULL, NULL, NULL, {} },
  { "left-dock",         dock_cb,              NULL, NULL, NULL, {} },
  { "right-dock",        dock_cb,              NULL, NULL, NULL, {} },
  { "all-docks",         dock_cb,              NULL, NULL, NULL, {} },
  { "desktop",           desktop_cb,           NULL, NULL, NULL, {} },
  { "menu",              menu_cb,              NULL, NULL, NULL, {} },
  { "toolbar",           toolbar_cb,           NULL, NULL, NULL, {} },
  { "override-redirect", override_redirect_cb, NULL, NULL, NULL, {} },
  { "border-only",       border_only_cb,       NULL, NULL, NULL, {} },
  /* toolbar */
  { "new",               do_appwindow,         NULL, NULL, NULL, {} },
  { "lock",              sleep_cb,             NULL, NULL, NULL, {} },
  { "decorations",       toggle_decorated_cb,  NULL, NULL, NULL, {} },
  { "quit",              clicked_toolbar_cb,   NULL, NULL, NULL, {} },
  { "ratio",             toggle_aspect_ratio,  NULL, NULL, NULL, {} },
948 949
};

950
static GtkWidget *
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
create_toolbar (void)
{
  GtkWidget *toolbar;
  GtkToolItem *item;

  toolbar = gtk_toolbar_new ();

  item = gtk_tool_button_new (gtk_image_new_from_icon_name ("document-new", GTK_ICON_SIZE_SMALL_TOOLBAR), NULL);
  gtk_tool_item_set_tooltip_markup (item, "Open another one of these windows");
  gtk_actionable_set_action_name (GTK_ACTIONABLE (item), "demo.new");
  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

  item = gtk_tool_button_new (gtk_image_new_from_icon_name ("document-open", GTK_ICON_SIZE_SMALL_TOOLBAR), NULL);
  gtk_tool_item_set_tooltip_markup (item, "This is a demo button that locks up the demo");
  gtk_actionable_set_action_name (GTK_ACTIONABLE (item), "demo.lock");
  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

  item = gtk_tool_button_new (gtk_image_new_from_icon_name ("document-open", GTK_ICON_SIZE_SMALL_TOOLBAR), NULL);
  gtk_tool_item_set_tooltip_markup (item, "This is a demo button that toggles window decorations");
  gtk_actionable_set_action_name (GTK_ACTIONABLE (item), "demo.decorations");
  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

  item = gtk_tool_button_new (gtk_image_new_from_icon_name ("document-open", GTK_ICON_SIZE_SMALL_TOOLBAR), NULL);
  gtk_tool_item_set_tooltip_markup (item, "This is a demo button that locks the aspect ratio using a hint");
  gtk_actionable_set_action_name (GTK_ACTIONABLE (item), "demo.ratio");
  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

  item = gtk_tool_button_new (gtk_image_new_from_icon_name ("gtk-quit", GTK_ICON_SIZE_SMALL_TOOLBAR), NULL);
  gtk_tool_item_set_tooltip_markup (item, "This is a demo button with a 'quit' icon");
  gtk_actionable_set_action_name (GTK_ACTIONABLE (item), "demo.quit");
  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);

  return toolbar;
}

static void
do_appwindow (GSimpleAction *action,
              GVariant      *parameter,
              gpointer       user_data)
990
{
991
  GtkWidget *window;
992
  GtkWidget *grid;
993 994 995 996
  GtkWidget *statusbar;
  GtkWidget *contents;
  GtkWidget *sw;
  GtkTextBuffer *buffer;
997 998
  GSimpleActionGroup *action_group;
  GtkBuilder *builder;
999 1000 1001
  GMenuModel *model;
  GtkWidget *menubar;
  GtkWidget *toolbar;
1002

1003 1004 1005 1006
  /* Create the toplevel window
   */

  ++window_count;
1007 1008 1009

  aspect_on = FALSE;

1010 1011
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Application Window");
1012

1013 1014
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (destroy_cb), NULL);
1015

1016 1017 1018 1019
  grid = gtk_grid_new ();

  gtk_widget_set_vexpand (grid, TRUE);
  gtk_widget_set_hexpand (grid, TRUE);
1020

1021
  gtk_container_add (GTK_CONTAINER (window), grid);
1022

1023 1024
  action_group = g_simple_action_group_new ();
  builder = gtk_builder_new_from_string (xml, -1);
1025

1026 1027 1028 1029 1030
  g_action_map_add_action_entries (G_ACTION_MAP (action_group),
                                   demo_entries,
                                   G_N_ELEMENTS (demo_entries),
                                   window);
  gtk_widget_insert_action_group (window, "demo", G_ACTION_GROUP (action_group));
1031

1032 1033
  /* Create the menubar
   */
1034

1035 1036
  model = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));
  menubar = gtk_menu_bar_new_from_model (model);
1037 1038
  gtk_grid_attach (GTK_GRID (grid), menubar, 0, 0, 1, 1);
  gtk_widget_set_hexpand (menubar, TRUE);
1039

1040 1041
  /* Create the toolbar
   */
1042

1043
  toolbar = create_toolbar ();
1044 1045
  gtk_grid_attach (GTK_GRID (grid), toolbar, 0, 1, 1, 1);
  gtk_widget_set_hexpand (toolbar, TRUE);
1046

1047 1048 1049
  /* Create document
   */

1050 1051
  contents = gtk_text_view_new ();

1052 1053 1054 1055 1056 1057 1058 1059
  sw = gtk_scrolled_window_new (NULL, NULL);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);

  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
                                       GTK_SHADOW_IN);
1060

1061 1062 1063 1064
  gtk_grid_attach (GTK_GRID (grid), sw, 0, 2, 1, 1);

  gtk_widget_set_hexpand (sw, TRUE);
  gtk_widget_set_vexpand (sw, TRUE);
1065 1066 1067

  gtk_window_set_default_size (GTK_WINDOW (window),
                               200, 200);
1068

1069 1070
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (contents),
                               PANGO_WRAP_WORD);
1071

1072 1073 1074
  gtk_container_add (GTK_CONTAINER (sw),
                     contents);

1075
  /* Create statusbar
1076 1077 1078
   */

  statusbar = gtk_statusbar_new ();
1079 1080
  gtk_grid_attach (GTK_GRID (grid), statusbar, 0, 3, 1, 1);
  gtk_widget_set_hexpand (statusbar, TRUE);
1081 1082

  /* Show text widget info in the statusbar */
1083

1084 1085 1086 1087 1088 1089 1090 1091
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));

  gtk_text_buffer_set_text (buffer,
                            "This demo demonstrates various kinds of windows that "
                            "window managers and window manager themes should handle. "
                            "Be sure to tear off the menu and toolbar, those are also "
                            "a special kind of window.",
                            -1);
1092

1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
  g_signal_connect_object (buffer,
                           "changed",
                           G_CALLBACK (update_statusbar),
                           statusbar,
                           0);

  g_signal_connect_object (buffer,
                           "mark_set", /* cursor moved */
                           G_CALLBACK (mark_set_callback),
                           statusbar,
                           0);
1104

1105
  update_statusbar (buffer, GTK_STATUSBAR (statusbar));
1106

1107
  gtk_widget_show_all (window);
1108

1109 1110
  g_object_unref (action_group);
  g_object_unref (builder);
1111
}