bloatpad.c 15.3 KB
Newer Older
1
#include <stdlib.h>
Matthias Clasen's avatar
Matthias Clasen committed
2 3
#include <gtk/gtk.h>

4 5 6 7 8 9 10 11 12 13 14 15
static void
activate_toggle (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       user_data)
{
  GVariant *state;

  state = g_action_get_state (G_ACTION (action));
  g_action_change_state (G_ACTION (action), g_variant_new_boolean (!g_variant_get_boolean (state)));
  g_variant_unref (state);
}

16 17 18 19 20 21 22 23
static void
activate_radio (GSimpleAction *action,
                GVariant      *parameter,
                gpointer       user_data)
{
  g_action_change_state (G_ACTION (action), parameter);
}

24 25 26 27 28 29 30 31 32 33 34 35 36
static void
change_fullscreen_state (GSimpleAction *action,
                         GVariant      *state,
                         gpointer       user_data)
{
  if (g_variant_get_boolean (state))
    gtk_window_fullscreen (user_data);
  else
    gtk_window_unfullscreen (user_data);

  g_simple_action_set_state (action, state);
}

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
static void
change_busy_state (GSimpleAction *action,
                   GVariant      *state,
                   gpointer       user_data)
{
  GtkWindow *window = user_data;
  GApplication *application = G_APPLICATION (gtk_window_get_application (window));

  /* do this twice to test multiple busy counter increases */
  if (g_variant_get_boolean (state))
    {
      g_application_mark_busy (application);
      g_application_mark_busy (application);
    }
  else
    {
      g_application_unmark_busy (application);
      g_application_unmark_busy (application);
    }

  g_simple_action_set_state (action, state);
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
static void
change_justify_state (GSimpleAction *action,
                      GVariant      *state,
                      gpointer       user_data)
{
  GtkTextView *text = g_object_get_data (user_data, "bloatpad-text");
  const gchar *str;

  str = g_variant_get_string (state, NULL);

  if (g_str_equal (str, "left"))
    gtk_text_view_set_justification (text, GTK_JUSTIFY_LEFT);
  else if (g_str_equal (str, "center"))
    gtk_text_view_set_justification (text, GTK_JUSTIFY_CENTER);
  else if (g_str_equal (str, "right"))
    gtk_text_view_set_justification (text, GTK_JUSTIFY_RIGHT);
  else
    /* ignore this attempted change */
    return;

  g_simple_action_set_state (action, state);
}

83 84
static GtkClipboard *
get_clipboard (GtkWidget *widget)
85
{
86
  return gtk_widget_get_clipboard (widget, gdk_atom_intern_static_string ("CLIPBOARD"));
87 88
}

89
static void
90
window_copy (GSimpleAction *action,
Matthias Clasen's avatar
Matthias Clasen committed
91 92
             GVariant      *parameter,
             gpointer       user_data)
93
{
94 95
  GtkWindow *window = GTK_WINDOW (user_data);
  GtkTextView *text = g_object_get_data ((GObject*)window, "bloatpad-text");
96

97
  gtk_text_buffer_copy_clipboard (gtk_text_view_get_buffer (text),
Matthias Clasen's avatar
Matthias Clasen committed
98
                                  get_clipboard ((GtkWidget*) text));
99 100 101
}

static void
102
window_paste (GSimpleAction *action,
Matthias Clasen's avatar
Matthias Clasen committed
103 104
              GVariant      *parameter,
              gpointer       user_data)
105
{
106 107 108 109
  GtkWindow *window = GTK_WINDOW (user_data);
  GtkTextView *text = g_object_get_data ((GObject*)window, "bloatpad-text");
  
  gtk_text_buffer_paste_clipboard (gtk_text_view_get_buffer (text),
Matthias Clasen's avatar
Matthias Clasen committed
110 111 112
                                   get_clipboard ((GtkWidget*) text),
                                   NULL,
                                   TRUE);
113 114 115

}

116
static GActionEntry win_entries[] = {
117 118
  { "copy", window_copy, NULL, NULL, NULL },
  { "paste", window_paste, NULL, NULL, NULL },
119
  { "fullscreen", activate_toggle, NULL, "false", change_fullscreen_state },
120
  { "busy", activate_toggle, NULL, "false", change_busy_state },
121
  { "justify", activate_radio, "s", "'left'", change_justify_state }
122 123
};

124
static void
125 126
new_window (GApplication *app,
            GFile        *file)
127
{
128
  GtkWidget *window, *grid, *scrolled, *view;
129 130
  GtkWidget *toolbar;
  GtkToolItem *button;
Matthias Clasen's avatar
Matthias Clasen committed
131
  GtkWidget *sw, *box, *label;
132

133
  window = gtk_application_window_new (GTK_APPLICATION (app));
134
  gtk_window_set_default_size ((GtkWindow*)window, 640, 480);
135
  g_action_map_add_action_entries (G_ACTION_MAP (window), win_entries, G_N_ELEMENTS (win_entries), window);
136
  gtk_window_set_title (GTK_WINDOW (window), "Bloatpad");
137 138 139 140

  grid = gtk_grid_new ();
  gtk_container_add (GTK_CONTAINER (window), grid);

141 142 143 144 145 146 147 148 149 150 151 152 153
  toolbar = gtk_toolbar_new ();
  button = gtk_toggle_tool_button_new_from_stock (GTK_STOCK_JUSTIFY_LEFT);
  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "win.justify::left");
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (button));

  button = gtk_toggle_tool_button_new_from_stock (GTK_STOCK_JUSTIFY_CENTER);
  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "win.justify::center");
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (button));

  button = gtk_toggle_tool_button_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT);
  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (button), "win.justify::right");
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (button));

