//
// Copyright (C) 2011 Adrien Plazas
//
// 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 3 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
//
// Authors:
// Adrien Plazas
// Artists:
// Daniel Foré
//
/* TODO
* For 0.1
* Keep focus on terminal if terminal focused (tag FIXME)
*
* For 0.2
* Notify with system bubbles if the window is not focused (tag FIXME)
* Set text colors ?
* Set preferences via GSettings ? (legacy theme)
* Use stepped window resize ? (usefull if using another terminal background color than the one from the window)
* Start the port to the terminal background service
* If the last page is moved to another instance, close the window ?
*/
using Gtk;
using Gdk;
using Vte;
using Pango;
//~ using Notify;
using Resources;
namespace PantheonTerminal
{
private class PantheonTerminal : Gtk.Window
{
public signal void theme_changed();
Notebook notebook;
FontDescription font;
Gdk.Color bgcolor;
Gdk.Color fgcolor;
bool window_focus = false;
string[] args;
//~ Notify.Notification notification;
// Control and Shift keys
bool ctrlL = false;
bool ctrlR = false;
bool shiftL = false;
bool shiftR = false;
bool arrow = false;
bool tab = false;
private PantheonTerminal(string[] args)
{
this.args = args;
foreach (string arg in args)
stdout.printf("%s\n", arg);
//~ Gtk.Settings.get_default().gtk_application_prefer_dark_theme = true;
title = "Terminal";
default_width = 640;
default_height = 400;
icon_name = "terminal";
destroy.connect(close);
// Check if the window have the focus
focus_in_event.connect(() => { window_focus = true; return false; });
focus_out_event.connect(() => { window_focus = false; return false; });
notebook = new Notebook();
var right_box = new HBox(false, 0);
right_box.show();
notebook.set_action_widget(right_box, PackType.END);
notebook.set_scrollable(true);
add(notebook);
// Set "New tab" button
var add_button = new Button();
add_button.can_focus = false;
//~ add_button.set_image(new Image.from_stock(Stock.ADD, IconSize.MENU));
add_button.set_image(new Image.from_icon_name("list-add", IconSize.MENU));
add_button.show();
add_button.set_relief(ReliefStyle.NONE);
add_button.set_tooltip_text("Open a new tab");
add_button.clicked.connect(() => { new_tab(false); } );
right_box.pack_start(add_button, false, false, 0);
// Set the theme
set_theme();
show_all();
new_tab(true);
key_press_event.connect(on_key_press_event);
key_release_event.connect(on_key_release_event);
}
public void remove_page(int page)
{
notebook.remove_page(page);
if (notebook.get_n_pages() == 0)
new_tab(false);
}
public bool on_key_press_event(EventKey event)
{
string key = Gdk.keyval_name(event.keyval);
if (key == "Control_L")
ctrlL = true;
else if (key == "Control_R")
ctrlR = true;
else if (key == "Shift_L")
shiftL = true;
else if (key == "Shift_R")
shiftR = true;
//~ else if (key == "Tab")
//~ {
//~ notebook.get_nth_page(notebook.get_current_page()).grab_focus();
//~ stdout.printf("tab %i\n", notebook.get_current_page());
//~ }
else if ((ctrlL || ctrlR) && (shiftL || shiftR))
{
if (key == "t" || key == "T")
new_tab(false);
else if (key == "w" || key == "W")
remove_page(notebook.get_current_page());
else if (key == "Tab" || key == "ISO_Left_Tab")
{
if (notebook.get_current_page() < notebook.get_n_pages() - 1)
notebook.next_page();
else
notebook.set_current_page(0);
}
else if (key == "q" || key == "Q")
close();
else
return false;
}
else if (key == "Up" || key == "Down" || key == "Left" || key == "Right")
arrow = true;
else if (key == "Tab")
tab = true;
// stdout.printf("%s\n", key);
return false;
}
public bool on_key_release_event(EventKey event)
{
string key = Gdk.keyval_name(event.keyval);
if (key == "Control_L")
ctrlL = false;
else if (key == "Control_R")
ctrlR = false;
else if (key == "Shift_L")
shiftL = false;
else if (key == "Shift_R")
shiftR = false;
else if (key == "Up" || key == "Down" || key == "Left" || key == "Right")
arrow = false;
else if (key == "Tab")
tab = false;
return false;
}
private void new_tab(bool first)
{
// Set up terminal
var t = new TerminalWithNotification();
// Set up style
set_terminal_theme(t);
if (first)
t.fork_command_full(0, "~/", args, null, 0, null, 0);
//~ t.fork_command_full(PtyFlags.DEFAULT, null, null, null, SpawnFlags.FILE_AND_ARGV_ZERO, null, 0);
//~ t.fork_command(args[0], args, null, null, true, true, true);
else
t.fork_command_full(0, "~/", {}, null, 0, null, 0);
//~ t.fork_command_full(PtyFlags.DEFAULT, null, null, null, SpawnFlags.LEAVE_DESCRIPTORS_OPEN, null, t.get_pty());
//~ t.fork_command(null, null, null, null, true, true, true);
//~ t.forkpty(null, null, true, true, true);
// Set up style
set_terminal_theme(t);
t.show();
// Set up style
set_terminal_theme(t);
// Create a new tab with the terminal
var tab = new TabWithCloseButton("Terminal");
notebook.insert_page(t, tab, notebook.get_current_page() + 1);
notebook.next_page();
notebook.set_tab_reorderable(t, true);
notebook.set_tab_detachable(t, true);
// Set connections
tab.clicked.connect(() => { remove_page(notebook.page_num(t)); });
tab.scroll_event.connect (on_scroll_event);
t.window_title_changed.connect(() => { tab.set_text(t.get_window_title()); });
notebook.switch_page.connect((page, page_num) => { if (notebook.page_num(t) == (int) page_num) tab.set_notification(false); });
focus_in_event.connect(() => { if (notebook.page_num(t) == notebook.get_current_page()) tab.set_notification(false); return false; });
t.preferences.connect(preferences);
t.about.connect(about);
t.child_exited.connect(() => { t.fork_command(null, null, null, null, true, true, true); });
theme_changed.connect(() => { set_terminal_theme(t); });
//~ t.contents_changed.connect(() => { stdout.printf("pty %i\n", t.get_pty()); });
// Make the terminal keep the focus when arrows are pressed FIXME
t.focus_out_event.connect((event) => {
if (notebook.page_num(t) == notebook.get_current_page() && (arrow || this.tab))
{
t.grab_focus();
}
return false; });
// If a task is over
t.task_over.connect(() => {
if (notebook.page_num(t) != notebook.get_current_page() || !window_focus)
tab.set_notification(true);
if (!window_focus)
{
try
{ GLib.Process.spawn_command_line_async("notify-send --icon=\"terminal\" \"" + t.get_window_title() + "\" \"Task finished.\""); }
catch
{ }
}
//~ notification = (Notify.Notification)GLib.Object.new (
//~ typeof (Notify.Notification),
//~ "summary", "sum",
//~ "body", "message",
//~ "icon-name", "");
// Notify OSD
//~ notification = new Notification("test", "test", "test");
//~ try { notification.show(); }
//~ catch {}
});
// Set up style
set_terminal_theme(t);
}
public void set_terminal_theme(TerminalWithNotification t)
{
t.set_font(font);
t.set_color_background(bgcolor);
t.set_color_foreground(fgcolor);
}
public void set_theme()
{
string theme = "dark";
if (theme == "normal")
{
Gtk.Settings.get_default().gtk_application_prefer_dark_theme = false;
// Get the system's style
realize();
font = FontDescription.from_string(system_font());
bgcolor = get_style().bg[StateType.NORMAL];
fgcolor = get_style().fg[StateType.NORMAL];
}
else
{
Gtk.Settings.get_default().gtk_application_prefer_dark_theme = true;
// Get the system's style
realize();
font = FontDescription.from_string(system_font());
bgcolor = get_style().bg[StateType.NORMAL];
fgcolor = get_style().fg[StateType.NORMAL];
}
theme_changed();
}
static string system_font()
{
string font_name = null;
/* Wait for GNOME 3 FIXME
* var settings = new GLib.Settings("org.gnome.desktop.interface");
* font_name = settings.get_string("monospace-font-name");
*/
font_name = "Droid Sans Mono 10";
return font_name;
}
public void about()
{
Gdk.Pixbuf logo = null;
try {
logo = IconTheme.get_default ().load_icon ("terminal", 64, 0);
} catch (Error err) {
stderr.printf ("Unable to load terminal icon: %s", err.message);
}
show_about_dialog(this,
"program-name", Resources.APP_TITLE,
"version", Resources.VERSION,
"comments", Resources.COMMENTS,
"copyright", Resources.COPYRIGHT,
"license", Resources.LICENSE,
"website", Resources.WEBSITE_URL,
"website-label", Resources.WEBSITE_LABEL,
"authors", Resources.AUTHORS,
"artists", Resources.ARTISTS,
"logo", logo,
//~ "translator-credits", _("translator-credits"), // FIXME
null);
}
public void preferences()
{
stdout.printf("Preferences not yet available.\n");
}
private void close()
{
Gtk.main_quit();
}
private static void main(string[] args)
{
Gtk.init(ref args);
new PantheonTerminal(args[1:args.length]);
Gtk.main();
}
}
public class TabWithCloseButton : HBox
{
public signal void clicked();
private Button button;
private Label label;
private string text;
bool notification = false;
public TabWithCloseButton(string text)
{
this.text = text;
// Button
button = new Button();
button.set_image(new Image.from_stock(Stock.CLOSE, IconSize.MENU));
button.show();
button.set_relief(ReliefStyle.NONE);
button.clicked.connect(() => { clicked(); });
// Label
label = new Label(text);
label.show();
// Pack the elements
pack_start(button, false, true, 0);
pack_end(label, true, true, 0);
show();
}
public void set_notification(bool notification)
{
this.notification = notification;
if (notification)
{ label.set_markup(""+text+""); }
else
{ label.set_markup(text); }
}
public void set_text(string text)
{
this.text = text;
set_notification(notification);
}
public bool on_scroll_event (EventScroll event) {
if (event.direction == ScrollDirection.UP || event.direction == ScrollDirection.LEFT) {
if (current_notebook.get_current_page() != 0) {
current_notebook.set_current_page ( current_notebook.get_current_page()-1 );
}
}
if (event.direction == ScrollDirection.DOWN || event.direction == ScrollDirection.RIGHT) {
if (current_notebook.get_current_page() != current_notebook.get_n_pages () ) {
current_notebook.set_current_page ( current_notebook.get_current_page()+1 );
}
}
return true;
}
}
}