Matthias Clasen's avatar
Matthias Clasen committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
  button = gtk_separator_tool_item_new ();
  gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (button), FALSE);
  gtk_tool_item_set_expand (GTK_TOOL_ITEM (button), TRUE);
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (button));

  button = gtk_tool_item_new ();
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  gtk_container_add (GTK_CONTAINER (button), box);
  label = gtk_label_new ("Fullscreen:");
  gtk_container_add (GTK_CONTAINER (box), label);
  sw = gtk_switch_new ();
  gtk_actionable_set_action_name (GTK_ACTIONABLE (sw), "win.fullscreen");
  gtk_container_add (GTK_CONTAINER (box), sw);
  gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (button));

169 170
  gtk_grid_attach (GTK_GRID (grid), toolbar, 0, 0, 1, 1);

171
  scrolled = gtk_scrolled_window_new (NULL, NULL);
172 173
  gtk_widget_set_hexpand (scrolled, TRUE);
  gtk_widget_set_vexpand (scrolled, TRUE);
174
  view = gtk_text_view_new ();
175

176 177
  g_object_set_data ((GObject*)window, "bloatpad-text", view);

178
  gtk_container_add (GTK_CONTAINER (scrolled), view);
179

180
  gtk_grid_attach (GTK_GRID (grid), scrolled, 0, 1, 1, 1);
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

  if (file != NULL)
    {
      gchar *contents;
      gsize length;

      if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
        {
          GtkTextBuffer *buffer;

          buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
          gtk_text_buffer_set_text (buffer, contents, length);
          g_free (contents);
        }
    }

  gtk_widget_show_all (GTK_WIDGET (window));
}

static void
201
bloat_pad_activate (GApplication *application)
202 203 204
{
  new_window (application, NULL);
}
Matthias Clasen's avatar
Matthias Clasen committed
205 206

static void
207 208 209 210
bloat_pad_open (GApplication  *application,
                GFile        **files,
                gint           n_files,
                const gchar   *hint)
Matthias Clasen's avatar
Matthias Clasen committed
211
{
212 213 214 215
  gint i;

  for (i = 0; i < n_files; i++)
    new_window (application, files[i]);
Matthias Clasen's avatar
Matthias Clasen committed
216 217
}

218 219 220 221 222 223 224 225
typedef struct
{
  GtkApplication parent_instance;

  GMenu *time;
  guint timeout;
} BloatPad;

226 227 228 229 230 231 232 233 234 235
typedef GtkApplicationClass BloatPadClass;

G_DEFINE_TYPE (BloatPad, bloat_pad, GTK_TYPE_APPLICATION)

static void
bloat_pad_finalize (GObject *object)
{
  G_OBJECT_CLASS (bloat_pad_parent_class)->finalize (object);
}

236 237 238 239 240
static void
new_activated (GSimpleAction *action,
               GVariant      *parameter,
               gpointer       user_data)
{
241 242 243
  GApplication *app = user_data;

  g_application_activate (app);
244 245
}

246
static void
247 248 249
about_activated (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       user_data)
250
{
251
  gtk_show_about_dialog (NULL,
252 253 254 255
                         "program-name", "Bloatpad",
                         "title", "About Bloatpad",
                         "comments", "Not much to say, really.",
                         NULL);
256 257
}

258 259 260 261 262
static void
quit_activated (GSimpleAction *action,
                GVariant      *parameter,
                gpointer       user_data)
{
263
  GApplication *app = user_data;
264

265
  g_application_quit (app);
266 267
}

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
static gboolean
update_time (gpointer user_data)
{
  BloatPad *bloatpad = user_data;
  GDateTime *now;
  gchar *time;

  while (g_menu_model_get_n_items (G_MENU_MODEL (bloatpad->time)))
    g_menu_remove (bloatpad->time, 0);

  g_message ("Updating the time menu (which should be open now)...");

  now = g_date_time_new_now_local ();
  time = g_date_time_format (now, "%c");
  g_menu_append (bloatpad->time, time, NULL);
  g_date_time_unref (now);
  g_free (time);

  return G_SOURCE_CONTINUE;
}

static void
time_active_changed (GSimpleAction *action,
                     GVariant      *state,
                     gpointer       user_data)
{
  BloatPad *bloatpad = user_data;

  if (g_variant_get_boolean (state))
    {
      if (!bloatpad->timeout)
        {
          bloatpad->timeout = g_timeout_add (1000, update_time, bloatpad);
          update_time (bloatpad);
        }
    }
  else
    {
      if (bloatpad->timeout)
        {
          g_source_remove (bloatpad->timeout);
          bloatpad->timeout = 0;
        }
    }

  g_simple_action_set_state (action, state);
}

316
static GActionEntry app_entries[] = {
317
  { "new", new_activated, NULL, NULL, NULL },
318 319
  { "about", about_activated, NULL, NULL, NULL },
  { "quit", quit_activated, NULL, NULL, NULL },
320
  { "time-active", NULL, NULL, "false", time_active_changed }
321 322
};

323
static void
324
bloat_pad_startup (GApplication *application)
325
{
326
  BloatPad *bloatpad = (BloatPad*) application;
327
  GtkBuilder *builder;
328

329 330 331 332
  G_APPLICATION_CLASS (bloat_pad_parent_class)
    ->startup (application);

  g_action_map_add_action_entries (G_ACTION_MAP (application), app_entries, G_N_ELEMENTS (app_entries), application);
333

334 335 336 337
  builder = gtk_builder_new ();
  gtk_builder_add_from_string (builder,
                               "<interface>"
                               "  <menu id='app-menu'>"
338
                               "    <section>"
339
                               "      <item>"
340
                               "        <attribute name='label' translatable='yes'>_New Window</attribute>"
341 342 343
                               "        <attribute name='action'>app.new</attribute>"
                               "        <attribute name='accel'>&lt;Primary&gt;n</attribute>"
                               "      </item>"
344 345
                               "    </section>"
                               "    <section>"
346
                               "      <item>"
347
                               "        <attribute name='label' translatable='yes'>_About Bloatpad</attribute>"
348 349
                               "        <attribute name='action'>app.about</attribute>"
                               "      </item>"
350 351
                               "    </section>"
                               "    <section>"
352
                               "      <item>"
353
                               "        <attribute name='label' translatable='yes'>_Quit</attribute>"
354 355 356
                               "        <attribute name='action'>app.quit</attribute>"
                               "        <attribute name='accel'>&lt;Primary&gt;q</attribute>"
                               "      </item>"
357
                               "    </section>"
358 359
                               "  </menu>"
                               "  <menu id='menubar'>"
360
                               "    <submenu>"
361
                               "      <attribute name='label' translatable='yes'>_Edit</attribute>"
362
                               "      <section>"
363
                               "        <item>"
364
                               "          <attribute name='label' translatable='yes'>_Copy</attribute>"
365 366 367 368
                               "          <attribute name='action'>win.copy</attribute>"
                               "          <attribute name='accel'>&lt;Primary&gt;c</attribute>"
                               "        </item>"
                               "        <item>"
369 370
                               "          <attribute name='label' translatable='yes'>_Paste</attribute>"
                               "          <attribute name='action'>win.paste</attribute>"
371 372
                               "          <attribute name='accel'>&lt;Primary&gt;v</attribute>"
                               "        </item>"
373
                               "      </section>"
374
                               "    </submenu>"
375
                               "    <submenu>"
376
                               "      <attribute name='label' translatable='yes'>_View</attribute>"
377
                               "      <section>"
378
                               "        <item>"
379
                               "          <attribute name='label' translatable='yes'>_Fullscreen</attribute>"
380 381 382
                               "          <attribute name='action'>win.fullscreen</attribute>"
                               "          <attribute name='accel'>F11</attribute>"
                               "        </item>"
383 384 385 386
                               "        <item>"
                               "          <attribute name='label' translatable='yes'>_Look Busy</attribute>"
                               "          <attribute name='action'>win.busy</attribute>"
                               "        </item>"
387
                               "      </section>"
388
                               "    </submenu>"
389 390 391 392
                               "    <submenu id='time-menu'>"
                               "      <attribute name='label' translatable='yes'>Time</attribute>"
                               "      <attribute name='submenu-action'>app.time-active</attribute>"
                               "    </submenu>"
393 394
                               "  </menu>"
                               "</interface>", -1, NULL);
395 396
  gtk_application_set_app_menu (GTK_APPLICATION (application), G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
  gtk_application_set_menubar (GTK_APPLICATION (application), G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")));
397
  //gtk_application_set_menubar (GTK_APPLICATION (application), G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
398
  bloatpad->time = G_MENU (gtk_builder_get_object (builder, "time-menu"));
399
  g_object_unref (builder);
400 401
}

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
static void
bloat_pad_shutdown (GApplication *application)
{
  BloatPad *bloatpad = (BloatPad *) application;

  if (bloatpad->timeout)
    {
      g_source_remove (bloatpad->timeout);
      bloatpad->timeout = 0;
    }

  G_APPLICATION_CLASS (bloat_pad_parent_class)
    ->shutdown (application);
}

417 418 419 420 421
static void
bloat_pad_init (BloatPad *app)
{
}

422 423 424
static void
bloat_pad_class_init (BloatPadClass *class)
{
425 426 427 428
  GApplicationClass *application_class = G_APPLICATION_CLASS (class);
  GObjectClass *object_class = G_OBJECT_CLASS (class);

  application_class->startup = bloat_pad_startup;
429
  application_class->shutdown = bloat_pad_shutdown;
430 431 432 433
  application_class->activate = bloat_pad_activate;
  application_class->open = bloat_pad_open;

  object_class->finalize = bloat_pad_finalize;
434 435 436 437 438 439

}

BloatPad *
bloat_pad_new (void)
{
440
  BloatPad *bloat_pad;
441

442 443
  g_set_application_name ("Bloatpad");

444 445 446 447 448 449 450 451
  bloat_pad = g_object_new (bloat_pad_get_type (),
                            "application-id", "org.gtk.Test.bloatpad",
                            "flags", G_APPLICATION_HANDLES_OPEN,
                            "inactivity-timeout", 30000,
                            "register-session", TRUE,
                            NULL);

  return bloat_pad;
452 453
}

Matthias Clasen's avatar
Matthias Clasen committed
454 455 456
int
main (int argc, char **argv)
{
457
  BloatPad *bloat_pad;
458
  int status;
Matthias Clasen's avatar
Matthias Clasen committed
459

460
  bloat_pad = bloat_pad_new ();
461

462 463
  gtk_application_add_accelerator (GTK_APPLICATION (bloat_pad),
                                   "F11", "win.fullscreen", NULL);
464

465
  status = g_application_run (G_APPLICATION (bloat_pad), argc, argv);
466

467
  g_object_unref (bloat_pad);
Matthias Clasen's avatar
Matthias Clasen committed
468

469
  return status;
Matthias Clasen's avatar
Matthias Clasen committed
470
}